summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick Boettcher <pb@linuxtv.org>2005-09-09 13:02:41 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2005-09-09 13:57:40 -0700
commit3706a4da2012679631da6d22e86c6a34cde7419a (patch)
tree45bf457db581f3b30d9ba49e1f4f1ad7f99f894c
parent80e27e20619902b11aa255081fd83eab10fc0839 (diff)
[PATCH] dvb: usb: add TwinhanDTV StarBox support
Add driver for the TwinhanDTV StarBox and clones. Thanks to Ralph Metzler for his initial work on this box and thanks to Twinhan for their support. Signed-off-by: Patrick Boettcher <pb@linuxtv.org> Signed-off-by: Johannes Stezenbach <js@linuxtv.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig23
-rw-r--r--drivers/media/dvb/dvb-usb/Makefile3
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h6
-rw-r--r--drivers/media/dvb/dvb-usb/vp702x-fe.c339
-rw-r--r--drivers/media/dvb/dvb-usb/vp702x.c290
-rw-r--r--drivers/media/dvb/dvb-usb/vp702x.h109
6 files changed, 765 insertions, 5 deletions
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 612e5b087b1..54e2b29076b 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -93,13 +93,30 @@ config DVB_USB_DIGITV
Say Y here to support the Nebula Electronics uDigitV USB2.0 DVB-T receiver.
config DVB_USB_VP7045
- tristate "TwinhanDTV Alpha/MagicBoxII and DNTV tinyUSB2 DVB-T USB2.0 support"
+ tristate "TwinhanDTV Alpha/MagicBoxII, DNTV tinyUSB2, Beetle USB2.0 support"
depends on DVB_USB
help
Say Y here to support the
+
TwinhanDTV Alpha (stick) (VP-7045),
- TwinhanDTV MagicBox II (VP-7046) and
- DigitalNow TinyUSB 2 DVB-t DVB-T USB2.0 receivers.
+ TwinhanDTV MagicBox II (VP-7046),
+ DigitalNow TinyUSB 2 DVB-t,
+ DigitalRise USB 2.0 Ter (Beetle) and
+ TYPHOON DVB-T USB DRIVE
+
+ DVB-T USB2.0 receivers.
+
+config DVB_USB_VP702X
+ tristate "TwinhanDTV StarBox and clones DVB-S USB2.0 support"
+ depends on DVB_USB
+ help
+ Say Y here to support the
+
+ TwinhanDTV StarBox,
+ DigitalRise USB Starbox and
+ TYPHOON DVB-S USB 2.0 BOX
+
+ DVB-S USB2.0 receivers.
config DVB_USB_NOVA_T_USB2
tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support"
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index 746d87ed6f3..2dc9aad9681 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -4,6 +4,9 @@ obj-$(CONFIG_DVB_USB) += dvb-usb.o
dvb-usb-vp7045-objs = vp7045.o vp7045-fe.o
obj-$(CONFIG_DVB_USB_VP7045) += dvb-usb-vp7045.o
+dvb-usb-vp702x-objs = vp702x.o vp702x-fe.o
+obj-$(CONFIG_DVB_USB_VP702X) += dvb-usb-vp702x.o
+
dvb-usb-dtt200u-objs = dtt200u.o dtt200u-fe.o
obj-$(CONFIG_DVB_USB_DTT200U) += dvb-usb-dtt200u.o
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 794d513a848..2b249c75fb0 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -52,12 +52,14 @@
#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
#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_VP7020_WARM 0x3204
#define USB_PID_TWINHAN_VP7045_COLD 0x3205
#define USB_PID_TWINHAN_VP7045_WARM 0x3206
-#define USB_PID_DNTV_TINYUSB2_COLD 0x3223
-#define USB_PID_DNTV_TINYUSB2_WARM 0x3224
#define USB_PID_TWINHAN_VP7021_COLD 0x3207
#define USB_PID_TWINHAN_VP7021_WARM 0x3208
+#define USB_PID_DNTV_TINYUSB2_COLD 0x3223
+#define USB_PID_DNTV_TINYUSB2_WARM 0x3224
#define USB_PID_ULTIMA_TVBOX_COLD 0x8105
#define USB_PID_ULTIMA_TVBOX_WARM 0x8106
#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107
diff --git a/drivers/media/dvb/dvb-usb/vp702x-fe.c b/drivers/media/dvb/dvb-usb/vp702x-fe.c
new file mode 100644
index 00000000000..f20d8dbd0be
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/vp702x-fe.c
@@ -0,0 +1,339 @@
+/* DVB frontend part of the Linux driver for the TwinhanDTV StarBox USB2.0
+ * DVB-S receiver.
+ *
+ * Copyright (C) 2005 Ralph Metzler <rjkm@metzlerbros.de>
+ * Metzler Brothers Systementwicklung GbR
+ *
+ * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@desy.de>
+ *
+ * Thanks to Twinhan who kindly provided hardware and information.
+ *
+ * This file can be removed soon, after the DST-driver is rewritten to provice
+ * the frontend-controlling separately.
+ *
+ * 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, version 2.
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ *
+ */
+#include "vp702x.h"
+
+struct vp702x_fe_state {
+ struct dvb_frontend fe;
+ struct dvb_usb_device *d;
+
+ fe_sec_voltage_t voltage;
+ fe_sec_tone_mode_t tone_mode;
+
+ u8 lnb_buf[8];
+
+ u8 lock;
+ u8 sig;
+ u8 snr;
+
+ unsigned long next_status_check;
+ unsigned long status_check_interval;
+};
+
+static int vp702x_fe_refresh_state(struct vp702x_fe_state *st)
+{
+ u8 buf[10];
+ if (time_after(jiffies,st->next_status_check)) {
+ vp702x_usb_in_op(st->d,READ_STATUS,0,0,buf,10);
+
+ st->lock = buf[4];
+ vp702x_usb_in_op(st->d,READ_TUNER_REG_REQ,0x11,0,&st->snr,1);
+ vp702x_usb_in_op(st->d,READ_TUNER_REG_REQ,0x15,0,&st->sig,1);
+
+ st->next_status_check = jiffies + (st->status_check_interval*HZ)/1000;
+ }
+ return 0;
+}
+
+static u8 vp702x_chksum(u8 *buf,int f, int count)
+{
+ u8 s = 0;
+ int i;
+ for (i = f; i < f+count; i++)
+ s += buf[i];
+ return ~s+1;
+}
+
+static int vp702x_fe_read_status(struct dvb_frontend* fe, fe_status_t *status)
+{
+ struct vp702x_fe_state *st = fe->demodulator_priv;
+ vp702x_fe_refresh_state(st);
+ deb_fe("%s\n",__FUNCTION__);
+
+ if (st->lock == 0)
+ *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_SIGNAL | FE_HAS_CARRIER;
+ else
+ *status = 0;
+
+ deb_fe("real state: %x\n",*status);
+ *status = 0x1f;
+
+ if (*status & FE_HAS_LOCK)
+ st->status_check_interval = 1000;
+ else
+ st->status_check_interval = 250;
+ return 0;
+}
+
+/* not supported by this Frontend */
+static int vp702x_fe_read_ber(struct dvb_frontend* fe, u32 *ber)
+{
+ struct vp702x_fe_state *st = fe->demodulator_priv;
+ vp702x_fe_refresh_state(st);
+ *ber = 0;
+ return 0;
+}
+
+/* not supported by this Frontend */
+static int vp702x_fe_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
+{
+ struct vp702x_fe_state *st = fe->demodulator_priv;
+ vp702x_fe_refresh_state(st);
+ *unc = 0;
+ return 0;
+}
+
+static int vp702x_fe_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
+{
+ struct vp702x_fe_state *st = fe->demodulator_priv;
+ vp702x_fe_refresh_state(st);
+
+ *strength = (st->sig << 8) | st->sig;
+ return 0;
+}
+
+static int vp702x_fe_read_snr(struct dvb_frontend* fe, u16 *snr)
+{
+ u8 _snr;
+ struct vp702x_fe_state *st = fe->demodulator_priv;
+ vp702x_fe_refresh_state(st);
+
+ _snr = (st->snr & 0x1f) * 0xff / 0x1f;
+ *snr = (_snr << 8) | _snr;
+ return 0;
+}
+
+static int vp702x_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+{
+ deb_fe("%s\n",__FUNCTION__);
+ tune->min_delay_ms = 2000;
+ return 0;
+}
+
+static int vp702x_fe_set_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct vp702x_fe_state *st = fe->demodulator_priv;
+ u32 freq = fep->frequency/1000;
+ /*CalFrequency*/
+/* u16 frequencyRef[16] = { 2, 4, 8, 16, 32, 64, 128, 256, 24, 5, 10, 20, 40, 80, 160, 320 }; */
+ u64 sr;
+ u8 cmd[8] = { 0 },ibuf[10];
+
+ cmd[0] = (freq >> 8) & 0x7f;
+ cmd[1] = freq & 0xff;
+ cmd[2] = 1; /* divrate == 4 -> frequencyRef[1] -> 1 here */
+
+ sr = (u64) (fep->u.qpsk.symbol_rate/1000) << 20;
+ do_div(sr,88000);
+ cmd[3] = (sr >> 12) & 0xff;
+ cmd[4] = (sr >> 4) & 0xff;
+ cmd[5] = (sr << 4) & 0xf0;
+
+ deb_fe("setting frontend to: %u -> %u (%x) LNB-based GHz, symbolrate: %d -> %Lu (%Lx)\n",
+ fep->frequency,freq,freq, fep->u.qpsk.symbol_rate, sr, sr);
+
+/* if (fep->inversion == INVERSION_ON)
+ cmd[6] |= 0x80; */
+
+ if (st->voltage == SEC_VOLTAGE_18)
+ cmd[6] |= 0x40;
+
+/* if (fep->u.qpsk.symbol_rate > 8000000)
+ cmd[6] |= 0x20;
+
+ if (fep->frequency < 1531000)
+ cmd[6] |= 0x04;
+
+ if (st->tone_mode == SEC_TONE_ON)
+ cmd[6] |= 0x01;*/
+
+ cmd[7] = vp702x_chksum(cmd,0,7);
+
+ st->status_check_interval = 250;
+ st->next_status_check = jiffies;
+
+ vp702x_usb_in_op(st->d, RESET_TUNER, 0, 0, NULL, 0);
+ msleep(30);
+ vp702x_usb_inout_op(st->d,cmd,8,ibuf,10,100);
+
+ if (ibuf[2] == 0 && ibuf[3] == 0)
+ deb_fe("tuning failed.\n");
+ else
+ deb_fe("tuning succeeded.\n");
+
+ return 0;
+}
+
+static int vp702x_fe_get_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ deb_fe("%s\n",__FUNCTION__);
+ return 0;
+}
+
+static int vp702x_fe_send_diseqc_msg (struct dvb_frontend* fe,
+ struct dvb_diseqc_master_cmd *m)
+{
+ struct vp702x_fe_state *st = fe->demodulator_priv;
+ u8 cmd[8],ibuf[10];
+ memset(cmd,0,8);
+
+ deb_fe("%s\n",__FUNCTION__);
+
+ if (m->msg_len > 4)
+ return -EINVAL;
+
+ cmd[1] = SET_DISEQC_CMD;
+ cmd[2] = m->msg_len;
+ memcpy(&cmd[3], m->msg, m->msg_len);
+ cmd[7] = vp702x_chksum(cmd,0,7);
+
+ vp702x_usb_inout_op(st->d,cmd,8,ibuf,10,100);
+
+ if (ibuf[2] == 0 && ibuf[3] == 0)
+ deb_fe("diseqc cmd failed.\n");
+ else
+ deb_fe("diseqc cmd succeeded.\n");
+
+ return 0;
+}
+
+static int vp702x_fe_send_diseqc_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t burst)
+{
+ deb_fe("%s\n",__FUNCTION__);
+ return 0;
+}
+
+static int vp702x_fe_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+ struct vp702x_fe_state *st = fe->demodulator_priv;
+ u8 ibuf[10];
+ deb_fe("%s\n",__FUNCTION__);
+
+ st->tone_mode = tone;
+
+ if (tone == SEC_TONE_ON)
+ st->lnb_buf[2] = 0x02;
+ else
+ st->lnb_buf[2] = 0x00;
+
+ st->lnb_buf[7] = vp702x_chksum(st->lnb_buf,0,7);
+
+ vp702x_usb_inout_op(st->d,st->lnb_buf,8,ibuf,10,100);
+ if (ibuf[2] == 0 && ibuf[3] == 0)
+ deb_fe("set_tone cmd failed.\n");
+ else
+ deb_fe("set_tone cmd succeeded.\n");
+
+ return 0;
+}
+
+static int vp702x_fe_set_voltage (struct dvb_frontend* fe, fe_sec_voltage_t
+ voltage)
+{
+ struct vp702x_fe_state *st = fe->demodulator_priv;
+ u8 ibuf[10];
+ deb_fe("%s\n",__FUNCTION__);
+
+ st->voltage = voltage;
+
+ if (voltage != SEC_VOLTAGE_OFF)
+ st->lnb_buf[4] = 0x01;
+ else
+ st->lnb_buf[4] = 0x00;
+
+ st->lnb_buf[7] = vp702x_chksum(st->lnb_buf,0,7);
+
+ vp702x_usb_inout_op(st->d,st->lnb_buf,8,ibuf,10,100);
+ if (ibuf[2] == 0 && ibuf[3] == 0)
+ deb_fe("set_voltage cmd failed.\n");
+ else
+ deb_fe("set_voltage cmd succeeded.\n");
+
+ return 0;
+}
+
+static void vp702x_fe_release(struct dvb_frontend* fe)
+{
+ struct vp702x_fe_state *st = fe->demodulator_priv;
+ kfree(st);
+}
+
+static struct dvb_frontend_ops vp702x_fe_ops;
+
+struct dvb_frontend * vp702x_fe_attach(struct dvb_usb_device *d)
+{
+ struct vp702x_fe_state *s = kmalloc(sizeof(struct vp702x_fe_state), GFP_KERNEL);
+ if (s == NULL)
+ goto error;
+ memset(s,0,sizeof(struct vp702x_fe_state));
+
+ s->d = d;
+ s->fe.ops = &vp702x_fe_ops;
+ s->fe.demodulator_priv = s;
+
+ s->lnb_buf[1] = SET_LNB_POWER;
+ s->lnb_buf[3] = 0xff; /* 0=tone burst, 2=data burst, ff=off */
+
+ goto success;
+error:
+ return NULL;
+success:
+ return &s->fe;
+}
+
+
+static struct dvb_frontend_ops vp702x_fe_ops = {
+ .info = {
+ .name = "Twinhan DST-like frontend (VP7021/VP7020) DVB-S",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 1000, /* kHz for QPSK frontends */
+ .frequency_tolerance = 0,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .symbol_rate_tolerance = 500, /* ppm */
+ .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_QPSK |
+ FE_CAN_FEC_AUTO
+ },
+ .release = vp702x_fe_release,
+
+ .init = NULL,
+ .sleep = NULL,
+
+ .set_frontend = vp702x_fe_set_frontend,
+ .get_frontend = vp702x_fe_get_frontend,
+ .get_tune_settings = vp702x_fe_get_tune_settings,
+
+ .read_status = vp702x_fe_read_status,
+ .read_ber = vp702x_fe_read_ber,
+ .read_signal_strength = vp702x_fe_read_signal_strength,
+ .read_snr = vp702x_fe_read_snr,
+ .read_ucblocks = vp702x_fe_read_unc_blocks,
+
+ .diseqc_send_master_cmd = vp702x_fe_send_diseqc_msg,
+ .diseqc_send_burst = vp702x_fe_send_diseqc_burst,
+ .set_tone = vp702x_fe_set_tone,
+ .set_voltage = vp702x_fe_set_voltage,
+};
diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c
new file mode 100644
index 00000000000..1f5034ca115
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/vp702x.c
@@ -0,0 +1,290 @@
+/* DVB USB compliant Linux driver for the TwinhanDTV StarBox USB2.0 DVB-S
+ * receiver.
+ *
+ * Copyright (C) 2005 Ralph Metzler <rjkm@metzlerbros.de>
+ * Metzler Brothers Systementwicklung GbR
+ *
+ * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@desy.de>
+ *
+ * Thanks to Twinhan who kindly provided hardware and 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, version 2.
+ *
+ * see Documentation/dvb/README.dvb-usb for more information
+ */
+#include "vp702x.h"
+
+/* debug */
+int dvb_usb_vp702x_debug;
+module_param_named(debug,dvb_usb_vp702x_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS);
+
+struct vp702x_state {
+ u8 pid_table[17]; /* [16] controls the pid_table state */
+};
+
+/* check for mutex FIXME */
+int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen)
+{
+ int ret = 0,try = 0;
+
+ while (ret >= 0 && ret != blen && try < 3) {
+ ret = usb_control_msg(d->udev,
+ usb_rcvctrlpipe(d->udev,0),
+ req,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ value,index,b,blen,
+ 2000);
+ deb_info("reading number %d (ret: %d)\n",try,ret);
+ try++;
+ }
+
+ if (ret < 0 || ret != blen) {
+ warn("usb in operation failed.");
+ ret = -EIO;
+ } else
+ ret = 0;
+
+ deb_xfer("in: req. %x, val: %x, ind: %x, buffer: ",req,value,index);
+ debug_dump(b,blen,deb_xfer);
+
+ return ret;
+}
+
+int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen)
+{
+ deb_xfer("out: req. %x, val: %x, ind: %x, buffer: ",req,value,index);
+ debug_dump(b,blen,deb_xfer);
+
+ if (usb_control_msg(d->udev,
+ usb_sndctrlpipe(d->udev,0),
+ req,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ value,index,b,blen,
+ 2000) != blen) {
+ warn("usb out operation failed.");
+ return -EIO;
+ } else
+ return 0;
+}
+
+int vp702x_usb_inout_op(struct dvb_usb_device *d, u8 *o, int olen, u8 *i, int ilen, int msec)
+{
+ int ret;
+
+ if ((ret = down_interruptible(&d->usb_sem)))
+ return ret;
+
+ if ((ret = vp702x_usb_out_op(d,REQUEST_OUT,0,0,o,olen)) < 0)
+ goto unlock;
+ msleep(msec);
+ ret = vp702x_usb_in_op(d,REQUEST_IN,0,0,i,ilen);
+
+unlock:
+ up(&d->usb_sem);
+
+ return ret;
+}
+
+int vp702x_usb_inout_cmd(struct dvb_usb_device *d, u8 cmd, u8 *o, int olen, u8 *i, int ilen, int msec)
+{
+ u8 bout[olen+2];
+ u8 bin[ilen+1];
+ int ret = 0;
+
+ bout[0] = 0x00;
+ bout[1] = cmd;
+ memcpy(&bout[2],o,olen);
+
+ ret = vp702x_usb_inout_op(d, bout, olen+2, bin, ilen+1,msec);
+
+ if (ret == 0)
+ memcpy(i,&bin[1],ilen);
+
+ return ret;
+}
+
+static int vp702x_pid_filter(struct dvb_usb_device *d, int index, u16 pid, int onoff)
+{
+ struct vp702x_state *st = d->priv;
+ u8 buf[9];
+
+ if (onoff) {
+ st->pid_table[16] |= 1 << index;
+ st->pid_table[index*2] = (pid >> 8) & 0xff;
+ st->pid_table[index*2+1] = pid & 0xff;
+ } else {
+ st->pid_table[16] &= ~(1 << index);
+ st->pid_table[index*2] = st->pid_table[index*2+1] = 0;
+ }
+
+ return vp702x_usb_inout_cmd(d,SET_PID_FILTER,st->pid_table,17,buf,9,10);
+}
+
+static int vp702x_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ vp702x_usb_in_op(d,RESET_TUNER,0,0,NULL,0);
+
+ vp702x_usb_in_op(d,SET_TUNER_POWER_REQ,0,onoff,NULL,0);
+ return vp702x_usb_in_op(d,SET_TUNER_POWER_REQ,0,onoff,NULL,0);
+}
+
+/* keys for the enclosed remote control */
+static struct dvb_usb_rc_key vp702x_rc_keys[] = {
+ { 0x00, 0x01, KEY_1 },
+ { 0x00, 0x02, KEY_2 },
+};
+
+/* remote control stuff (does not work with my box) */
+static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
+{
+ u8 key[10];
+ int i;
+
+/* remove the following return to enabled remote querying */
+ return 0;
+
+ vp702x_usb_in_op(d,READ_REMOTE_REQ,0,0,key,10);
+
+ deb_rc("remote query key: %x %d\n",key[1],key[1]);
+
+ if (key[1] == 0x44) {
+ *state = REMOTE_NO_KEY_PRESSED;
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(vp702x_rc_keys); i++)
+ if (vp702x_rc_keys[i].custom == key[1]) {
+ *state = REMOTE_KEY_PRESSED;
+ *event = vp702x_rc_keys[i].event;
+ break;
+ }
+ return 0;
+}
+
+static int vp702x_read_mac_addr(struct dvb_usb_device *d,u8 mac[6])
+{
+ u8 macb[9];
+ if (vp702x_usb_inout_cmd(d, GET_MAC_ADDRESS, NULL, 0, macb, 9, 10))
+ return -EIO;
+ memcpy(mac,&macb[3],6);
+ return 0;
+}
+
+static int vp702x_frontend_attach(struct dvb_usb_device *d)
+{
+ u8 buf[9] = { 0 };
+
+ if (vp702x_usb_inout_cmd(d, GET_SYSTEM_STRING, NULL, 0, buf, 9, 10))
+ return -EIO;
+
+ buf[8] = '\0';
+ info("system string: %s",&buf[1]);
+
+ d->fe = vp702x_fe_attach(d);
+ return 0;
+}
+
+static struct dvb_usb_properties vp702x_properties;
+
+static int vp702x_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+
+ usb_clear_halt(udev,usb_sndctrlpipe(udev,0));
+ usb_clear_halt(udev,usb_rcvctrlpipe(udev,0));
+
+ return dvb_usb_device_init(intf,&vp702x_properties,THIS_MODULE);
+}
+
+static struct usb_device_id vp702x_usb_table [] = {
+ { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7021_COLD) },
+ { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7021_WARM) },
+ { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7020_COLD) },
+ { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7020_WARM) },
+ { 0 },
+};
+MODULE_DEVICE_TABLE(usb, vp702x_usb_table);
+
+static struct dvb_usb_properties vp702x_properties = {
+ .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_NEED_PID_FILTERING,
+ .pid_filter_count = 8, /* !!! */
+
+ .usb_ctrl = CYPRESS_FX2,
+ .firmware = "dvb-usb-vp702x-01.fw",
+
+ .pid_filter = vp702x_pid_filter,
+ .power_ctrl = vp702x_power_ctrl,
+ .frontend_attach = vp702x_frontend_attach,
+ .read_mac_address = vp702x_read_mac_addr,
+
+ .rc_key_map = vp702x_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(vp702x_rc_keys),
+ .rc_interval = 400,
+ .rc_query = vp702x_rc_query,
+
+ .size_of_priv = sizeof(struct vp702x_state),
+
+ /* parameter for the MPEG2-data transfer */
+ .urb = {
+ .type = DVB_USB_BULK,
+ .count = 7,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
+
+ .num_device_descs = 2,
+ .devices = {
+ { .name = "TwinhanDTV StarBox DVB-S USB2.0 (VP7021)",
+ .cold_ids = { &vp702x_usb_table[0], NULL },
+ .warm_ids = { &vp702x_usb_table[1], NULL },
+ },
+ { .name = "TwinhanDTV StarBox DVB-S USB2.0 (VP7020)",
+ .cold_ids = { &vp702x_usb_table[2], NULL },
+ .warm_ids = { &vp702x_usb_table[3], NULL },
+ },
+ { 0 },
+ }
+};
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver vp702x_usb_driver = {
+ .owner = THIS_MODULE,
+ .name = "dvb-usb-vp702x",
+ .probe = vp702x_usb_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = vp702x_usb_table,
+};
+
+/* module stuff */
+static int __init vp702x_usb_module_init(void)
+{
+ int result;
+ if ((result = usb_register(&vp702x_usb_driver))) {
+ err("usb_register failed. (%d)",result);
+ return result;
+ }
+
+ return 0;
+}
+
+static void __exit vp702x_usb_module_exit(void)
+{
+ /* deregister this driver from the USB subsystem */
+ usb_deregister(&vp702x_usb_driver);
+}
+
+module_init(vp702x_usb_module_init);
+module_exit(vp702x_usb_module_exit);
+
+MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
+MODULE_DESCRIPTION("Driver for Twinhan StarBox DVB-S USB2.0 and clones");
+MODULE_VERSION("1.0-alpha");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/vp702x.h b/drivers/media/dvb/dvb-usb/vp702x.h
new file mode 100644
index 00000000000..4a3e8c7eca2
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/vp702x.h
@@ -0,0 +1,109 @@
+#ifndef _DVB_USB_VP7021_H_
+#define _DVB_USB_VP7021_H_
+
+#define DVB_USB_LOG_PREFIX "vp702x"
+#include "dvb-usb.h"
+
+extern int dvb_usb_vp702x_debug;
+#define deb_info(args...) dprintk(dvb_usb_vp702x_debug,0x01,args)
+#define deb_xfer(args...) dprintk(dvb_usb_vp702x_debug,0x02,args)
+#define deb_rc(args...) dprintk(dvb_usb_vp702x_debug,0x04,args)
+#define deb_fe(args...) dprintk(dvb_usb_vp702x_debug,0x08,args)
+
+/* commands are read and written with USB control messages */
+
+/* consecutive read/write operation */
+#define REQUEST_OUT 0xB2
+#define REQUEST_IN 0xB3
+
+/* the out-buffer of these consecutive operations contain sub-commands when b[0] = 0
+ * request: 0xB2; i: 0; v: 0; b[0] = 0, b[1] = subcmd, additional buffer
+ * the returning buffer looks as follows
+ * request: 0xB3; i: 0; v: 0; b[0] = 0xB3, additional buffer */
+
+#define GET_TUNER_STATUS 0x05
+/* additional in buffer:
+ * 0 1 2 3 4 5 6 7 8
+ * N/A N/A 0x05 signal-quality N/A N/A signal-strength lock==0 N/A */
+
+#define GET_SYSTEM_STRING 0x06
+/* additional in buffer:
+ * 0 1 2 3 4 5 6 7 8
+ * N/A 'U' 'S' 'B' '7' '0' '2' 'X' N/A */
+
+#define SET_DISEQC_CMD 0x08
+/* additional out buffer:
+ * 0 1 2 3 4
+ * len X1 X2 X3 X4
+ * additional in buffer:
+ * 0 1 2
+ * N/A 0 0 b[1] == b[2] == 0 -> success otherwise not */
+
+#define SET_LNB_POWER 0x09
+/* additional out buffer:
+ * 0 1 2
+ * 0x00 0xff 1 = on, 0 = off
+ * additional in buffer:
+ * 0 1 2
+ * N/A 0 0 b[1] == b[2] == 0 -> success otherwise not */
+
+#define GET_MAC_ADDRESS 0x0A
+/* #define GET_MAC_ADDRESS 0x0B */
+/* additional in buffer:
+ * 0 1 2 3 4 5 6 7 8
+ * N/A N/A 0x0A or 0x0B MAC0 MAC1 MAC2 MAC3 MAC4 MAC5 */
+
+#define SET_PID_FILTER 0x11
+/* additional in buffer:
+ * 0 1 ... 14 15 16
+ * PID0_MSB PID0_LSB ... PID7_MSB PID7_LSB PID_active (bits) */
+
+/* request: 0xB2; i: 0; v: 0;
+ * b[0] != 0 -> tune and lock a channel
+ * 0 1 2 3 4 5 6 7
+ * freq0 freq1 divstep srate0 srate1 srate2 flag chksum
+ */
+
+
+/* one direction requests */
+#define READ_REMOTE_REQ 0xB4
+/* IN i: 0; v: 0; b[0] == request, b[1] == key */
+
+#define READ_PID_NUMBER_REQ 0xB5
+/* IN i: 0; v: 0; b[0] == request, b[1] == 0, b[2] = pid number */
+
+#define WRITE_EEPROM_REQ 0xB6
+/* OUT i: offset; v: value to write; no extra buffer */
+
+#define READ_EEPROM_REQ 0xB7
+/* IN i: bufferlen; v: offset; buffer with bufferlen bytes */
+
+#define READ_STATUS 0xB8
+/* IN i: 0; v: 0; bufferlen 10 */
+
+#define READ_TUNER_REG_REQ 0xB9
+/* IN i: 0; v: register; b[0] = value */
+
+#define READ_FX2_REG_REQ 0xBA
+/* IN i: offset; v: 0; b[0] = value */
+
+#define WRITE_FX2_REG_REQ 0xBB
+/* OUT i: offset; v: value to write; 1 byte extra buffer */
+
+#define SET_TUNER_POWER_REQ 0xBC
+/* IN i: 0 = power off, 1 = power on */
+
+#define WRITE_TUNER_REG_REQ 0xBD
+/* IN i: register, v: value to write, no extra buffer */
+
+#define RESET_TUNER 0xBE
+/* IN i: 0, v: 0, no extra buffer */
+
+extern struct dvb_frontend * vp702x_fe_attach(struct dvb_usb_device *d);
+
+extern int vp702x_usb_inout_op(struct dvb_usb_device *d, u8 *o, int olen, u8 *i, int ilen, int msec);
+extern int vp702x_usb_inout_cmd(struct dvb_usb_device *d, u8 cmd, u8 *o, int olen, u8 *i, int ilen, int msec);
+extern int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen);
+extern int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen);
+
+#endif