]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
V4L/DVB (8972): initial driver for af9015 chipset
authorAntti Palosaari <crope@iki.fi>
Mon, 15 Sep 2008 20:18:09 +0000 (17:18 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Sun, 12 Oct 2008 11:37:03 +0000 (09:37 -0200)
- initial driver for the Afatech AF9015 chipset

Thanks-to: Mark Spieth <mark@digivation.com.au>
Thanks-to: Lee Essen <lee.essen@nowonline.co.uk>
Thanks-to: Luca Olivetti <luca@ventoso.org>
Thanks-to: Andrew Leech <andrew@floppyspongeonline.com>
Thanks-to: Nick Andrew <nick-linuxtv@nick-andrew.net>
Thanks-to: Rafael Antoniello <rafael.antoniello@gmail.com>
Thanks-to: Jarryd Beck <jarro.2783@gmail.com>
Thanks-to: Jose Alberto Reguero <jareguero@telefonica.net>
Thanks-to: Benjamin Larsson <banan@ludd.ltu.se>
Thanks-to: Wolfgang Breyha <wbreyha@gmx.net>
Signed-off-by: Antti Palosaari <crope@iki.fi>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/dvb/dvb-usb/Kconfig
drivers/media/dvb/dvb-usb/Makefile
drivers/media/dvb/dvb-usb/af9015.c [new file with mode: 0644]
drivers/media/dvb/dvb-usb/af9015.h [new file with mode: 0644]
drivers/media/dvb/dvb-usb/dvb-usb-ids.h

index 1d9e98cee9e7b109d53016ed6b9ad40c239ee42d..b35d7f0db9aa81e0d065c57e77004d3b60d67b9e 100644 (file)
@@ -271,3 +271,15 @@ config DVB_USB_DTV5100
        select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
        help
          Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver.
+
+config DVB_USB_AF9015
+       tristate "Afatech AF9015 DVB-T USB2.0 support"
+       depends on DVB_USB && EXPERIMENTAL
+       select DVB_AF9013
+       select DVB_PLL              if !DVB_FE_CUSTOMISE
+       select MEDIA_TUNER_MT2060   if !DVB_FE_CUSTOMISE
+       select MEDIA_TUNER_QT1010   if !DVB_FE_CUSTOMISE
+       select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMISE
+       select MEDIA_TUNER_MXL5005S if !DVB_FE_CUSTOMISE
+       help
+         Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver
index 116544f4272d260260c6395ee193026e06bcaa6f..ece8c9dfed183ad475ede08b54fc83d899216c5b 100644 (file)
@@ -70,6 +70,9 @@ obj-$(CONFIG_DVB_USB_DW2102) += dvb-usb-dw2102.o
 dvb-usb-dtv5100-objs = dtv5100.o
 obj-$(CONFIG_DVB_USB_DTV5100) += dvb-usb-dtv5100.o
 
+dvb-usb-af9015-objs = af9015.o
+obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o
+
 EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
 # due to tuner-xc3028
 EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
new file mode 100644 (file)
index 0000000..a284648
--- /dev/null
@@ -0,0 +1,1459 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "af9015.h"
+#include "af9013.h"
+#include "mt2060.h"
+#include "qt1010.h"
+#include "tda18271.h"
+#include "mxl5005s.h"
+#if 0
+#include "mc44s80x.h"
+#endif
+
+int dvb_usb_af9015_debug;
+module_param_named(debug, dvb_usb_af9015_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+int dvb_usb_af9015_remote;
+module_param_named(remote, dvb_usb_af9015_remote, int, 0644);
+MODULE_PARM_DESC(remote, "select remote");
+int dvb_usb_af9015_dual_mode;
+module_param_named(dual_mode, dvb_usb_af9015_dual_mode, int, 0644);
+MODULE_PARM_DESC(dual_mode, "enable dual mode");
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static DEFINE_MUTEX(af9015_usb_mutex);
+
+static struct af9015_config af9015_config;
+static struct dvb_usb_device_properties af9015_properties[2];
+int af9015_properties_count = ARRAY_SIZE(af9015_properties);
+
+static struct af9013_config af9015_af9013_config[] = {
+       {
+               .demod_address = AF9015_I2C_DEMOD,
+               .output_mode = AF9013_OUTPUT_MODE_USB,
+               .api_version = { 0, 1, 9, 0 },
+               .gpio[0] = AF9013_GPIO_HI,
+               .gpio[1] = AF9013_GPIO_LO,
+               .gpio[3] = AF9013_GPIO_TUNER_ON,
+
+       }, {
+               .output_mode = AF9013_OUTPUT_MODE_SERIAL,
+               .api_version = { 0, 1, 9, 0 },
+               .gpio[0] = AF9013_GPIO_TUNER_ON,
+               .gpio[1] = AF9013_GPIO_LO,
+       }
+};
+
+static int af9015_rw_udev(struct usb_device *udev, struct req_t *req)
+{
+       int act_len, ret;
+       u8 buf[64];
+       u8 write = 1;
+       u8 msg_len = 8;
+       static u8 seq; /* packet sequence number */
+
+       if (mutex_lock_interruptible(&af9015_usb_mutex) < 0)
+               return -EAGAIN;
+
+       buf[0] = req->cmd;
+       buf[1] = seq++;
+       buf[2] = req->i2c_addr;
+       buf[3] = req->addr >> 8;
+       buf[4] = req->addr & 0xff;
+       buf[5] = req->mbox;
+       buf[6] = req->addr_len;
+       buf[7] = req->data_len;
+
+       switch (req->cmd) {
+       case GET_CONFIG:
+       case BOOT:
+       case READ_MEMORY:
+       case RECONNECT_USB:
+       case GET_IR_CODE:
+               write = 0;
+               break;
+       case READ_I2C:
+               write = 0;
+               buf[2] |= 0x01; /* set I2C direction */
+       case WRITE_I2C:
+               buf[0] = READ_WRITE_I2C;
+               break;
+       case WRITE_MEMORY:
+               if (((req->addr & 0xff00) == 0xff00) ||
+                   ((req->addr & 0xae00) == 0xae00))
+                       buf[0] = WRITE_VIRTUAL_MEMORY;
+       case WRITE_VIRTUAL_MEMORY:
+       case COPY_FIRMWARE:
+       case DOWNLOAD_FIRMWARE:
+               break;
+       default:
+               err("unknown command:%d", req->cmd);
+               ret = -1;
+               goto error_unlock;
+       }
+
+       /* write requested */
+       if (write) {
+               memcpy(&buf[8], req->data, req->data_len);
+               msg_len += req->data_len;
+       }
+       deb_xfer(">>> ");
+       debug_dump(buf, msg_len, deb_xfer);
+
+       /* send req */
+       ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len,
+       &act_len, AF9015_USB_TIMEOUT);
+       if (ret)
+               err("bulk message failed:%d (%d/%d)", ret, msg_len, act_len);
+       else
+               if (act_len != msg_len)
+                       ret = -1; /* all data is not send */
+       if (ret)
+               goto error_unlock;
+
+       /* no ack for those packets */
+       if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB)
+               goto exit_unlock;
+
+       /* receive ack and data if read req */
+       msg_len = 1 + 1 + req->data_len;  /* seq + status + data len */
+       ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len,
+                          &act_len, AF9015_USB_TIMEOUT);
+       if (ret) {
+               err("recv bulk message failed:%d", ret);
+               ret = -1;
+               goto error_unlock;
+       }
+
+       deb_xfer("<<< ");
+       debug_dump(buf, act_len, deb_xfer);
+
+       /* remote controller query status is 1 if remote code is not received */
+       if (req->cmd == GET_IR_CODE && buf[1] == 1) {
+               buf[1] = 0; /* clear command "error" status */
+               memset(&buf[2], 0, req->data_len);
+               buf[3] = 1; /* no remote code received mark */
+       }
+
+       /* check status */
+       if (buf[1]) {
+               err("command failed:%d", buf[1]);
+               ret = -1;
+               goto error_unlock;
+       }
+
+       /* read request, copy returned data to return buf */
+       if (!write)
+               memcpy(req->data, &buf[2], req->data_len);
+
+error_unlock:
+exit_unlock:
+       mutex_unlock(&af9015_usb_mutex);
+
+       return ret;
+}
+
+static int af9015_ctrl_msg(struct dvb_usb_device *d, struct req_t *req)
+{
+       return af9015_rw_udev(d->udev, req);
+}
+
+static int af9015_write_regs(struct dvb_usb_device *d, u16 addr, u8 *val,
+       u8 len)
+{
+       struct req_t req = {WRITE_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, len,
+               val};
+       return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_write_reg(struct dvb_usb_device *d, u16 addr, u8 val)
+{
+       return af9015_write_regs(d, addr, &val, 1);
+}
+
+static int af9015_read_reg(struct dvb_usb_device *d, u16 addr, u8 *val)
+{
+       struct req_t req = {READ_MEMORY, AF9015_I2C_DEMOD, addr, 0, 0, 1, val};
+       return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_write_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
+       u8 val)
+{
+       struct req_t req = {WRITE_I2C, addr, reg, 1, 1, 1, &val};
+
+       if (addr == af9015_af9013_config[0].demod_address ||
+           addr == af9015_af9013_config[1].demod_address)
+               req.addr_len = 3;
+
+       return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_read_reg_i2c(struct dvb_usb_device *d, u8 addr, u16 reg,
+       u8 *val)
+{
+       struct req_t req = {READ_I2C, addr, reg, 0, 1, 1, val};
+
+       if (addr == af9015_af9013_config[0].demod_address ||
+           addr == af9015_af9013_config[1].demod_address)
+               req.addr_len = 3;
+
+       return af9015_ctrl_msg(d, &req);
+}
+
+static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+       int num)
+{
+       struct dvb_usb_device *d = i2c_get_adapdata(adap);
+       int ret = 0, i = 0;
+       u16 addr;
+       u8 mbox, addr_len;
+       struct req_t req;
+
+/* TODO: implement bus lock
+
+The bus lock is needed because there is two tuners both using same I2C-address.
+Due to that the only way to select correct tuner is use demodulator I2C-gate.
+
+................................................
+. AF9015 includes integrated AF9013 demodulator.
+. ____________                   ____________  .                ____________
+.|     uC     |                 |   demod    | .               |    tuner   |
+.|------------|                 |------------| .               |------------|
+.|   AF9015   |                 |  AF9013/5  | .               |   MXL5003  |
+.|            |--+----I2C-------|-----/ -----|-.-----I2C-------|            |
+.|            |  |              | addr 0x38  | .               |  addr 0xc6 |
+.|____________|  |              |____________| .               |____________|
+.................|..............................
+                |               ____________                   ____________
+                |              |   demod    |                 |    tuner   |
+                |              |------------|                 |------------|
+                |              |   AF9013   |                 |   MXL5003  |
+                +----I2C-------|-----/ -----|-------I2C-------|            |
+                               | addr 0x3a  |                 |  addr 0xc6 |
+                               |____________|                 |____________|
+*/
+       if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+               return -EAGAIN;
+
+       while (i < num) {
+               if (msg[i].addr == af9015_af9013_config[0].demod_address ||
+                   msg[i].addr == af9015_af9013_config[1].demod_address) {
+                       addr = msg[i].buf[0] << 8;
+                       addr += msg[i].buf[1];
+                       mbox = msg[i].buf[2];
+                       addr_len = 3;
+               } else {
+                       addr = msg[i].buf[0];
+                       addr_len = 1;
+                       mbox = 0;
+               }
+
+               if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
+                       if (msg[i].addr ==
+                               af9015_af9013_config[0].demod_address)
+                               req.cmd = READ_MEMORY;
+                       else
+                               req.cmd = READ_I2C;
+                       req.i2c_addr = msg[i].addr;
+                       req.addr = addr;
+                       req.mbox = mbox;
+                       req.addr_len = addr_len;
+                       req.data_len = msg[i+1].len;
+                       req.data = &msg[i+1].buf[0];
+                       ret = af9015_ctrl_msg(d, &req);
+                       i += 2;
+               } else {
+                       if (msg[i].addr ==
+                               af9015_af9013_config[0].demod_address)
+                               req.cmd = WRITE_MEMORY;
+                       else
+                               req.cmd = WRITE_I2C;
+                       req.i2c_addr = msg[i].addr;
+                       req.addr = addr;
+                       req.mbox = mbox;
+                       req.addr_len = addr_len;
+                       req.data_len = msg[i].len-addr_len;
+                       req.data = &msg[i].buf[addr_len];
+                       ret = af9015_ctrl_msg(d, &req);
+                       i += 1;
+               }
+               if (ret)
+                       goto error;
+
+       }
+       ret = i;
+
+error:
+       mutex_unlock(&d->i2c_mutex);
+
+       return ret;
+}
+
+static u32 af9015_i2c_func(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm af9015_i2c_algo = {
+       .master_xfer = af9015_i2c_xfer,
+       .functionality = af9015_i2c_func,
+};
+
+static int af9015_do_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit, u8 op)
+{
+       int ret;
+       u8 val, mask = 0x01;
+
+       ret = af9015_read_reg(d, addr, &val);
+       if (ret)
+               return ret;
+
+       mask <<= bit;
+       if (op) {
+               /* set bit */
+               val |= mask;
+       } else {
+               /* clear bit */
+               mask ^= 0xff;
+               val &= mask;
+       }
+
+       return af9015_write_reg(d, addr, val);
+}
+
+static int af9015_set_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
+{
+       return af9015_do_reg_bit(d, addr, bit, 1);
+}
+
+static int af9015_clear_reg_bit(struct dvb_usb_device *d, u16 addr, u8 bit)
+{
+       return af9015_do_reg_bit(d, addr, bit, 0);
+}
+
+static int af9015_init_endpoint(struct dvb_usb_device *d)
+{
+       int ret;
+       u16 frame_size;
+       u8  packet_size;
+       deb_info("%s: USB speed:%d\n", __func__, d->udev->speed);
+
+#define TS_PACKET_SIZE            188
+
+#define TS_USB20_PACKET_COUNT     348
+#define TS_USB20_FRAME_SIZE       (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT)
+
+#define TS_USB11_PACKET_COUNT      21
+#define TS_USB11_FRAME_SIZE       (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT)
+
+#define TS_USB20_MAX_PACKET_SIZE  512
+#define TS_USB11_MAX_PACKET_SIZE   64
+
+       if (d->udev->speed == USB_SPEED_FULL) {
+               frame_size = TS_USB11_FRAME_SIZE/4;
+               packet_size = TS_USB11_MAX_PACKET_SIZE/4;
+       } else {
+               frame_size = TS_USB20_FRAME_SIZE/4;
+               packet_size = TS_USB20_MAX_PACKET_SIZE/4;
+       }
+
+       ret = af9015_set_reg_bit(d, 0xd507, 2); /* assert EP4 reset */
+       if (ret)
+               goto error;
+       ret = af9015_set_reg_bit(d, 0xd50b, 1); /* assert EP5 reset */
+       if (ret)
+               goto error;
+       ret = af9015_clear_reg_bit(d, 0xdd11, 5); /* disable EP4 */
+       if (ret)
+               goto error;
+       ret = af9015_clear_reg_bit(d, 0xdd11, 6); /* disable EP5 */
+       if (ret)
+               goto error;
+       ret = af9015_set_reg_bit(d, 0xdd11, 5); /* enable EP4 */
+       if (ret)
+               goto error;
+       if (af9015_config.dual_mode) {
+               ret = af9015_set_reg_bit(d, 0xdd11, 6); /* enable EP5 */
+               if (ret)
+                       goto error;
+       }
+       ret = af9015_clear_reg_bit(d, 0xdd13, 5); /* disable EP4 NAK */
+       if (ret)
+               goto error;
+       if (af9015_config.dual_mode) {
+               ret = af9015_clear_reg_bit(d, 0xdd13, 6); /* disable EP5 NAK */
+               if (ret)
+                       goto error;
+       }
+       /* EP4 xfer length */
+       ret = af9015_write_reg(d, 0xdd88, frame_size & 0xff);
+       if (ret)
+               goto error;
+       ret = af9015_write_reg(d, 0xdd89, frame_size >> 8);
+       if (ret)
+               goto error;
+       /* EP5 xfer length */
+       ret = af9015_write_reg(d, 0xdd8a, frame_size & 0xff);
+       if (ret)
+               goto error;
+       ret = af9015_write_reg(d, 0xdd8b, frame_size >> 8);
+       if (ret)
+               goto error;
+       ret = af9015_write_reg(d, 0xdd0c, packet_size); /* EP4 packet size */
+       if (ret)
+               goto error;
+       ret = af9015_write_reg(d, 0xdd0d, packet_size); /* EP5 packet size */
+       if (ret)
+               goto error;
+       ret = af9015_clear_reg_bit(d, 0xd507, 2); /* negate EP4 reset */
+       if (ret)
+               goto error;
+       if (af9015_config.dual_mode) {
+               ret = af9015_clear_reg_bit(d, 0xd50b, 1); /* negate EP5 reset */
+               if (ret)
+                       goto error;
+       }
+
+       /* enable / disable mp2if2 */
+       if (af9015_config.dual_mode)
+               ret = af9015_set_reg_bit(d, 0xd50b, 0);
+       else
+               ret = af9015_clear_reg_bit(d, 0xd50b, 0);
+error:
+       if (ret)
+               err("endpoint init failed:%d", ret);
+       return ret;
+}
+
+static int af9015_copy_firmware(struct dvb_usb_device *d)
+{
+       int ret;
+       u8 fw_params[4];
+       u8 val, i;
+       struct req_t req = {COPY_FIRMWARE, 0, 0x5100, 0, 0, sizeof(fw_params),
+               fw_params };
+       deb_info("%s:\n", __func__);
+
+       fw_params[0] = af9015_config.firmware_size >> 8;
+       fw_params[1] = af9015_config.firmware_size & 0xff;
+       fw_params[2] = af9015_config.firmware_checksum >> 8;
+       fw_params[3] = af9015_config.firmware_checksum & 0xff;
+
+       /* wait 2nd demodulator ready */
+       msleep(100);
+
+       ret = af9015_read_reg_i2c(d, 0x3a, 0x98be, &val);
+       if (ret)
+               goto error;
+       else
+               deb_info("%s: firmware status:%02x\n", __func__, val);
+
+       if (val == 0x0c) /* fw is running, no need for download */
+               goto exit;
+
+       /* set I2C master clock to fast (to speed up firmware copy) */
+       ret = af9015_write_reg(d, 0xd416, 0x04); /* 0x04 * 400ns */
+       if (ret)
+               goto error;
+
+       msleep(50);
+
+       /* copy firmware */
+       ret = af9015_ctrl_msg(d, &req);
+       if (ret)
+               err("firmware copy cmd failed:%d", ret);
+       deb_info("%s: firmware copy done\n", __func__);
+
+       /* set I2C master clock back to normal */
+       ret = af9015_write_reg(d, 0xd416, 0x14); /* 0x14 * 400ns */
+       if (ret)
+               goto error;
+
+       /* request boot firmware */
+       ret = af9015_write_reg_i2c(d, af9015_af9013_config[1].demod_address,
+               0xe205, 1);
+       deb_info("%s: firmware boot cmd status:%d\n", __func__, ret);
+       if (ret)
+               goto error;
+
+       for (i = 0; i < 15; i++) {
+               msleep(100);
+
+               /* check firmware status */
+               ret = af9015_read_reg_i2c(d,
+                       af9015_af9013_config[1].demod_address, 0x98be, &val);
+               deb_info("%s: firmware status cmd status:%d fw status:%02x\n",
+                       __func__, ret, val);
+               if (ret)
+                       goto error;
+
+               if (val == 0x0c || val == 0x04) /* success or fail */
+                       break;
+       }
+
+       if (val == 0x04) {
+               err("firmware did not run");
+               ret = -1;
+       } else if (val != 0x0c) {
+               err("firmware boot timeout");
+               ret = -1;
+       }
+
+error:
+exit:
+       return ret;
+}
+
+/* dump eeprom */
+static int af9015_eeprom_dump(struct dvb_usb_device *d)
+{
+       char buf[52], buf2[4];
+       u8 reg, val;
+
+       for (reg = 0; ; reg++) {
+               if (reg % 16 == 0) {
+                       if (reg)
+                               deb_info("%s\n", buf);
+                       sprintf(buf, "%02x: ", reg);
+               }
+               if (af9015_read_reg_i2c(d, AF9015_I2C_EEPROM, reg, &val) == 0)
+                       sprintf(buf2, "%02x ", val);
+               else
+                       strcpy(buf2, "-- ");
+               strcat(buf, buf2);
+               if (reg == 0xff)
+                       break;
+       }
+       deb_info("%s\n", buf);
+       return 0;
+}
+
+int af9015_download_ir_table(struct dvb_usb_device *d)
+{
+       int i, packets = 0, ret;
+       u16 addr = 0x9a56; /* ir-table start address */
+       struct req_t req = {WRITE_MEMORY, 0, 0, 0, 0, 1, NULL};
+       u8 *data = NULL;
+       deb_info("%s:\n", __func__);
+
+       data = af9015_config.ir_table;
+       packets = af9015_config.ir_table_size;
+
+       /* no remote */
+       if (!packets)
+               goto exit;
+
+       /* load remote ir-table */
+       for (i = 0; i < packets; i++) {
+               req.addr = addr + i;
+               req.data = &data[i];
+               ret = af9015_ctrl_msg(d, &req);
+               if (ret) {
+                       err("ir-table download failed at packet %d with " \
+                               "code %d", i, ret);
+                       return ret;
+               }
+       }
+
+exit:
+       return 0;
+}
+
+static int af9015_init(struct dvb_usb_device *d)
+{
+       int ret;
+       deb_info("%s:\n", __func__);
+
+       ret = af9015_init_endpoint(d);
+       if (ret)
+               goto error;
+
+       ret = af9015_download_ir_table(d);
+       if (ret)
+               goto error;
+
+error:
+       return ret;
+}
+
+static int af9015_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+       int ret;
+       deb_info("%s: onoff:%d\n", __func__, onoff);
+
+       if (onoff)
+               ret = af9015_set_reg_bit(adap->dev, 0xd503, 0);
+       else
+               ret = af9015_clear_reg_bit(adap->dev, 0xd503, 0);
+
+       return ret;
+}
+
+static int af9015_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
+       int onoff)
+{
+       int ret;
+       u8 idx;
+
+       deb_info("%s: set pid filter, index %d, pid %x, onoff %d\n",
+               __func__, index, pid, onoff);
+
+       ret = af9015_write_reg(adap->dev, 0xd505, (pid & 0xff));
+       if (ret)
+               goto error;
+
+       ret = af9015_write_reg(adap->dev, 0xd506, (pid >> 8));
+       if (ret)
+               goto error;
+
+       idx = ((index & 0x1f) | (1 << 5));
+       ret = af9015_write_reg(adap->dev, 0xd504, idx);
+
+error:
+       return ret;
+}
+
+static int af9015_download_firmware(struct usb_device *udev,
+       const struct firmware *fw)
+{
+       int i, len, packets, remainder, ret;
+       struct req_t req = {DOWNLOAD_FIRMWARE, 0, 0, 0, 0, 0, NULL};
+       u16 addr = 0x5100; /* firmware start address */
+       u16 checksum = 0;
+
+       deb_info("%s:\n", __func__);
+
+       /* calc checksum */
+       for (i = 0; i < fw->size; i++)
+               checksum += fw->data[i];
+
+       af9015_config.firmware_size = fw->size;
+       af9015_config.firmware_checksum = checksum;
+
+       #define FW_PACKET_MAX_DATA  55
+
+       packets = fw->size / FW_PACKET_MAX_DATA;
+       remainder = fw->size % FW_PACKET_MAX_DATA;
+       len = FW_PACKET_MAX_DATA;
+       for (i = 0; i <= packets; i++) {
+               if (i == packets)  /* set size of the last packet */
+                       len = remainder;
+
+               req.data_len = len;
+               req.data = (fw->data + i * FW_PACKET_MAX_DATA);
+               req.addr = addr;
+               addr += FW_PACKET_MAX_DATA;
+
+               ret = af9015_rw_udev(udev, &req);
+               if (ret) {
+                       err("firmware download failed at packet %d with " \
+                               "code %d", i, ret);
+                       goto error;
+               }
+       }
+       #undef FW_PACKET_MAX_DATA
+
+       /* firmware loaded, request boot */
+       req.cmd = BOOT;
+       ret = af9015_rw_udev(udev, &req);
+       if (ret) {
+               err("firmware boot failed:%d", ret);
+               goto error;
+       }
+
+       /* firmware is running, reconnect device in the usb bus */
+       req.cmd = RECONNECT_USB;
+       ret = af9015_rw_udev(udev, &req);
+       if (ret)
+               err("reconnect failed: %d", ret);
+
+error:
+       return ret;
+}
+
+static int af9015_read_config(struct usb_device *udev)
+{
+       int ret;
+       u8 val, i, offset = 0;
+       struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val};
+       char manufacturer[10];
+
+       /* IR remote controller */
+       req.addr = AF9015_EEPROM_IR_MODE;
+       ret = af9015_rw_udev(udev, &req);
+       if (ret)
+               goto error;
+       deb_info("%s: IR mode:%d\n", __func__, val);
+       for (i = 0; i < af9015_properties_count; i++) {
+               if (val == AF9015_IR_MODE_DISABLED || val == 0x04) {
+                       af9015_properties[i].rc_key_map = NULL;
+                       af9015_properties[i].rc_key_map_size  = 0;
+               } else if (dvb_usb_af9015_remote) {
+                       /* load remote defined as module param */
+                       switch (dvb_usb_af9015_remote) {
+                       case AF9015_REMOTE_A_LINK_DTU_M:
+                               af9015_properties[i].rc_key_map =
+                                 af9015_rc_keys_a_link;
+                               af9015_properties[i].rc_key_map_size =
+                                 ARRAY_SIZE(af9015_rc_keys_a_link);
+                               af9015_config.ir_table = af9015_ir_table_a_link;
+                               af9015_config.ir_table_size =
+                                 ARRAY_SIZE(af9015_ir_table_a_link);
+                               break;
+                       case AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3:
+                               af9015_properties[i].rc_key_map =
+                                 af9015_rc_keys_msi;
+                               af9015_properties[i].rc_key_map_size =
+                                 ARRAY_SIZE(af9015_rc_keys_msi);
+                               af9015_config.ir_table = af9015_ir_table_msi;
+                               af9015_config.ir_table_size =
+                                 ARRAY_SIZE(af9015_ir_table_msi);
+                               break;
+                       case AF9015_REMOTE_MYGICTV_U718:
+                               af9015_properties[i].rc_key_map =
+                                 af9015_rc_keys_mygictv;
+                               af9015_properties[i].rc_key_map_size =
+                                 ARRAY_SIZE(af9015_rc_keys_mygictv);
+                               af9015_config.ir_table =
+                                 af9015_ir_table_mygictv;
+                               af9015_config.ir_table_size =
+                                 ARRAY_SIZE(af9015_ir_table_mygictv);
+                               break;
+                       }
+               } else {
+                       switch (udev->descriptor.idVendor) {
+                       case cpu_to_le16(USB_VID_LEADTEK):
+                               af9015_properties[i].rc_key_map =
+                                 af9015_rc_keys_leadtek;
+                               af9015_properties[i].rc_key_map_size =
+                                 ARRAY_SIZE(af9015_rc_keys_leadtek);
+                               af9015_config.ir_table =
+                                 af9015_ir_table_leadtek;
+                               af9015_config.ir_table_size =
+                                 ARRAY_SIZE(af9015_ir_table_leadtek);
+                               break;
+                       case cpu_to_le16(USB_VID_VISIONPLUS):
+                               if (udev->descriptor.idProduct ==
+                               cpu_to_le16(USB_PID_AZUREWAVE_AD_TU700)) {
+                                       af9015_properties[i].rc_key_map =
+                                         af9015_rc_keys_twinhan;
+                                       af9015_properties[i].rc_key_map_size =
+                                         ARRAY_SIZE(af9015_rc_keys_twinhan);
+                                       af9015_config.ir_table =
+                                         af9015_ir_table_twinhan;
+                                       af9015_config.ir_table_size =
+                                         ARRAY_SIZE(af9015_ir_table_twinhan);
+                               }
+                               break;
+                       case cpu_to_le16(USB_VID_KWORLD_2):
+                               /* TODO: use correct rc keys */
+                               af9015_properties[i].rc_key_map =
+                                 af9015_rc_keys_twinhan;
+                               af9015_properties[i].rc_key_map_size =
+                                 ARRAY_SIZE(af9015_rc_keys_twinhan);
+                               af9015_config.ir_table = af9015_ir_table_kworld;
+                               af9015_config.ir_table_size =
+                                 ARRAY_SIZE(af9015_ir_table_kworld);
+                               break;
+                       /* Check USB manufacturer and product strings and try
+                          to determine correct remote in case of chip vendor
+                          reference IDs are used. */
+                       case cpu_to_le16(USB_VID_AFATECH):
+                               memset(manufacturer, 0, sizeof(manufacturer));
+                               usb_string(udev, udev->descriptor.iManufacturer,
+                                       manufacturer, sizeof(manufacturer));
+                               if (!strcmp("Geniatech", manufacturer)) {
+                                       /* iManufacturer 1 Geniatech
+                                          iProduct      2 AF9015 */
+                                       af9015_properties[i].rc_key_map =
+                                         af9015_rc_keys_mygictv;
+                                       af9015_properties[i].rc_key_map_size =
+                                         ARRAY_SIZE(af9015_rc_keys_mygictv);
+                                       af9015_config.ir_table =
+                                         af9015_ir_table_mygictv;
+                                       af9015_config.ir_table_size =
+                                         ARRAY_SIZE(af9015_ir_table_mygictv);
+                               } else if (!strcmp("MSI", manufacturer)) {
+                                       /* iManufacturer 1 MSI
+                                          iProduct      2 MSI K-VOX */
+                                       af9015_properties[i].rc_key_map =
+                                         af9015_rc_keys_msi;
+                                       af9015_properties[i].rc_key_map_size =
+                                         ARRAY_SIZE(af9015_rc_keys_msi);
+                                       af9015_config.ir_table =
+                                         af9015_ir_table_msi;
+                                       af9015_config.ir_table_size =
+                                         ARRAY_SIZE(af9015_ir_table_msi);
+                               }
+                               break;
+                       }
+               }
+       }
+
+       /* TS mode - one or two receivers */
+       req.addr = AF9015_EEPROM_TS_MODE;
+       ret = af9015_rw_udev(udev, &req);
+       if (ret)
+               goto error;
+       af9015_config.dual_mode = val;
+       deb_info("%s: TS mode:%d\n", __func__, af9015_config.dual_mode);
+       /* disable dual mode by default because it is buggy */
+       if (!dvb_usb_af9015_dual_mode)
+               af9015_config.dual_mode = 0;
+
+       /* set buffer size according to USB port speed */
+       for (i = 0; i < af9015_properties_count; i++) {
+               /* USB1.1 set smaller buffersize and disable 2nd adapter */
+               if (udev->speed == USB_SPEED_FULL) {
+                       af9015_properties[i].adapter->stream.u.bulk.buffersize =
+                               TS_USB11_MAX_PACKET_SIZE;
+                       /* disable 2nd adapter because we don't have
+                          PID-filters */
+                       af9015_config.dual_mode = 0;
+               } else {
+                       af9015_properties[i].adapter->stream.u.bulk.buffersize =
+                               TS_USB20_MAX_PACKET_SIZE;
+               }
+       }
+
+       if (af9015_config.dual_mode) {
+               /* read 2nd demodulator I2C address */
+               req.addr = AF9015_EEPROM_DEMOD2_I2C;
+               ret = af9015_rw_udev(udev, &req);
+               if (ret)
+                       goto error;
+               af9015_af9013_config[1].demod_address = val;
+
+               /* enable 2nd adapter */
+               for (i = 0; i < af9015_properties_count; i++)
+                       af9015_properties[i].num_adapters = 2;
+
+       } else {
+                /* disable 2nd adapter */
+               for (i = 0; i < af9015_properties_count; i++)
+                       af9015_properties[i].num_adapters = 1;
+       }
+
+       for (i = 0; i < af9015_properties[0].num_adapters; i++) {
+               if (i == 1)
+                       offset = AF9015_EEPROM_OFFSET;
+               /* xtal */
+               req.addr = AF9015_EEPROM_XTAL_TYPE1 + offset;
+               ret = af9015_rw_udev(udev, &req);
+               if (ret)
+                       goto error;
+               switch (val) {
+               case 0:
+                       af9015_af9013_config[i].adc_clock = 28800;
+                       break;
+               case 1:
+                       af9015_af9013_config[i].adc_clock = 20480;
+                       break;
+               case 2:
+                       af9015_af9013_config[i].adc_clock = 28000;
+                       break;
+               case 3:
+                       af9015_af9013_config[i].adc_clock = 25000;
+                       break;
+               };
+               deb_info("%s: [%d] xtal:%d set adc_clock:%d\n", __func__, i,
+                       val, af9015_af9013_config[i].adc_clock);
+
+               /* tuner IF */
+               req.addr = AF9015_EEPROM_IF1H + offset;
+               ret = af9015_rw_udev(udev, &req);
+               if (ret)
+                       goto error;
+               af9015_af9013_config[i].tuner_if = val << 8;
+               req.addr = AF9015_EEPROM_IF1L + offset;
+               ret = af9015_rw_udev(udev, &req);
+               if (ret)
+                       goto error;
+               af9015_af9013_config[i].tuner_if += val;
+               deb_info("%s: [%d] IF1:%d\n", __func__, i,
+                       af9015_af9013_config[0].tuner_if);
+
+               /* MT2060 IF1 */
+               req.addr = AF9015_EEPROM_MT2060_IF1H  + offset;
+               ret = af9015_rw_udev(udev, &req);
+               if (ret)
+                       goto error;
+               af9015_config.mt2060_if1[i] = val << 8;
+               req.addr = AF9015_EEPROM_MT2060_IF1L + offset;
+               ret = af9015_rw_udev(udev, &req);
+               if (ret)
+                       goto error;
+               af9015_config.mt2060_if1[i] += val;
+               deb_info("%s: [%d] MT2060 IF1:%d\n", __func__, i,
+                       af9015_config.mt2060_if1[i]);
+
+               /* tuner */
+               req.addr =  AF9015_EEPROM_TUNER_ID1 + offset;
+               ret = af9015_rw_udev(udev, &req);
+               if (ret)
+                       goto error;
+               switch (val) {
+               case AF9013_TUNER_ENV77H11D5:
+               case AF9013_TUNER_MT2060:
+               case AF9013_TUNER_MC44S803:
+               case AF9013_TUNER_QT1010:
+               case AF9013_TUNER_UNKNOWN:
+               case AF9013_TUNER_MT2060_2:
+               case AF9013_TUNER_TDA18271:
+               case AF9013_TUNER_QT1010A:
+                       af9015_af9013_config[i].rf_spec_inv = 1;
+                       break;
+               case AF9013_TUNER_MXL5003D:
+               case AF9013_TUNER_MXL5005D:
+               case AF9013_TUNER_MXL5005R:
+                       af9015_af9013_config[i].rf_spec_inv = 0;
+                       break;
+               default:
+                       warn("tuner id:%d not supported, please report!", val);
+                       return -ENODEV;
+               };
+
+               af9015_af9013_config[i].tuner = val;
+               deb_info("%s: [%d] tuner id:%d\n", __func__, i, val);
+       }
+
+error:
+       if (ret)
+               err("eeprom read failed:%d", ret);
+
+       return ret;
+}
+
+static int af9015_identify_state(struct usb_device *udev,
+                                struct dvb_usb_device_properties *props,
+                                struct dvb_usb_device_description **desc,
+                                int *cold)
+{
+       int ret;
+       u8 reply;
+       struct req_t req = {GET_CONFIG, 0, 0, 0, 0, 1, &reply};
+
+       ret = af9015_rw_udev(udev, &req);
+       if (ret)
+               return ret;
+
+       deb_info("%s: reply:%02x\n", __func__, reply);
+       if (reply == 0x02)
+               *cold = 0;
+       else
+               *cold = 1;
+
+       return ret;
+}
+
+static int af9015_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
+{
+       u8 buf[8];
+       struct req_t req = {GET_IR_CODE, 0, 0, 0, 0, sizeof(buf), buf};
+       struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
+       int i, ret;
+
+       memset(buf, 0, sizeof(buf));
+
+       ret = af9015_ctrl_msg(d, &req);
+       if (ret)
+               return ret;
+
+       *event = 0;
+       *state = REMOTE_NO_KEY_PRESSED;
+
+       for (i = 0; i < d->props.rc_key_map_size; i++) {
+               if (!buf[1] && keymap[i].custom == buf[0] &&
+                   keymap[i].data == buf[2]) {
+                       *event = keymap[i].event;
+                       *state = REMOTE_KEY_PRESSED;
+                       break;
+               }
+       }
+       if (!buf[1])
+               deb_rc("%s: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+                       __func__, buf[0], buf[1], buf[2], buf[3], buf[4],
+                       buf[5], buf[6], buf[7]);
+
+       return 0;
+}
+
+/* init 2nd I2C adapter */
+int af9015_i2c_init(struct dvb_usb_device *d)
+{
+       int ret;
+       struct af9015_state *state = d->priv;
+       deb_info("%s:\n", __func__);
+
+       strncpy(state->i2c_adap.name, d->desc->name,
+               sizeof(state->i2c_adap.name));
+#ifdef I2C_ADAP_CLASS_TV_DIGITAL
+       state->i2c_adap.class = I2C_ADAP_CLASS_TV_DIGITAL,
+#else
+       state->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
+#endif
+       state->i2c_adap.algo      = d->props.i2c_algo;
+       state->i2c_adap.algo_data = NULL;
+       state->i2c_adap.dev.parent = &d->udev->dev;
+
+       i2c_set_adapdata(&state->i2c_adap, d);
+
+       ret = i2c_add_adapter(&state->i2c_adap);
+       if (ret < 0)
+               err("could not add i2c adapter");
+
+       return ret;
+}
+
+static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
+{
+       int ret;
+       struct af9015_state *state = adap->dev->priv;
+       struct i2c_adapter *i2c_adap;
+
+       if (adap->id == 0) {
+               /* select I2C adapter */
+               i2c_adap = &adap->dev->i2c_adap;
+
+               deb_info("%s: init I2C\n", __func__);
+               ret = af9015_i2c_init(adap->dev);
+
+               /* dump eeprom (debug) */
+               ret = af9015_eeprom_dump(adap->dev);
+               if (ret)
+                       return ret;
+       } else {
+               /* select I2C adapter */
+               i2c_adap = &state->i2c_adap;
+
+               /* copy firmware to 2nd demodulator */
+               if (af9015_config.dual_mode) {
+                       ret = af9015_copy_firmware(adap->dev);
+                       if (ret) {
+                               err("firmware copy to 2nd frontend " \
+                                       "failed, will disable it");
+                               af9015_config.dual_mode = 0;
+                               return -ENODEV;
+                       }
+               } else {
+                       return -ENODEV;
+               }
+       }
+
+       /* attach demodulator */
+       adap->fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id],
+               i2c_adap);
+
+       return adap->fe == NULL ? -ENODEV : 0;
+}
+
+static struct mt2060_config af9015_mt2060_config = {
+       .i2c_address = 0xc0,
+       .clock_out = 0,
+};
+
+static struct qt1010_config af9015_qt1010_config = {
+       .i2c_address = 0xc4,
+};
+
+static struct tda18271_config af9015_tda18271_config = {
+       .gate = TDA18271_GATE_DIGITAL,
+       .small_i2c = 1,
+};
+
+static struct mxl5005s_config af9015_mxl5003_config = {
+       .i2c_address     = 0xc6,
+       .if_freq         = IF_FREQ_4570000HZ,
+       .xtal_freq       = CRYSTAL_FREQ_16000000HZ,
+       .agc_mode        = MXL_SINGLE_AGC,
+       .tracking_filter = MXL_TF_DEFAULT,
+       .rssi_enable     = MXL_RSSI_DISABLE,
+       .cap_select      = MXL_CAP_SEL_ENABLE,
+       .div_out         = MXL_DIV_OUT_4,
+       .clock_out       = MXL_CLOCK_OUT_DISABLE,
+       .output_load     = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+       .top             = MXL5005S_TOP_25P2,
+       .mod_mode        = MXL_DIGITAL_MODE,
+       .if_mode         = MXL_ZERO_IF,
+       .AgcMasterByte   = 0x00,
+};
+
+static struct mxl5005s_config af9015_mxl5005_config = {
+       .i2c_address     = 0xc6,
+       .if_freq         = IF_FREQ_4570000HZ,
+       .xtal_freq       = CRYSTAL_FREQ_16000000HZ,
+       .agc_mode        = MXL_SINGLE_AGC,
+       .tracking_filter = MXL_TF_OFF,
+       .rssi_enable     = MXL_RSSI_DISABLE,
+       .cap_select      = MXL_CAP_SEL_ENABLE,
+       .div_out         = MXL_DIV_OUT_4,
+       .clock_out       = MXL_CLOCK_OUT_DISABLE,
+       .output_load     = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+       .top             = MXL5005S_TOP_25P2,
+       .mod_mode        = MXL_DIGITAL_MODE,
+       .if_mode         = MXL_ZERO_IF,
+       .AgcMasterByte   = 0x00,
+};
+
+static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
+{
+       struct af9015_state *state = adap->dev->priv;
+       struct i2c_adapter *i2c_adap;
+       int ret;
+       deb_info("%s: \n", __func__);
+
+       /* select I2C adapter */
+       if (adap->id == 0)
+               i2c_adap = &adap->dev->i2c_adap;
+       else
+               i2c_adap = &state->i2c_adap;
+
+       switch (af9015_af9013_config[adap->id].tuner) {
+       case AF9013_TUNER_MT2060:
+       case AF9013_TUNER_MT2060_2:
+               ret = dvb_attach(mt2060_attach, adap->fe, i2c_adap,
+                       &af9015_mt2060_config,
+                       af9015_config.mt2060_if1[adap->id])
+                       == NULL ? -ENODEV : 0;
+               break;
+       case AF9013_TUNER_QT1010:
+       case AF9013_TUNER_QT1010A:
+               ret = dvb_attach(qt1010_attach, adap->fe, i2c_adap,
+                       &af9015_qt1010_config) == NULL ? -ENODEV : 0;
+               break;
+       case AF9013_TUNER_TDA18271:
+               ret = dvb_attach(tda18271_attach, adap->fe, 0xc0, i2c_adap,
+                       &af9015_tda18271_config) == NULL ? -ENODEV : 0;
+               break;
+       case AF9013_TUNER_MXL5003D:
+               ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap,
+                       &af9015_mxl5003_config) == NULL ? -ENODEV : 0;
+               break;
+       case AF9013_TUNER_MXL5005D:
+       case AF9013_TUNER_MXL5005R:
+               ret = dvb_attach(mxl5005s_attach, adap->fe, i2c_adap,
+                       &af9015_mxl5005_config) == NULL ? -ENODEV : 0;
+               break;
+       case AF9013_TUNER_ENV77H11D5:
+               ret = dvb_attach(dvb_pll_attach, adap->fe, 0xc0, i2c_adap,
+                       DVB_PLL_TDA665X) == NULL ? -ENODEV : 0;
+               break;
+       case AF9013_TUNER_MC44S803:
+#if 0
+               ret = dvb_attach(mc44s80x_attach, adap->fe, i2c_adap)
+                       == NULL ? -ENODEV : 0;
+#else
+               ret = -ENODEV;
+               info("Freescale MC44S803 tuner found but no driver for that" \
+                       "tuner. Look at the Linuxtv.org for tuner driver" \
+                       "status.");
+#endif
+               break;
+       case AF9013_TUNER_UNKNOWN:
+       default:
+               ret = -ENODEV;
+               err("Unknown tuner id:%d",
+                       af9015_af9013_config[adap->id].tuner);
+       }
+       return ret;
+}
+
+static struct usb_device_id af9015_usb_table[] = {
+/*  0 */{USB_DEVICE(USB_VID_AFATECH,   USB_PID_AFATECH_AF9015_9015)},
+       {USB_DEVICE(USB_VID_AFATECH,   USB_PID_AFATECH_AF9015_9016)},
+       {USB_DEVICE(USB_VID_LEADTEK,   USB_PID_WINFAST_DTV_DONGLE_GOLD)},
+       {USB_DEVICE(USB_VID_PINNACLE,  USB_PID_PINNACLE_PCTV71E)},
+       {USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_KWORLD_399U)},
+/*  5 */{USB_DEVICE(USB_VID_VISIONPLUS,
+               USB_PID_TINYTWIN)},
+       {USB_DEVICE(USB_VID_VISIONPLUS,
+               USB_PID_AZUREWAVE_AD_TU700)},
+       {USB_DEVICE(USB_VID_TERRATEC,  USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2)},
+       {USB_DEVICE(USB_VID_KWORLD_2,  USB_PID_KWORLD_PC160_2T)},
+       {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X)},
+/* 10 */{USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)},
+       {USB_DEVICE(USB_VID_MSI_2,     USB_PID_MSI_DIGIVOX_DUO)},
+       {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)},
+       {0},
+};
+MODULE_DEVICE_TABLE(usb, af9015_usb_table);
+
+static struct dvb_usb_device_properties af9015_properties[] = {
+       {
+               .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+               .usb_ctrl = DEVICE_SPECIFIC,
+               .download_firmware = af9015_download_firmware,
+               .firmware = "dvb-usb-af9015.fw",
+
+               .size_of_priv = sizeof(struct af9015_state), \
+
+               .num_adapters = 2,
+               .adapter = {
+                       {
+                               .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+                               DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+                               .pid_filter_count = 32,
+                               .pid_filter       = af9015_pid_filter,
+                               .pid_filter_ctrl  = af9015_pid_filter_ctrl,
+
+                               .frontend_attach =
+                                       af9015_af9013_frontend_attach,
+                               .tuner_attach    = af9015_tuner_attach,
+                               .stream = {
+                                       .type = USB_BULK,
+                                       .count = 6,
+                                       .endpoint = 0x84,
+                               },
+                       },
+                       {
+                               .frontend_attach =
+                                       af9015_af9013_frontend_attach,
+                               .tuner_attach    = af9015_tuner_attach,
+                               .stream = {
+                                       .type = USB_BULK,
+                                       .count = 6,
+                                       .endpoint = 0x85,
+                               },
+                       }
+               },
+
+               .identify_state = af9015_identify_state,
+
+               .rc_query         = af9015_rc_query,
+               .rc_interval      = 150,
+
+               .i2c_algo = &af9015_i2c_algo,
+
+               .num_device_descs = 9,
+               .devices = {
+                       {
+                               .name = "Afatech AF9015 DVB-T USB2.0 stick",
+                               .cold_ids = {&af9015_usb_table[0],
+                                            &af9015_usb_table[1], NULL},
+                               .warm_ids = {NULL},
+                       },
+                       {
+                               .name = "Leadtek WinFast DTV Dongle Gold",
+                               .cold_ids = {&af9015_usb_table[2], NULL},
+                               .warm_ids = {NULL},
+                       },
+                       {
+                               .name = "Pinnacle PCTV 71e",
+                               .cold_ids = {&af9015_usb_table[3], NULL},
+                               .warm_ids = {NULL},
+                       },
+                       {
+                               .name = "KWorld PlusTV Dual DVB-T Stick " \
+                                       "(DVB-T 399U)",
+                               .cold_ids = {&af9015_usb_table[4], NULL},
+                               .warm_ids = {NULL},
+                       },
+                       {
+                               .name = "DigitalNow TinyTwin DVB-T Receiver",
+                               .cold_ids = {&af9015_usb_table[5], NULL},
+                               .warm_ids = {NULL},
+                       },
+                       {
+                               .name = "TwinHan AzureWave AD-TU700(704J)",
+                               .cold_ids = {&af9015_usb_table[6], NULL},
+                               .warm_ids = {NULL},
+                       },
+                       {
+                               .name = "TerraTec Cinergy T USB XE",
+                               .cold_ids = {&af9015_usb_table[7], NULL},
+                               .warm_ids = {NULL},
+                       },
+                       {
+                               .name = "KWorld PlusTV Dual DVB-T PCI " \
+                                       "(DVB-T PC160-2T)",
+                               .cold_ids = {&af9015_usb_table[8], NULL},
+                               .warm_ids = {NULL},
+                       },
+                       {
+                               .name = "AVerMedia AVerTV DVB-T Volar X",
+                               .cold_ids = {&af9015_usb_table[9], NULL},
+                               .warm_ids = {NULL},
+                       },
+               }
+       }, {
+               .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+               .usb_ctrl = DEVICE_SPECIFIC,
+               .download_firmware = af9015_download_firmware,
+               .firmware = "dvb-usb-af9015.fw",
+
+               .size_of_priv = sizeof(struct af9015_state), \
+
+               .num_adapters = 2,
+               .adapter = {
+                       {
+                               .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+                               DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+                               .pid_filter_count = 32,
+                               .pid_filter       = af9015_pid_filter,
+                               .pid_filter_ctrl  = af9015_pid_filter_ctrl,
+
+                               .frontend_attach =
+                                       af9015_af9013_frontend_attach,
+                               .tuner_attach    = af9015_tuner_attach,
+                               .stream = {
+                                       .type = USB_BULK,
+                                       .count = 6,
+                                       .endpoint = 0x84,
+                               },
+                       },
+                       {
+                               .frontend_attach =
+                                       af9015_af9013_frontend_attach,
+                               .tuner_attach    = af9015_tuner_attach,
+                               .stream = {
+                                       .type = USB_BULK,
+                                       .count = 6,
+                                       .endpoint = 0x85,
+                               },
+                       }
+               },
+
+               .identify_state = af9015_identify_state,
+
+               .rc_query         = af9015_rc_query,
+               .rc_interval      = 150,
+
+               .i2c_algo = &af9015_i2c_algo,
+
+               .num_device_descs = 3,
+               .devices = {
+                       {
+                               .name = "Xtensions XD-380",
+                               .cold_ids = {&af9015_usb_table[10], NULL},
+                               .warm_ids = {NULL},
+                       },
+                       {
+                               .name = "MSI DIGIVOX Duo",
+                               .cold_ids = {&af9015_usb_table[11], NULL},
+                               .warm_ids = {NULL},
+                       },
+                       {
+                               .name = "Fujitsu-Siemens Slim Mobile USB DVB-T",
+                               .cold_ids = {&af9015_usb_table[12], NULL},
+                               .warm_ids = {NULL},
+                       },
+               }
+       }
+};
+#undef AF9015_DEFAULT_PROPERTIES
+
+static int af9015_usb_probe(struct usb_interface *intf,
+                           const struct usb_device_id *id)
+{
+       int ret = 0;
+       struct dvb_usb_device *d = NULL;
+       struct usb_device *udev = interface_to_usbdev(intf);
+       u8 i;
+
+       deb_info("%s: interface:%d\n", __func__,
+               intf->cur_altsetting->desc.bInterfaceNumber);
+
+       /* interface 0 is used by DVB-T receiver and
+          interface 1 is for remote controller (HID) */
+       if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
+               ret = af9015_read_config(udev);
+               if (ret)
+                       return ret;
+
+               for (i = 0; i < af9015_properties_count; i++) {
+                       ret = dvb_usb_device_init(intf, &af9015_properties[i],
+                               THIS_MODULE, &d, adapter_nr);
+                       if (!ret)
+                               break;
+                       if (ret != -ENODEV)
+                               return ret;
+               }
+               if (ret)
+                       return ret;
+
+               if (d)
+                       ret = af9015_init(d);
+       }
+
+       return ret;
+}
+
+void af9015_i2c_exit(struct dvb_usb_device *d)
+{
+       struct af9015_state *state = d->priv;
+       deb_info("%s: \n", __func__);
+
+       /* remove 2nd I2C adapter */
+       if (d->state & DVB_USB_STATE_I2C)
+               i2c_del_adapter(&state->i2c_adap);
+}
+
+static void af9015_usb_device_exit(struct usb_interface *intf)
+{
+       struct dvb_usb_device *d = usb_get_intfdata(intf);
+       deb_info("%s: \n", __func__);
+
+       /* remove 2nd I2C adapter */
+       if (d != NULL && d->desc != NULL)
+               af9015_i2c_exit(d);
+
+       dvb_usb_device_exit(intf);
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver af9015_usb_driver = {
+       .name = "dvb_usb_af9015",
+       .probe = af9015_usb_probe,
+       .disconnect = af9015_usb_device_exit,
+       .id_table = af9015_usb_table,
+};
+
+/* module stuff */
+static int __init af9015_usb_module_init(void)
+{
+       int ret;
+       ret = usb_register(&af9015_usb_driver);
+       if (ret)
+               err("module init failed:%d", ret);
+
+       return ret;
+}
+
+static void __exit af9015_usb_module_exit(void)
+{
+       /* deregister this driver from the USB subsystem */
+       usb_deregister(&af9015_usb_driver);
+}
+
+module_init(af9015_usb_module_init);
+module_exit(af9015_usb_module_exit);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Driver for Afatech AF9015 DVB-T");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
new file mode 100644 (file)
index 0000000..882e8a4
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ * DVB USB Linux driver for Afatech AF9015 DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * Thanks to Afatech who kindly provided information.
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _DVB_USB_AF9015_H_
+#define _DVB_USB_AF9015_H_
+
+#define DVB_USB_LOG_PREFIX "af9015"
+#include "dvb-usb.h"
+
+extern int dvb_usb_af9015_debug;
+#define deb_info(args...) dprintk(dvb_usb_af9015_debug, 0x01, args)
+#define deb_rc(args...)   dprintk(dvb_usb_af9015_debug, 0x02, args)
+#define deb_xfer(args...) dprintk(dvb_usb_af9015_debug, 0x04, args)
+#define deb_reg(args...)  dprintk(dvb_usb_af9015_debug, 0x08, args)
+#define deb_i2c(args...)  dprintk(dvb_usb_af9015_debug, 0x10, args)
+#define deb_fw(args...)   dprintk(dvb_usb_af9015_debug, 0x20, args)
+
+#define AF9015_I2C_EEPROM  0xa0
+#define AF9015_I2C_DEMOD   0x38
+#define AF9015_USB_TIMEOUT 2000
+
+/* EEPROM locations */
+#define AF9015_EEPROM_IR_MODE        0x18
+#define AF9015_EEPROM_IR_REMOTE_TYPE 0x34
+#define AF9015_EEPROM_TS_MODE        0x31
+#define AF9015_EEPROM_DEMOD2_I2C     0x32
+
+#define AF9015_EEPROM_SAW_BW1        0x35
+#define AF9015_EEPROM_XTAL_TYPE1     0x36
+#define AF9015_EEPROM_SPEC_INV1      0x37
+#define AF9015_EEPROM_IF1L           0x38
+#define AF9015_EEPROM_IF1H           0x39
+#define AF9015_EEPROM_MT2060_IF1L    0x3a
+#define AF9015_EEPROM_MT2060_IF1H    0x3b
+#define AF9015_EEPROM_TUNER_ID1      0x3c
+
+#define AF9015_EEPROM_SAW_BW2        0x45
+#define AF9015_EEPROM_XTAL_TYPE2     0x46
+#define AF9015_EEPROM_SPEC_INV2      0x47
+#define AF9015_EEPROM_IF2L           0x48
+#define AF9015_EEPROM_IF2H           0x49
+#define AF9015_EEPROM_MT2060_IF2L    0x4a
+#define AF9015_EEPROM_MT2060_IF2H    0x4b
+#define AF9015_EEPROM_TUNER_ID2      0x4c
+
+#define AF9015_EEPROM_OFFSET (AF9015_EEPROM_SAW_BW2 - AF9015_EEPROM_SAW_BW1)
+
+#define AF9015_GPIO_ON (1 << 0)
+#define AF9015_GPIO_EN (1 << 1)
+#define AF9015_GPIO_O  (1 << 2)
+#define AF9015_GPIO_I  (1 << 3)
+
+#define AF9015_GPIO_TUNER_ON  (AF9015_GPIO_ON|AF9015_GPIO_EN)
+#define AF9015_GPIO_TUNER_OFF (AF9015_GPIO_ON|AF9015_GPIO_EN|AF9015_GPIO_O)
+
+struct req_t {
+       u8  cmd;       /* [0] */
+       /*  seq */     /* [1] */
+       u8  i2c_addr;  /* [2] */
+       u16 addr;      /* [3|4] */
+       u8  mbox;      /* [5] */
+       u8  addr_len;  /* [6] */
+       u8  data_len;  /* [7] */
+       u8  *data;
+};
+
+enum af9015_cmd {
+       GET_CONFIG           = 0x10,
+       DOWNLOAD_FIRMWARE    = 0x11,
+       BOOT                 = 0x13,
+       READ_MEMORY          = 0x20,
+       WRITE_MEMORY         = 0x21,
+       READ_WRITE_I2C       = 0x22,
+       COPY_FIRMWARE        = 0x23,
+       RECONNECT_USB        = 0x5a,
+       WRITE_VIRTUAL_MEMORY = 0x26,
+       GET_IR_CODE          = 0x27,
+       READ_I2C,
+       WRITE_I2C,
+};
+
+enum af9015_ir_mode {
+       AF9015_IR_MODE_DISABLED = 0,
+       AF9015_IR_MODE_HID,
+       AF9015_IR_MODE_RLC,
+       AF9015_IR_MODE_RC6,
+};
+
+struct af9015_state {
+       struct i2c_adapter i2c_adap; /* I2C adapter for 2nd FE */
+};
+
+struct af9015_config {
+       u8  dual_mode:1;
+       u16 mt2060_if1[2];
+       u16 firmware_size;
+       u16 firmware_checksum;
+       u8  *ir_table;
+       u16 ir_table_size;
+};
+
+enum af9015_remote {
+       AF9015_REMOTE_NONE                    = 0,
+       AF9015_REMOTE_A_LINK_DTU_M,
+       AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
+       AF9015_REMOTE_MYGICTV_U718,
+};
+
+/* Leadtek WinFast DTV Dongle Gold */
+static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = {
+       { 0x00, 0x1e, KEY_1 },
+       { 0x00, 0x1f, KEY_2 },
+       { 0x00, 0x20, KEY_3 },
+       { 0x00, 0x21, KEY_4 },
+       { 0x00, 0x22, KEY_5 },
+       { 0x00, 0x23, KEY_6 },
+       { 0x00, 0x24, KEY_7 },
+       { 0x00, 0x25, KEY_8 },
+       { 0x00, 0x26, KEY_9 },
+       { 0x00, 0x27, KEY_0 },
+       { 0x00, 0x28, KEY_ENTER },
+       { 0x00, 0x4f, KEY_VOLUMEUP },
+       { 0x00, 0x50, KEY_VOLUMEDOWN },
+       { 0x00, 0x51, KEY_CHANNELDOWN },
+       { 0x00, 0x52, KEY_CHANNELUP },
+};
+
+static u8 af9015_ir_table_leadtek[] = {
+       0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00,
+       0x03, 0xfc, 0x56, 0xa9, 0x00, 0x00, 0x00,
+       0x03, 0xfc, 0x4b, 0xb4, 0x00, 0x00, 0x00,
+       0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00,
+       0x03, 0xfc, 0x4d, 0xb2, 0x00, 0x00, 0x00,
+       0x03, 0xfc, 0x4e, 0xb1, 0x00, 0x00, 0x00,
+       0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00,
+       0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00,
+       0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00,
+       0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00,
+       0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00,
+       0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00,
+       0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00,
+       0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00,
+       0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00,
+       0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00,
+       0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00,
+       0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00,
+       0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00,
+       0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00,
+       0x03, 0xfc, 0x43, 0xbc, 0x00, 0x00, 0x00,
+       0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00,
+       0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00,
+       0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00,
+       0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00,
+       0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00,
+       0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00,
+       0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00,
+       0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00,
+       0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00,
+       0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00,
+       0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00,
+       0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00,
+       0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00,
+       0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00,
+       0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00,
+       0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00,
+       0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00,
+       0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00,
+       0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00,
+       0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00,
+       0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00,
+       0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00,
+       0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00,
+       0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00,
+       0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00,
+       0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00,
+       0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00,
+       0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00,
+       0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00,
+};
+
+/* TwinHan AzureWave AD-TU700(704J) */
+static struct dvb_usb_rc_key af9015_rc_keys_twinhan[] = {
+       { 0x05, 0x3f, KEY_POWER },
+       { 0x00, 0x19, KEY_FAVORITES },    /* Favorite List */
+       { 0x00, 0x04, KEY_TEXT },         /* Teletext */
+       { 0x00, 0x0e, KEY_POWER },
+       { 0x00, 0x0e, KEY_INFO },         /* Preview */
+       { 0x00, 0x08, KEY_EPG },          /* Info/EPG */
+       { 0x00, 0x0f, KEY_LIST },         /* Record List */
+       { 0x00, 0x1e, KEY_1 },
+       { 0x00, 0x1f, KEY_2 },
+       { 0x00, 0x20, KEY_3 },
+       { 0x00, 0x21, KEY_4 },
+       { 0x00, 0x22, KEY_5 },
+       { 0x00, 0x23, KEY_6 },
+       { 0x00, 0x24, KEY_7 },
+       { 0x00, 0x25, KEY_8 },
+       { 0x00, 0x26, KEY_9 },
+       { 0x00, 0x27, KEY_0 },
+       { 0x00, 0x29, KEY_CANCEL },       /* Cancel */
+       { 0x00, 0x4c, KEY_CLEAR },        /* Clear */
+       { 0x00, 0x2a, KEY_BACK },         /* Back */
+       { 0x00, 0x2b, KEY_TAB },          /* Tab */
+       { 0x00, 0x52, KEY_UP },           /* up arrow */
+       { 0x00, 0x51, KEY_DOWN },         /* down arrow */
+       { 0x00, 0x4f, KEY_RIGHT },        /* right arrow */
+       { 0x00, 0x50, KEY_LEFT },         /* left arrow */
+       { 0x00, 0x28, KEY_ENTER },        /* Enter / ok */
+       { 0x02, 0x52, KEY_VOLUMEUP },
+       { 0x02, 0x51, KEY_VOLUMEDOWN },
+       { 0x00, 0x4e, KEY_CHANNELDOWN },
+       { 0x00, 0x4b, KEY_CHANNELUP },
+       { 0x00, 0x4a, KEY_RECORD },
+       { 0x01, 0x11, KEY_PLAY },
+       { 0x00, 0x17, KEY_PAUSE },
+       { 0x00, 0x0c, KEY_REWIND },       /* FR << */
+       { 0x00, 0x11, KEY_FASTFORWARD },  /* FF >> */
+       { 0x01, 0x15, KEY_PREVIOUS },     /* Replay */
+       { 0x01, 0x0e, KEY_NEXT },         /* Skip */
+       { 0x00, 0x13, KEY_CAMERA },       /* Capture */
+       { 0x01, 0x0f, KEY_LANGUAGE },     /* SAP */
+       { 0x01, 0x13, KEY_TV2 },          /* PIP */
+       { 0x00, 0x1d, KEY_ZOOM },         /* Full Screen */
+       { 0x01, 0x17, KEY_SUBTITLE },     /* Subtitle / CC */
+       { 0x00, 0x10, KEY_MUTE },
+       { 0x01, 0x19, KEY_AUDIO },        /* L/R */ /* TODO better event */
+       { 0x01, 0x16, KEY_SLEEP },        /* Hibernate */
+       { 0x01, 0x16, KEY_SWITCHVIDEOMODE },
+                                         /* A/V */ /* TODO does not work */
+       { 0x00, 0x06, KEY_AGAIN },        /* Recall */
+       { 0x01, 0x16, KEY_KPPLUS },       /* Zoom+ */ /* TODO does not work */
+       { 0x01, 0x16, KEY_KPMINUS },      /* Zoom- */ /* TODO does not work */
+       { 0x02, 0x15, KEY_RED },
+       { 0x02, 0x0a, KEY_GREEN },
+       { 0x02, 0x1c, KEY_YELLOW },
+       { 0x02, 0x05, KEY_BLUE },
+};
+
+static u8 af9015_ir_table_twinhan[] = {
+       0x00, 0xff, 0x16, 0xe9, 0x3f, 0x05, 0x00,
+       0x00, 0xff, 0x07, 0xf8, 0x16, 0x01, 0x00,
+       0x00, 0xff, 0x14, 0xeb, 0x11, 0x01, 0x00,
+       0x00, 0xff, 0x1a, 0xe5, 0x4d, 0x00, 0x00,
+       0x00, 0xff, 0x4c, 0xb3, 0x17, 0x00, 0x00,
+       0x00, 0xff, 0x12, 0xed, 0x11, 0x00, 0x00,
+       0x00, 0xff, 0x40, 0xbf, 0x0c, 0x00, 0x00,
+       0x00, 0xff, 0x11, 0xee, 0x4a, 0x00, 0x00,
+       0x00, 0xff, 0x54, 0xab, 0x13, 0x00, 0x00,
+       0x00, 0xff, 0x41, 0xbe, 0x15, 0x01, 0x00,
+       0x00, 0xff, 0x42, 0xbd, 0x0e, 0x01, 0x00,
+       0x00, 0xff, 0x43, 0xbc, 0x17, 0x01, 0x00,
+       0x00, 0xff, 0x50, 0xaf, 0x0f, 0x01, 0x00,
+       0x00, 0xff, 0x4d, 0xb2, 0x1d, 0x00, 0x00,
+       0x00, 0xff, 0x47, 0xb8, 0x13, 0x01, 0x00,
+       0x00, 0xff, 0x05, 0xfa, 0x4b, 0x00, 0x00,
+       0x00, 0xff, 0x02, 0xfd, 0x4e, 0x00, 0x00,
+       0x00, 0xff, 0x0e, 0xf1, 0x06, 0x00, 0x00,
+       0x00, 0xff, 0x1e, 0xe1, 0x52, 0x02, 0x00,
+       0x00, 0xff, 0x0a, 0xf5, 0x51, 0x02, 0x00,
+       0x00, 0xff, 0x10, 0xef, 0x10, 0x00, 0x00,
+       0x00, 0xff, 0x49, 0xb6, 0x19, 0x01, 0x00,
+       0x00, 0xff, 0x15, 0xea, 0x27, 0x00, 0x00,
+       0x00, 0xff, 0x03, 0xfc, 0x1e, 0x00, 0x00,
+       0x00, 0xff, 0x01, 0xfe, 0x1f, 0x00, 0x00,
+       0x00, 0xff, 0x06, 0xf9, 0x20, 0x00, 0x00,
+       0x00, 0xff, 0x09, 0xf6, 0x21, 0x00, 0x00,
+       0x00, 0xff, 0x1d, 0xe2, 0x22, 0x00, 0x00,
+       0x00, 0xff, 0x1f, 0xe0, 0x23, 0x00, 0x00,
+       0x00, 0xff, 0x0d, 0xf2, 0x24, 0x00, 0x00,
+       0x00, 0xff, 0x19, 0xe6, 0x25, 0x00, 0x00,
+       0x00, 0xff, 0x1b, 0xe4, 0x26, 0x00, 0x00,
+       0x00, 0xff, 0x00, 0xff, 0x2b, 0x00, 0x00,
+       0x00, 0xff, 0x4a, 0xb5, 0x4c, 0x00, 0x00,
+       0x00, 0xff, 0x4b, 0xb4, 0x52, 0x00, 0x00,
+       0x00, 0xff, 0x51, 0xae, 0x51, 0x00, 0x00,
+       0x00, 0xff, 0x52, 0xad, 0x4f, 0x00, 0x00,
+       0x00, 0xff, 0x4e, 0xb1, 0x50, 0x00, 0x00,
+       0x00, 0xff, 0x0c, 0xf3, 0x29, 0x00, 0x00,
+       0x00, 0xff, 0x4f, 0xb0, 0x28, 0x00, 0x00,
+       0x00, 0xff, 0x13, 0xec, 0x2a, 0x00, 0x00,
+       0x00, 0xff, 0x17, 0xe8, 0x19, 0x00, 0x00,
+       0x00, 0xff, 0x04, 0xfb, 0x0f, 0x00, 0x00,
+       0x00, 0xff, 0x48, 0xb7, 0x0e, 0x00, 0x00,
+       0x00, 0xff, 0x0f, 0xf0, 0x04, 0x00, 0x00,
+       0x00, 0xff, 0x1c, 0xe3, 0x08, 0x00, 0x00,
+       0x00, 0xff, 0x18, 0xe7, 0x15, 0x02, 0x00,
+       0x00, 0xff, 0x53, 0xac, 0x0a, 0x02, 0x00,
+       0x00, 0xff, 0x5e, 0xa1, 0x1c, 0x02, 0x00,
+       0x00, 0xff, 0x5f, 0xa0, 0x05, 0x02, 0x00,
+};
+
+/* A-Link DTU(m) */
+static struct dvb_usb_rc_key af9015_rc_keys_a_link[] = {
+       { 0x00, 0x1e, KEY_1 },
+       { 0x00, 0x1f, KEY_2 },
+       { 0x00, 0x20, KEY_3 },
+       { 0x00, 0x21, KEY_4 },
+       { 0x00, 0x22, KEY_5 },
+       { 0x00, 0x23, KEY_6 },
+       { 0x00, 0x24, KEY_7 },
+       { 0x00, 0x25, KEY_8 },
+       { 0x00, 0x26, KEY_9 },
+       { 0x00, 0x27, KEY_0 },
+       { 0x00, 0x2e, KEY_CHANNELUP },
+       { 0x00, 0x2d, KEY_CHANNELDOWN },
+       { 0x04, 0x28, KEY_ZOOM },
+       { 0x00, 0x41, KEY_MUTE },
+       { 0x00, 0x42, KEY_VOLUMEDOWN },
+       { 0x00, 0x43, KEY_VOLUMEUP },
+       { 0x00, 0x44, KEY_GOTO },         /* jump */
+       { 0x05, 0x45, KEY_POWER },
+};
+
+static u8 af9015_ir_table_a_link[] = {
+       0x08, 0xf7, 0x12, 0xed, 0x45, 0x05, 0x00, /* power */
+       0x08, 0xf7, 0x1a, 0xe5, 0x41, 0x00, 0x00, /* mute */
+       0x08, 0xf7, 0x01, 0xfe, 0x1e, 0x00, 0x00, /* 1 */
+       0x08, 0xf7, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */
+       0x08, 0xf7, 0x03, 0xfc, 0x24, 0x00, 0x00, /* 7 */
+       0x08, 0xf7, 0x05, 0xfa, 0x28, 0x04, 0x00, /* zoom */
+       0x08, 0xf7, 0x00, 0xff, 0x43, 0x00, 0x00, /* volume up */
+       0x08, 0xf7, 0x16, 0xe9, 0x42, 0x00, 0x00, /* volume down */
+       0x08, 0xf7, 0x0f, 0xf0, 0x1f, 0x00, 0x00, /* 2 */
+       0x08, 0xf7, 0x0d, 0xf2, 0x22, 0x00, 0x00, /* 5 */
+       0x08, 0xf7, 0x1b, 0xe4, 0x25, 0x00, 0x00, /* 8 */
+       0x08, 0xf7, 0x06, 0xf9, 0x27, 0x00, 0x00, /* 0 */
+       0x08, 0xf7, 0x14, 0xeb, 0x2e, 0x00, 0x00, /* channel up */
+       0x08, 0xf7, 0x1d, 0xe2, 0x2d, 0x00, 0x00, /* channel down */
+       0x08, 0xf7, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */
+       0x08, 0xf7, 0x18, 0xe7, 0x23, 0x00, 0x00, /* 6 */
+       0x08, 0xf7, 0x04, 0xfb, 0x26, 0x00, 0x00, /* 9 */
+       0x08, 0xf7, 0x07, 0xf8, 0x44, 0x00, 0x00, /* jump */
+};
+
+/* MSI DIGIVOX mini II V3.0 */
+static struct dvb_usb_rc_key af9015_rc_keys_msi[] = {
+       { 0x00, 0x1e, KEY_1 },
+       { 0x00, 0x1f, KEY_2 },
+       { 0x00, 0x20, KEY_3 },
+       { 0x00, 0x21, KEY_4 },
+       { 0x00, 0x22, KEY_5 },
+       { 0x00, 0x23, KEY_6 },
+       { 0x00, 0x24, KEY_7 },
+       { 0x00, 0x25, KEY_8 },
+       { 0x00, 0x26, KEY_9 },
+       { 0x00, 0x27, KEY_0 },
+       { 0x03, 0x0f, KEY_CHANNELUP },
+       { 0x03, 0x0e, KEY_CHANNELDOWN },
+       { 0x00, 0x42, KEY_VOLUMEDOWN },
+       { 0x00, 0x43, KEY_VOLUMEUP },
+       { 0x05, 0x45, KEY_POWER },
+       { 0x00, 0x52, KEY_UP },           /* up */
+       { 0x00, 0x51, KEY_DOWN },         /* down */
+       { 0x00, 0x28, KEY_ENTER },
+};
+
+static u8 af9015_ir_table_msi[] = {
+       0x03, 0xfc, 0x17, 0xe8, 0x45, 0x05, 0x00, /* power */
+       0x03, 0xfc, 0x0d, 0xf2, 0x51, 0x00, 0x00, /* down */
+       0x03, 0xfc, 0x03, 0xfc, 0x52, 0x00, 0x00, /* up */
+       0x03, 0xfc, 0x1a, 0xe5, 0x1e, 0x00, 0x00, /* 1 */
+       0x03, 0xfc, 0x02, 0xfd, 0x1f, 0x00, 0x00, /* 2 */
+       0x03, 0xfc, 0x04, 0xfb, 0x20, 0x00, 0x00, /* 3 */
+       0x03, 0xfc, 0x1c, 0xe3, 0x21, 0x00, 0x00, /* 4 */
+       0x03, 0xfc, 0x08, 0xf7, 0x22, 0x00, 0x00, /* 5 */
+       0x03, 0xfc, 0x1d, 0xe2, 0x23, 0x00, 0x00, /* 6 */
+       0x03, 0xfc, 0x11, 0xee, 0x24, 0x00, 0x00, /* 7 */
+       0x03, 0xfc, 0x0b, 0xf4, 0x25, 0x00, 0x00, /* 8 */
+       0x03, 0xfc, 0x10, 0xef, 0x26, 0x00, 0x00, /* 9 */
+       0x03, 0xfc, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */
+       0x03, 0xfc, 0x14, 0xeb, 0x43, 0x00, 0x00, /* volume up */
+       0x03, 0xfc, 0x1f, 0xe0, 0x42, 0x00, 0x00, /* volume down */
+       0x03, 0xfc, 0x15, 0xea, 0x0f, 0x03, 0x00, /* channel up */
+       0x03, 0xfc, 0x05, 0xfa, 0x0e, 0x03, 0x00, /* channel down */
+       0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, /* enter */
+};
+
+/* MYGICTV U718 */
+static struct dvb_usb_rc_key af9015_rc_keys_mygictv[] = {
+       { 0x00, 0x3d, KEY_SWITCHVIDEOMODE },
+                                         /* TV / AV */
+       { 0x05, 0x45, KEY_POWER },
+       { 0x00, 0x1e, KEY_1 },
+       { 0x00, 0x1f, KEY_2 },
+       { 0x00, 0x20, KEY_3 },
+       { 0x00, 0x21, KEY_4 },
+       { 0x00, 0x22, KEY_5 },
+       { 0x00, 0x23, KEY_6 },
+       { 0x00, 0x24, KEY_7 },
+       { 0x00, 0x25, KEY_8 },
+       { 0x00, 0x26, KEY_9 },
+       { 0x00, 0x27, KEY_0 },
+       { 0x00, 0x41, KEY_MUTE },
+       { 0x00, 0x2a, KEY_ESC },          /* Esc */
+       { 0x00, 0x2e, KEY_CHANNELUP },
+       { 0x00, 0x2d, KEY_CHANNELDOWN },
+       { 0x00, 0x42, KEY_VOLUMEDOWN },
+       { 0x00, 0x43, KEY_VOLUMEUP },
+       { 0x00, 0x52, KEY_UP },           /* up arrow */
+       { 0x00, 0x51, KEY_DOWN },         /* down arrow */
+       { 0x00, 0x4f, KEY_RIGHT },        /* right arrow */
+       { 0x00, 0x50, KEY_LEFT },         /* left arrow */
+       { 0x00, 0x28, KEY_ENTER },        /* ok */
+       { 0x01, 0x15, KEY_RECORD },
+       { 0x03, 0x13, KEY_PLAY },
+       { 0x01, 0x13, KEY_PAUSE },
+       { 0x01, 0x16, KEY_STOP },
+       { 0x03, 0x07, KEY_REWIND },       /* FR << */
+       { 0x03, 0x09, KEY_FASTFORWARD },  /* FF >> */
+       { 0x00, 0x3b, KEY_TIME },         /* TimeShift */
+       { 0x00, 0x3e, KEY_CAMERA },       /* Snapshot */
+       { 0x03, 0x16, KEY_CYCLEWINDOWS }, /* yellow, min / max */
+       { 0x00, 0x00, KEY_ZOOM },         /* 'select' (?) */
+       { 0x03, 0x16, KEY_SHUFFLE },      /* Shuffle */
+       { 0x03, 0x45, KEY_POWER },
+};
+
+static u8 af9015_ir_table_mygictv[] = {
+       0x02, 0xbd, 0x0c, 0xf3, 0x3d, 0x00, 0x00, /* TV / AV */
+       0x02, 0xbd, 0x14, 0xeb, 0x45, 0x05, 0x00, /* power */
+       0x02, 0xbd, 0x00, 0xff, 0x1e, 0x00, 0x00, /* 1 */
+       0x02, 0xbd, 0x01, 0xfe, 0x1f, 0x00, 0x00, /* 2 */
+       0x02, 0xbd, 0x02, 0xfd, 0x20, 0x00, 0x00, /* 3 */
+       0x02, 0xbd, 0x03, 0xfc, 0x21, 0x00, 0x00, /* 4 */
+       0x02, 0xbd, 0x04, 0xfb, 0x22, 0x00, 0x00, /* 5 */
+       0x02, 0xbd, 0x05, 0xfa, 0x23, 0x00, 0x00, /* 6 */
+       0x02, 0xbd, 0x06, 0xf9, 0x24, 0x00, 0x00, /* 7 */
+       0x02, 0xbd, 0x07, 0xf8, 0x25, 0x00, 0x00, /* 8 */
+       0x02, 0xbd, 0x08, 0xf7, 0x26, 0x00, 0x00, /* 9 */
+       0x02, 0xbd, 0x09, 0xf6, 0x27, 0x00, 0x00, /* 0 */
+       0x02, 0xbd, 0x0a, 0xf5, 0x41, 0x00, 0x00, /* mute */
+       0x02, 0xbd, 0x1c, 0xe3, 0x2a, 0x00, 0x00, /* esc */
+       0x02, 0xbd, 0x1f, 0xe0, 0x43, 0x00, 0x00, /* volume up */
+       0x02, 0xbd, 0x12, 0xed, 0x52, 0x00, 0x00, /* up arrow */
+       0x02, 0xbd, 0x11, 0xee, 0x50, 0x00, 0x00, /* left arrow */
+       0x02, 0xbd, 0x15, 0xea, 0x28, 0x00, 0x00, /* ok */
+       0x02, 0xbd, 0x10, 0xef, 0x4f, 0x00, 0x00, /* right arrow */
+       0x02, 0xbd, 0x13, 0xec, 0x51, 0x00, 0x00, /* down arrow */
+       0x02, 0xbd, 0x0e, 0xf1, 0x42, 0x00, 0x00, /* volume down */
+       0x02, 0xbd, 0x19, 0xe6, 0x15, 0x01, 0x00, /* record */
+       0x02, 0xbd, 0x1e, 0xe1, 0x13, 0x03, 0x00, /* play */
+       0x02, 0xbd, 0x16, 0xe9, 0x16, 0x01, 0x00, /* stop */
+       0x02, 0xbd, 0x0b, 0xf4, 0x28, 0x04, 0x00, /* yellow, min / max */
+       0x02, 0xbd, 0x0f, 0xf0, 0x3b, 0x00, 0x00, /* time shift */
+       0x02, 0xbd, 0x18, 0xe7, 0x2e, 0x00, 0x00, /* channel up */
+       0x02, 0xbd, 0x1a, 0xe5, 0x2d, 0x00, 0x00, /* channel down */
+       0x02, 0xbd, 0x17, 0xe8, 0x3e, 0x00, 0x00, /* snapshot */
+       0x02, 0xbd, 0x40, 0xbf, 0x13, 0x01, 0x00, /* pause */
+       0x02, 0xbd, 0x41, 0xbe, 0x09, 0x03, 0x00, /* FF >> */
+       0x02, 0xbd, 0x42, 0xbd, 0x07, 0x03, 0x00, /* FR << */
+       0x02, 0xbd, 0x43, 0xbc, 0x00, 0x00, 0x00, /* 'select' (?) */
+       0x02, 0xbd, 0x44, 0xbb, 0x16, 0x03, 0x00, /* shuffle */
+       0x02, 0xbd, 0x45, 0xba, 0x45, 0x03, 0x00, /* power */
+};
+
+/* KWorld PlusTV Dual DVB-T Stick (DVB-T 399U) */
+static u8 af9015_ir_table_kworld[] = {
+       0x86, 0x6b, 0x0c, 0xf3, 0x2e, 0x07, 0x00,
+       0x86, 0x6b, 0x16, 0xe9, 0x2d, 0x07, 0x00,
+       0x86, 0x6b, 0x1d, 0xe2, 0x37, 0x07, 0x00,
+       0x86, 0x6b, 0x00, 0xff, 0x1e, 0x07, 0x00,
+       0x86, 0x6b, 0x01, 0xfe, 0x1f, 0x07, 0x00,
+       0x86, 0x6b, 0x02, 0xfd, 0x20, 0x07, 0x00,
+       0x86, 0x6b, 0x03, 0xfc, 0x21, 0x07, 0x00,
+       0x86, 0x6b, 0x04, 0xfb, 0x22, 0x07, 0x00,
+       0x86, 0x6b, 0x05, 0xfa, 0x23, 0x07, 0x00,
+       0x86, 0x6b, 0x06, 0xf9, 0x24, 0x07, 0x00,
+       0x86, 0x6b, 0x07, 0xf8, 0x25, 0x07, 0x00,
+       0x86, 0x6b, 0x08, 0xf7, 0x26, 0x07, 0x00,
+       0x86, 0x6b, 0x09, 0xf6, 0x4d, 0x07, 0x00,
+       0x86, 0x6b, 0x0a, 0xf5, 0x4e, 0x07, 0x00,
+       0x86, 0x6b, 0x14, 0xeb, 0x4f, 0x07, 0x00,
+       0x86, 0x6b, 0x1e, 0xe1, 0x50, 0x07, 0x00,
+       0x86, 0x6b, 0x17, 0xe8, 0x52, 0x07, 0x00,
+       0x86, 0x6b, 0x1f, 0xe0, 0x51, 0x07, 0x00,
+       0x86, 0x6b, 0x0e, 0xf1, 0x0b, 0x07, 0x00,
+       0x86, 0x6b, 0x20, 0xdf, 0x0c, 0x07, 0x00,
+       0x86, 0x6b, 0x42, 0xbd, 0x0d, 0x07, 0x00,
+       0x86, 0x6b, 0x0b, 0xf4, 0x0e, 0x07, 0x00,
+       0x86, 0x6b, 0x43, 0xbc, 0x0f, 0x07, 0x00,
+       0x86, 0x6b, 0x10, 0xef, 0x10, 0x07, 0x00,
+       0x86, 0x6b, 0x21, 0xde, 0x11, 0x07, 0x00,
+       0x86, 0x6b, 0x13, 0xec, 0x12, 0x07, 0x00,
+       0x86, 0x6b, 0x11, 0xee, 0x13, 0x07, 0x00,
+       0x86, 0x6b, 0x12, 0xed, 0x14, 0x07, 0x00,
+       0x86, 0x6b, 0x19, 0xe6, 0x15, 0x07, 0x00,
+       0x86, 0x6b, 0x1a, 0xe5, 0x16, 0x07, 0x00,
+       0x86, 0x6b, 0x1b, 0xe4, 0x17, 0x07, 0x00,
+       0x86, 0x6b, 0x4b, 0xb4, 0x18, 0x07, 0x00,
+       0x86, 0x6b, 0x40, 0xbf, 0x19, 0x07, 0x00,
+       0x86, 0x6b, 0x44, 0xbb, 0x1a, 0x07, 0x00,
+       0x86, 0x6b, 0x41, 0xbe, 0x1b, 0x07, 0x00,
+       0x86, 0x6b, 0x22, 0xdd, 0x1c, 0x07, 0x00,
+       0x86, 0x6b, 0x15, 0xea, 0x1d, 0x07, 0x00,
+       0x86, 0x6b, 0x0f, 0xf0, 0x3f, 0x07, 0x00,
+       0x86, 0x6b, 0x1c, 0xe3, 0x40, 0x07, 0x00,
+       0x86, 0x6b, 0x4a, 0xb5, 0x41, 0x07, 0x00,
+       0x86, 0x6b, 0x48, 0xb7, 0x42, 0x07, 0x00,
+       0x86, 0x6b, 0x49, 0xb6, 0x43, 0x07, 0x00,
+       0x86, 0x6b, 0x18, 0xe7, 0x44, 0x07, 0x00,
+       0x86, 0x6b, 0x23, 0xdc, 0x45, 0x07, 0x00,
+};
+
+#endif
index 81369b79e75a203609603e2b9ac77410ec38d7da..2558f2b51c2bafb7a15efd57728349cd28f62b2e 100644 (file)
 #define USB_VID_HAUPPAUGE                      0x2040
 #define USB_VID_HYPER_PALTEK                   0x1025
 #define USB_VID_KWORLD                         0xeb2a
+#define USB_VID_KWORLD_2                       0x1b80
 #define USB_VID_KYE                            0x0458
 #define USB_VID_LEADTEK                                0x0413
 #define USB_VID_LITEON                         0x04ca
 #define USB_VID_MEDION                         0x1660
 #define USB_VID_MIGLIA                         0x18f3
 #define USB_VID_MSI                            0x0db0
+#define USB_VID_MSI_2                          0x1462
 #define USB_VID_OPERA1                         0x695c
 #define USB_VID_PINNACLE                       0x2304
 #define USB_VID_TECHNOTREND                    0x0b48
 #define USB_VID_WIDEVIEW                       0x14aa
 #define USB_VID_GIGABYTE                       0x1044
 #define USB_VID_YUAN                           0x1164
-
+#define USB_VID_XTENSIONS                      0x1ae7
 
 /* Product IDs */
 #define USB_PID_ADSTECH_USB2_COLD                      0xa333
 #define USB_PID_ADSTECH_USB2_WARM                      0xa334
 #define USB_PID_AFATECH_AF9005                         0x9020
+#define USB_PID_AFATECH_AF9015_9015                    0x9015
+#define USB_PID_AFATECH_AF9015_9016                    0x9016
 #define USB_VID_ALINK_DTU                              0xf170
 #define USB_PID_ANSONIC_DVBT_USB                       0x6000
 #define USB_PID_ANYSEE                                 0x861f
+#define USB_PID_AZUREWAVE_AD_TU700                     0x3237
 #define USB_PID_AVERMEDIA_DVBT_USB_COLD                        0x0001
 #define USB_PID_AVERMEDIA_DVBT_USB_WARM                        0x0002
 #define USB_PID_AVERMEDIA_DVBT_USB2_COLD               0xa800
 #define USB_PID_UNIWILL_STK7700P                       0x6003
 #define USB_PID_GRANDTEC_DVBT_USB_COLD                 0x0fa0
 #define USB_PID_GRANDTEC_DVBT_USB_WARM                 0x0fa1
+#define USB_PID_KWORLD_399U                            0xe399
+#define USB_PID_KWORLD_PC160_2T                                0xc160
 #define USB_PID_KWORLD_VSTREAM_COLD                    0x17de
 #define USB_PID_KWORLD_VSTREAM_WARM                    0x17df
 #define USB_PID_TERRATEC_CINERGY_T_USB_XE              0x0055
+#define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2         0x0069
 #define USB_PID_TWINHAN_VP7041_COLD                    0x3201
 #define USB_PID_TWINHAN_VP7041_WARM                    0x3202
 #define USB_PID_TWINHAN_VP7020_COLD                    0x3203
 #define USB_PID_TWINHAN_VP7045_WARM                    0x3206
 #define USB_PID_TWINHAN_VP7021_COLD                    0x3207
 #define USB_PID_TWINHAN_VP7021_WARM                    0x3208
+#define USB_PID_TINYTWIN                               0x3226
 #define USB_PID_DNTV_TINYUSB2_COLD                     0x3223
 #define USB_PID_DNTV_TINYUSB2_WARM                     0x3224
 #define USB_PID_ULTIMA_TVBOX_COLD                      0x8105
 #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R       0x0039
 #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_ATSC  0x1039
 #define USB_PID_AVERMEDIA_HYBRID_ULTRA_USB_M039R_DVBT  0x2039
+#define USB_PID_AVERMEDIA_VOLAR_X                      0xa815
+#define USB_PID_AVERMEDIA_VOLAR_X_2                    0x8150
 #define USB_PID_TECHNOTREND_CONNECT_S2400               0x3006
 #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY       0x005a
 #define USB_PID_TERRATEC_CINERGY_HT_USB_XE             0x0058
 #define USB_PID_PINNACLE_PCTV2000E                     0x022c
 #define USB_PID_PINNACLE_PCTV_DVB_T_FLASH              0x0228
 #define USB_PID_PINNACLE_PCTV_DUAL_DIVERSITY_DVB_T     0x0229
+#define USB_PID_PINNACLE_PCTV71E                       0x022b
 #define USB_PID_PINNACLE_PCTV72E                       0x0236
 #define USB_PID_PINNACLE_PCTV73E                       0x0237
 #define USB_PID_PCTV_200E                              0x020e
 #define USB_PID_WINFAST_DTV_DONGLE_WARM                        0x6026
 #define USB_PID_WINFAST_DTV_DONGLE_STK7700P            0x6f00
 #define USB_PID_WINFAST_DTV_DONGLE_STK7700P_2          0x6f01
+#define USB_PID_WINFAST_DTV_DONGLE_GOLD                        0x6029
 #define USB_PID_GENPIX_8PSK_REV_1_COLD                 0x0200
 #define USB_PID_GENPIX_8PSK_REV_1_WARM                 0x0201
 #define USB_PID_GENPIX_8PSK_REV_2                      0x0202
 #define USB_PID_GENPIX_SKYWALKER_CW3K                  0x0204
 #define USB_PID_SIGMATEK_DVB_110                       0x6610
 #define USB_PID_MSI_DIGI_VOX_MINI_II                   0x1513
+#define USB_PID_MSI_DIGIVOX_DUO                                0x8801
 #define USB_PID_OPERA1_COLD                            0x2830
 #define USB_PID_OPERA1_WARM                            0x3829
 #define USB_PID_LIFEVIEW_TV_WALKER_TWIN_COLD           0x0514
 #define USB_PID_ASUS_U3100                             0x173f
 #define USB_PID_YUAN_EC372S                            0x1edc
 #define USB_PID_DW2102                                 0x2102
+#define USB_PID_XTENSIONS_XD_380                       0x0381
 
 #endif