This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / media / dvb / ttusb-dec / ttusb_dec.c
index b51a961..cc48a65 100644 (file)
 #include <asm/semaphore.h>
 #include <linux/list.h>
 #include <linux/module.h>
-#include <linux/moduleparam.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/usb.h>
-#include <linux/version.h>
 #include <linux/interrupt.h>
 #include <linux/firmware.h>
+#if defined(CONFIG_CRC32) || defined(CONFIG_CRC32_MODULE)
 #include <linux/crc32.h>
+#else
+#warning "CRC checking of firmware not available"
+#endif
 #include <linux/init.h>
 
 #include "dmxdev.h"
 #include "dvb_demux.h"
+#include "dvb_i2c.h"
 #include "dvb_filter.h"
 #include "dvb_frontend.h"
 #include "dvb_net.h"
-#include "ttusbdecfe.h"
 
-static int debug;
-static int output_pva;
-
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
-module_param(output_pva, int, 0444);
-MODULE_PARM_DESC(output_pva, "Output PVA from dvr device (default:off)");
+static int debug = 0;
+static int output_pva = 0;
 
 #define dprintk        if (debug) printk
 
@@ -66,6 +63,9 @@ MODULE_PARM_DESC(output_pva, "Output PVA from dvr device (default:off)");
 
 #define        MAX_PVA_LENGTH          6144
 
+#define LOF_HI                 10600000
+#define LOF_LO                 9750000
+
 enum ttusb_dec_model {
        TTUSB_DEC2000T,
        TTUSB_DEC2540T,
@@ -95,10 +95,14 @@ struct ttusb_dec {
        struct dmxdev                   dmxdev;
        struct dvb_demux                demux;
        struct dmx_frontend             frontend;
+       struct dvb_i2c_bus              i2c_bus;
        struct dvb_net                  dvb_net;
-       struct dvb_frontend*            fe;
+       struct dvb_frontend_info        *frontend_info;
+       int (*frontend_ioctl) (struct dvb_frontend *, unsigned int, void *);
 
        u16                     pid[DMX_PES_OTHER];
+       int                     hi_band;
+       int                     voltage;
 
        /* USB bits */
        struct usb_device       *udev;
@@ -157,6 +161,32 @@ struct filter_info {
        struct list_head        filter_info_list;
 };
 
+static struct dvb_frontend_info dec2000t_frontend_info = {
+       .name                   = "TechnoTrend/Hauppauge DEC2000-t Frontend",
+       .type                   = FE_OFDM,
+       .frequency_min          = 51000000,
+       .frequency_max          = 858000000,
+       .frequency_stepsize     = 62500,
+       .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+               FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+               FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+               FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+               FE_CAN_HIERARCHY_AUTO,
+};
+
+static struct dvb_frontend_info dec3000s_frontend_info = {
+       .name                   = "TechnoTrend/Hauppauge DEC3000-s Frontend",
+       .type                   = FE_QPSK,
+       .frequency_min          = 950000,
+       .frequency_max          = 2150000,
+       .frequency_stepsize     = 125,
+       .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+               FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+               FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+               FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+               FE_CAN_HIERARCHY_AUTO,
+};
+
 static void ttusb_dec_set_model(struct ttusb_dec *dec,
                                enum ttusb_dec_model model);
 
@@ -1070,7 +1100,7 @@ static int ttusb_dec_alloc_iso_urbs(struct ttusb_dec *dec)
                                               &dec->iso_dma_handle);
 
        memset(dec->iso_buffer, 0,
-              ISO_FRAME_SIZE * (FRAMES_PER_ISO_BUF * ISO_BUF_COUNT));
+              sizeof(ISO_FRAME_SIZE * (FRAMES_PER_ISO_BUF * ISO_BUF_COUNT)));
 
        for (i = 0; i < ISO_BUF_COUNT; i++) {
                struct urb *urb;
@@ -1135,9 +1165,10 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec)
        u16 firmware_csum = 0;
        u16 firmware_csum_ns;
        u32 firmware_size_nl;
+#if defined(CONFIG_CRC32) || defined(CONFIG_CRC32_MODULE)
        u32 crc32_csum, crc32_check, tmp;
+#endif
        const struct firmware *fw_entry = NULL;
-
        dprintk("%s\n", __FUNCTION__);
 
        if (request_firmware(&fw_entry, dec->firmware_name, &dec->udev->dev)) {
@@ -1158,6 +1189,7 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec)
        /* a 32 bit checksum over the first 56 bytes of the DSP Code is stored
           at offset 56 of file, so use it to check if the firmware file is
           valid. */
+#if defined(CONFIG_CRC32) || defined(CONFIG_CRC32_MODULE)
        crc32_csum = crc32(~0L, firmware, 56) ^ ~0L;
        memcpy(&tmp, &firmware[56], 4);
        crc32_check = htonl(tmp);
@@ -1167,6 +1199,7 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec)
                        __FUNCTION__, crc32_csum, crc32_check);
                return -1;
        }
+#endif
        memcpy(idstring, &firmware[36], 20);
        idstring[20] = '\0';
        printk(KERN_INFO "ttusb_dec: found DSP code \"%s\".\n", idstring);
@@ -1255,7 +1288,6 @@ static int ttusb_dec_init_stb(struct ttusb_dec *dec)
                                ttusb_dec_set_model(dec, TTUSB_DEC3000S);
                                break;
                        case 0x00070009:
-                       case 0x00070013:
                                ttusb_dec_set_model(dec, TTUSB_DEC2000T);
                                break;
                        case 0x00070011:
@@ -1366,7 +1398,6 @@ static void ttusb_dec_exit_dvb(struct ttusb_dec *dec)
        dec->demux.dmx.remove_frontend(&dec->demux.dmx, &dec->frontend);
        dvb_dmxdev_release(&dec->dmxdev);
        dvb_dmx_release(&dec->demux);
-       if (dec->fe) dvb_unregister_frontend(dec->fe);
        dvb_unregister_adapter(dec->adapter);
 }
 
@@ -1398,6 +1429,257 @@ static void ttusb_dec_exit_tasklet(struct ttusb_dec *dec)
        }
 }
 
+static int ttusb_dec_2000t_frontend_ioctl(struct dvb_frontend *fe, unsigned int cmd,
+                                 void *arg)
+{
+       struct ttusb_dec *dec = fe->data;
+
+       dprintk("%s\n", __FUNCTION__);
+
+       switch (cmd) {
+
+       case FE_GET_INFO:
+               dprintk("%s: FE_GET_INFO\n", __FUNCTION__);
+               memcpy(arg, dec->frontend_info,
+                      sizeof (struct dvb_frontend_info));
+               break;
+
+       case FE_READ_STATUS: {
+                       fe_status_t *status = (fe_status_t *)arg;
+                       dprintk("%s: FE_READ_STATUS\n", __FUNCTION__);
+                       *status = FE_HAS_SIGNAL | FE_HAS_VITERBI |
+                                 FE_HAS_SYNC | FE_HAS_CARRIER | FE_HAS_LOCK;
+                       break;
+               }
+
+       case FE_READ_BER: {
+                       u32 *ber = (u32 *)arg;
+                       dprintk("%s: FE_READ_BER\n", __FUNCTION__);
+                       *ber = 0;
+                       return -ENOSYS;
+                       break;
+               }
+
+       case FE_READ_SIGNAL_STRENGTH: {
+                       dprintk("%s: FE_READ_SIGNAL_STRENGTH\n", __FUNCTION__);
+                       *(s32 *)arg = 0xFF;
+                       return -ENOSYS;
+                       break;
+               }
+
+       case FE_READ_SNR:
+               dprintk("%s: FE_READ_SNR\n", __FUNCTION__);
+               *(s32 *)arg = 0;
+               return -ENOSYS;
+               break;
+
+       case FE_READ_UNCORRECTED_BLOCKS:
+               dprintk("%s: FE_READ_UNCORRECTED_BLOCKS\n", __FUNCTION__);
+               *(u32 *)arg = 0;
+               return -ENOSYS;
+               break;
+
+       case FE_SET_FRONTEND: {
+                       struct dvb_frontend_parameters *p =
+                               (struct dvb_frontend_parameters *)arg;
+               u8 b[] = { 0x00, 0x00, 0x00, 0x03,
+                          0x00, 0x00, 0x00, 0x00,
+                          0x00, 0x00, 0x00, 0x01,
+                          0x00, 0x00, 0x00, 0xff,
+                          0x00, 0x00, 0x00, 0xff };
+                       u32 freq;
+
+                       dprintk("%s: FE_SET_FRONTEND\n", __FUNCTION__);
+
+                       dprintk("            frequency->%d\n", p->frequency);
+                       dprintk("            symbol_rate->%d\n",
+                               p->u.qam.symbol_rate);
+                       dprintk("            inversion->%d\n", p->inversion);
+
+                       freq = htonl(p->frequency / 1000);
+                       memcpy(&b[4], &freq, sizeof (u32));
+                       ttusb_dec_send_command(dec, 0x71, sizeof(b), b, NULL, NULL);
+
+                       break;
+               }
+
+       case FE_GET_FRONTEND:
+               dprintk("%s: FE_GET_FRONTEND\n", __FUNCTION__);
+               break;
+
+       case FE_SLEEP:
+               dprintk("%s: FE_SLEEP\n", __FUNCTION__);
+               return -ENOSYS;
+               break;
+
+       case FE_INIT:
+               dprintk("%s: FE_INIT\n", __FUNCTION__);
+               break;
+
+       default:
+               dprintk("%s: unknown IOCTL (0x%X)\n", __FUNCTION__, cmd);
+               return -EINVAL;
+
+       }
+
+       return 0;
+}
+
+static int ttusb_dec_3000s_frontend_ioctl(struct dvb_frontend *fe,
+                                         unsigned int cmd, void *arg)
+{
+       struct ttusb_dec *dec = fe->data;
+
+       dprintk("%s\n", __FUNCTION__);
+
+       switch (cmd) {
+
+       case FE_GET_INFO:
+               dprintk("%s: FE_GET_INFO\n", __FUNCTION__);
+               memcpy(arg, dec->frontend_info,
+                      sizeof (struct dvb_frontend_info));
+               break;
+
+       case FE_READ_STATUS: {
+                       fe_status_t *status = (fe_status_t *)arg;
+                       dprintk("%s: FE_READ_STATUS\n", __FUNCTION__);
+                       *status = FE_HAS_SIGNAL | FE_HAS_VITERBI |
+                                 FE_HAS_SYNC | FE_HAS_CARRIER | FE_HAS_LOCK;
+                       break;
+               }
+
+       case FE_READ_BER: {
+                       u32 *ber = (u32 *)arg;
+                       dprintk("%s: FE_READ_BER\n", __FUNCTION__);
+                       *ber = 0;
+                       return -ENOSYS;
+                       break;
+               }
+
+       case FE_READ_SIGNAL_STRENGTH: {
+                       dprintk("%s: FE_READ_SIGNAL_STRENGTH\n", __FUNCTION__);
+                       *(s32 *)arg = 0xFF;
+                       return -ENOSYS;
+                       break;
+               }
+
+       case FE_READ_SNR:
+               dprintk("%s: FE_READ_SNR\n", __FUNCTION__);
+               *(s32 *)arg = 0;
+               return -ENOSYS;
+               break;
+
+       case FE_READ_UNCORRECTED_BLOCKS:
+               dprintk("%s: FE_READ_UNCORRECTED_BLOCKS\n", __FUNCTION__);
+               *(u32 *)arg = 0;
+               return -ENOSYS;
+               break;
+
+       case FE_SET_FRONTEND: {
+                       struct dvb_frontend_parameters *p =
+                               (struct dvb_frontend_parameters *)arg;
+               u8 b[] = { 0x00, 0x00, 0x00, 0x01,
+                          0x00, 0x00, 0x00, 0x00,
+                          0x00, 0x00, 0x00, 0x01,
+                          0x00, 0x00, 0x00, 0x00,
+                          0x00, 0x00, 0x00, 0x00,
+                          0x00, 0x00, 0x00, 0x00,
+                          0x00, 0x00, 0x00, 0x00,
+                          0x00, 0x00, 0x00, 0x00,
+                          0x00, 0x00, 0x00, 0x00,
+                          0x00, 0x00, 0x00, 0x00 };
+                       u32 freq;
+                       u32 sym_rate;
+                       u32 band;
+               u32 lnb_voltage;
+
+                       dprintk("%s: FE_SET_FRONTEND\n", __FUNCTION__);
+
+                       dprintk("            frequency->%d\n", p->frequency);
+                       dprintk("            symbol_rate->%d\n",
+                               p->u.qam.symbol_rate);
+                       dprintk("            inversion->%d\n", p->inversion);
+
+               freq = htonl(p->frequency * 1000 +
+                      (dec->hi_band ? LOF_HI : LOF_LO));
+                       memcpy(&b[4], &freq, sizeof(u32));
+                       sym_rate = htonl(p->u.qam.symbol_rate);
+                       memcpy(&b[12], &sym_rate, sizeof(u32));
+                       band = htonl(dec->hi_band ? LOF_HI : LOF_LO);
+                       memcpy(&b[24], &band, sizeof(u32));
+               lnb_voltage = htonl(dec->voltage);
+               memcpy(&b[28], &lnb_voltage, sizeof(u32));
+
+                       ttusb_dec_send_command(dec, 0x71, sizeof(b), b, NULL, NULL);
+
+                       break;
+               }
+
+       case FE_GET_FRONTEND:
+               dprintk("%s: FE_GET_FRONTEND\n", __FUNCTION__);
+               break;
+
+       case FE_SLEEP:
+               dprintk("%s: FE_SLEEP\n", __FUNCTION__);
+               return -ENOSYS;
+               break;
+
+       case FE_INIT:
+               dprintk("%s: FE_INIT\n", __FUNCTION__);
+               break;
+
+       case FE_DISEQC_SEND_MASTER_CMD:
+               dprintk("%s: FE_DISEQC_SEND_MASTER_CMD\n", __FUNCTION__);
+               break;
+
+       case FE_DISEQC_SEND_BURST:
+               dprintk("%s: FE_DISEQC_SEND_BURST\n", __FUNCTION__);
+               break;
+
+       case FE_SET_TONE: {
+                       fe_sec_tone_mode_t tone = (fe_sec_tone_mode_t)arg;
+                       dprintk("%s: FE_SET_TONE\n", __FUNCTION__);
+                       dec->hi_band = (SEC_TONE_ON == tone);
+                       break;
+               }
+
+       case FE_SET_VOLTAGE:
+               dprintk("%s: FE_SET_VOLTAGE\n", __FUNCTION__);
+               switch ((fe_sec_voltage_t) arg) {
+               case SEC_VOLTAGE_13:
+                       dec->voltage = 13;
+                       break;
+               case SEC_VOLTAGE_18:
+                       dec->voltage = 18;
+                       break;
+               default:
+                       return -EINVAL;
+                       break;
+               }
+               break;
+
+       default:
+               dprintk("%s: unknown IOCTL (0x%X)\n", __FUNCTION__, cmd);
+               return -EINVAL;
+
+       }
+
+       return 0;
+}
+
+static void ttusb_dec_init_frontend(struct ttusb_dec *dec)
+{
+       dec->i2c_bus.adapter = dec->adapter;
+
+       dvb_register_frontend(dec->frontend_ioctl, &dec->i2c_bus, (void *)dec,
+                             dec->frontend_info);
+}
+
+static void ttusb_dec_exit_frontend(struct ttusb_dec *dec)
+{
+       dvb_unregister_frontend(dec->frontend_ioctl, &dec->i2c_bus);
+}
+
 static void ttusb_dec_init_filters(struct ttusb_dec *dec)
 {
        INIT_LIST_HEAD(&dec->filter_info_list);
@@ -1416,18 +1698,6 @@ static void ttusb_dec_exit_filters(struct ttusb_dec *dec)
        }
 }
 
-int fe_send_command(struct dvb_frontend* fe, const u8 command,
-                   int param_length, const u8 params[],
-                   int *result_length, u8 cmd_result[])
-{
-       struct ttusb_dec* dec = (struct ttusb_dec*) fe->dvb->priv;
-       return ttusb_dec_send_command(dec, command, param_length, params, result_length, cmd_result);
-}
-
-struct ttusbdecfe_config fe_config = {
-       .send_command = fe_send_command
-};
-
 static int ttusb_dec_probe(struct usb_interface *intf,
                           const struct usb_device_id *id)
 {
@@ -1469,32 +1739,7 @@ static int ttusb_dec_probe(struct usb_interface *intf,
                return 0;
        }
        ttusb_dec_init_dvb(dec);
-
-       dec->adapter->priv = dec;
-       switch (id->idProduct) {
-       case 0x1006:
-               dec->fe = ttusbdecfe_dvbs_attach(&fe_config);
-               break;
-
-       case 0x1008:
-       case 0x1009:
-               dec->fe = ttusbdecfe_dvbt_attach(&fe_config);
-               break;
-       }
-
-       if (dec->fe == NULL) {
-               printk("dvb-ttusb-dec: A frontend driver was not found for device %04x/%04x\n",
-                      dec->udev->descriptor.idVendor,
-                      dec->udev->descriptor.idProduct);
-       } else {
-               if (dvb_register_frontend(dec->adapter, dec->fe)) {
-                       printk("budget-ci: Frontend registration failed!\n");
-                       if (dec->fe->ops->release)
-                               dec->fe->ops->release(dec->fe);
-                       dec->fe = NULL;
-               }
-       }
-
+       ttusb_dec_init_frontend(dec);
        ttusb_dec_init_v_pes(dec);
        ttusb_dec_init_filters(dec);
        ttusb_dec_init_tasklet(dec);
@@ -1518,6 +1763,7 @@ static void ttusb_dec_disconnect(struct usb_interface *intf)
        ttusb_dec_exit_tasklet(dec);
                ttusb_dec_exit_filters(dec);
        ttusb_dec_exit_usb(dec);
+               ttusb_dec_exit_frontend(dec);
        ttusb_dec_exit_dvb(dec);
        }
 
@@ -1533,16 +1779,22 @@ static void ttusb_dec_set_model(struct ttusb_dec *dec,
        case TTUSB_DEC2000T:
                dec->model_name = "DEC2000-t";
                dec->firmware_name = "dvb-ttusb-dec-2000t.fw";
+               dec->frontend_info = &dec2000t_frontend_info;
+               dec->frontend_ioctl = ttusb_dec_2000t_frontend_ioctl;
                break;
 
        case TTUSB_DEC2540T:
                dec->model_name = "DEC2540-t";
                dec->firmware_name = "dvb-ttusb-dec-2540t.fw";
+               dec->frontend_info = &dec2000t_frontend_info;
+               dec->frontend_ioctl = ttusb_dec_2000t_frontend_ioctl;
                break;
 
        case TTUSB_DEC3000S:
                dec->model_name = "DEC3000-s";
                dec->firmware_name = "dvb-ttusb-dec-3000s.fw";
+               dec->frontend_info = &dec3000s_frontend_info;
+               dec->frontend_ioctl = ttusb_dec_3000s_frontend_ioctl;
                break;
        }
 }
@@ -1588,3 +1840,7 @@ MODULE_DESCRIPTION(DRIVER_NAME);
 MODULE_LICENSE("GPL");
 MODULE_DEVICE_TABLE(usb, ttusb_dec_table);
 
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug level");
+MODULE_PARM(output_pva, "i");
+MODULE_PARM_DESC(output_pva, "Output PVA from dvr device");