diff options
Diffstat (limited to 'drivers/media/dvb/dvb-usb/vp702x.c')
-rw-r--r-- | drivers/media/dvb/dvb-usb/vp702x.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c new file mode 100644 index 000000000000..1f5034ca1152 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/vp702x.c | |||
@@ -0,0 +1,290 @@ | |||
1 | /* DVB USB compliant Linux driver for the TwinhanDTV StarBox USB2.0 DVB-S | ||
2 | * receiver. | ||
3 | * | ||
4 | * Copyright (C) 2005 Ralph Metzler <rjkm@metzlerbros.de> | ||
5 | * Metzler Brothers Systementwicklung GbR | ||
6 | * | ||
7 | * Copyright (C) 2005 Patrick Boettcher <patrick.boettcher@desy.de> | ||
8 | * | ||
9 | * Thanks to Twinhan who kindly provided hardware and information. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify it | ||
12 | * under the terms of the GNU General Public License as published by the Free | ||
13 | * Software Foundation, version 2. | ||
14 | * | ||
15 | * see Documentation/dvb/README.dvb-usb for more information | ||
16 | */ | ||
17 | #include "vp702x.h" | ||
18 | |||
19 | /* debug */ | ||
20 | int dvb_usb_vp702x_debug; | ||
21 | module_param_named(debug,dvb_usb_vp702x_debug, int, 0644); | ||
22 | MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS); | ||
23 | |||
24 | struct vp702x_state { | ||
25 | u8 pid_table[17]; /* [16] controls the pid_table state */ | ||
26 | }; | ||
27 | |||
28 | /* check for mutex FIXME */ | ||
29 | int vp702x_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen) | ||
30 | { | ||
31 | int ret = 0,try = 0; | ||
32 | |||
33 | while (ret >= 0 && ret != blen && try < 3) { | ||
34 | ret = usb_control_msg(d->udev, | ||
35 | usb_rcvctrlpipe(d->udev,0), | ||
36 | req, | ||
37 | USB_TYPE_VENDOR | USB_DIR_IN, | ||
38 | value,index,b,blen, | ||
39 | 2000); | ||
40 | deb_info("reading number %d (ret: %d)\n",try,ret); | ||
41 | try++; | ||
42 | } | ||
43 | |||
44 | if (ret < 0 || ret != blen) { | ||
45 | warn("usb in operation failed."); | ||
46 | ret = -EIO; | ||
47 | } else | ||
48 | ret = 0; | ||
49 | |||
50 | deb_xfer("in: req. %x, val: %x, ind: %x, buffer: ",req,value,index); | ||
51 | debug_dump(b,blen,deb_xfer); | ||
52 | |||
53 | return ret; | ||
54 | } | ||
55 | |||
56 | int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen) | ||
57 | { | ||
58 | deb_xfer("out: req. %x, val: %x, ind: %x, buffer: ",req,value,index); | ||
59 | debug_dump(b,blen,deb_xfer); | ||
60 | |||
61 | if (usb_control_msg(d->udev, | ||
62 | usb_sndctrlpipe(d->udev,0), | ||
63 | req, | ||
64 | USB_TYPE_VENDOR | USB_DIR_OUT, | ||
65 | value,index,b,blen, | ||
66 | 2000) != blen) { | ||
67 | warn("usb out operation failed."); | ||
68 | return -EIO; | ||
69 | } else | ||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | int vp702x_usb_inout_op(struct dvb_usb_device *d, u8 *o, int olen, u8 *i, int ilen, int msec) | ||
74 | { | ||
75 | int ret; | ||
76 | |||
77 | if ((ret = down_interruptible(&d->usb_sem))) | ||
78 | return ret; | ||
79 | |||
80 | if ((ret = vp702x_usb_out_op(d,REQUEST_OUT,0,0,o,olen)) < 0) | ||
81 | goto unlock; | ||
82 | msleep(msec); | ||
83 | ret = vp702x_usb_in_op(d,REQUEST_IN,0,0,i,ilen); | ||
84 | |||
85 | unlock: | ||
86 | up(&d->usb_sem); | ||
87 | |||
88 | return ret; | ||
89 | } | ||
90 | |||
91 | int vp702x_usb_inout_cmd(struct dvb_usb_device *d, u8 cmd, u8 *o, int olen, u8 *i, int ilen, int msec) | ||
92 | { | ||
93 | u8 bout[olen+2]; | ||
94 | u8 bin[ilen+1]; | ||
95 | int ret = 0; | ||
96 | |||
97 | bout[0] = 0x00; | ||
98 | bout[1] = cmd; | ||
99 | memcpy(&bout[2],o,olen); | ||
100 | |||
101 | ret = vp702x_usb_inout_op(d, bout, olen+2, bin, ilen+1,msec); | ||
102 | |||
103 | if (ret == 0) | ||
104 | memcpy(i,&bin[1],ilen); | ||
105 | |||
106 | return ret; | ||
107 | } | ||
108 | |||
109 | static int vp702x_pid_filter(struct dvb_usb_device *d, int index, u16 pid, int onoff) | ||
110 | { | ||
111 | struct vp702x_state *st = d->priv; | ||
112 | u8 buf[9]; | ||
113 | |||
114 | if (onoff) { | ||
115 | st->pid_table[16] |= 1 << index; | ||
116 | st->pid_table[index*2] = (pid >> 8) & 0xff; | ||
117 | st->pid_table[index*2+1] = pid & 0xff; | ||
118 | } else { | ||
119 | st->pid_table[16] &= ~(1 << index); | ||
120 | st->pid_table[index*2] = st->pid_table[index*2+1] = 0; | ||
121 | } | ||
122 | |||
123 | return vp702x_usb_inout_cmd(d,SET_PID_FILTER,st->pid_table,17,buf,9,10); | ||
124 | } | ||
125 | |||
126 | static int vp702x_power_ctrl(struct dvb_usb_device *d, int onoff) | ||
127 | { | ||
128 | vp702x_usb_in_op(d,RESET_TUNER,0,0,NULL,0); | ||
129 | |||
130 | vp702x_usb_in_op(d,SET_TUNER_POWER_REQ,0,onoff,NULL,0); | ||
131 | return vp702x_usb_in_op(d,SET_TUNER_POWER_REQ,0,onoff,NULL,0); | ||
132 | } | ||
133 | |||
134 | /* keys for the enclosed remote control */ | ||
135 | static struct dvb_usb_rc_key vp702x_rc_keys[] = { | ||
136 | { 0x00, 0x01, KEY_1 }, | ||
137 | { 0x00, 0x02, KEY_2 }, | ||
138 | }; | ||
139 | |||
140 | /* remote control stuff (does not work with my box) */ | ||
141 | static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) | ||
142 | { | ||
143 | u8 key[10]; | ||
144 | int i; | ||
145 | |||
146 | /* remove the following return to enabled remote querying */ | ||
147 | return 0; | ||
148 | |||
149 | vp702x_usb_in_op(d,READ_REMOTE_REQ,0,0,key,10); | ||
150 | |||
151 | deb_rc("remote query key: %x %d\n",key[1],key[1]); | ||
152 | |||
153 | if (key[1] == 0x44) { | ||
154 | *state = REMOTE_NO_KEY_PRESSED; | ||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | for (i = 0; i < ARRAY_SIZE(vp702x_rc_keys); i++) | ||
159 | if (vp702x_rc_keys[i].custom == key[1]) { | ||
160 | *state = REMOTE_KEY_PRESSED; | ||
161 | *event = vp702x_rc_keys[i].event; | ||
162 | break; | ||
163 | } | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static int vp702x_read_mac_addr(struct dvb_usb_device *d,u8 mac[6]) | ||
168 | { | ||
169 | u8 macb[9]; | ||
170 | if (vp702x_usb_inout_cmd(d, GET_MAC_ADDRESS, NULL, 0, macb, 9, 10)) | ||
171 | return -EIO; | ||
172 | memcpy(mac,&macb[3],6); | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static int vp702x_frontend_attach(struct dvb_usb_device *d) | ||
177 | { | ||
178 | u8 buf[9] = { 0 }; | ||
179 | |||
180 | if (vp702x_usb_inout_cmd(d, GET_SYSTEM_STRING, NULL, 0, buf, 9, 10)) | ||
181 | return -EIO; | ||
182 | |||
183 | buf[8] = '\0'; | ||
184 | info("system string: %s",&buf[1]); | ||
185 | |||
186 | d->fe = vp702x_fe_attach(d); | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static struct dvb_usb_properties vp702x_properties; | ||
191 | |||
192 | static int vp702x_usb_probe(struct usb_interface *intf, | ||
193 | const struct usb_device_id *id) | ||
194 | { | ||
195 | struct usb_device *udev = interface_to_usbdev(intf); | ||
196 | |||
197 | usb_clear_halt(udev,usb_sndctrlpipe(udev,0)); | ||
198 | usb_clear_halt(udev,usb_rcvctrlpipe(udev,0)); | ||
199 | |||
200 | return dvb_usb_device_init(intf,&vp702x_properties,THIS_MODULE); | ||
201 | } | ||
202 | |||
203 | static struct usb_device_id vp702x_usb_table [] = { | ||
204 | { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7021_COLD) }, | ||
205 | { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7021_WARM) }, | ||
206 | { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7020_COLD) }, | ||
207 | { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7020_WARM) }, | ||
208 | { 0 }, | ||
209 | }; | ||
210 | MODULE_DEVICE_TABLE(usb, vp702x_usb_table); | ||
211 | |||
212 | static struct dvb_usb_properties vp702x_properties = { | ||
213 | .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_NEED_PID_FILTERING, | ||
214 | .pid_filter_count = 8, /* !!! */ | ||
215 | |||
216 | .usb_ctrl = CYPRESS_FX2, | ||
217 | .firmware = "dvb-usb-vp702x-01.fw", | ||
218 | |||
219 | .pid_filter = vp702x_pid_filter, | ||
220 | .power_ctrl = vp702x_power_ctrl, | ||
221 | .frontend_attach = vp702x_frontend_attach, | ||
222 | .read_mac_address = vp702x_read_mac_addr, | ||
223 | |||
224 | .rc_key_map = vp702x_rc_keys, | ||
225 | .rc_key_map_size = ARRAY_SIZE(vp702x_rc_keys), | ||
226 | .rc_interval = 400, | ||
227 | .rc_query = vp702x_rc_query, | ||
228 | |||
229 | .size_of_priv = sizeof(struct vp702x_state), | ||
230 | |||
231 | /* parameter for the MPEG2-data transfer */ | ||
232 | .urb = { | ||
233 | .type = DVB_USB_BULK, | ||
234 | .count = 7, | ||
235 | .endpoint = 0x02, | ||
236 | .u = { | ||
237 | .bulk = { | ||
238 | .buffersize = 4096, | ||
239 | } | ||
240 | } | ||
241 | }, | ||
242 | |||
243 | .num_device_descs = 2, | ||
244 | .devices = { | ||
245 | { .name = "TwinhanDTV StarBox DVB-S USB2.0 (VP7021)", | ||
246 | .cold_ids = { &vp702x_usb_table[0], NULL }, | ||
247 | .warm_ids = { &vp702x_usb_table[1], NULL }, | ||
248 | }, | ||
249 | { .name = "TwinhanDTV StarBox DVB-S USB2.0 (VP7020)", | ||
250 | .cold_ids = { &vp702x_usb_table[2], NULL }, | ||
251 | .warm_ids = { &vp702x_usb_table[3], NULL }, | ||
252 | }, | ||
253 | { 0 }, | ||
254 | } | ||
255 | }; | ||
256 | |||
257 | /* usb specific object needed to register this driver with the usb subsystem */ | ||
258 | static struct usb_driver vp702x_usb_driver = { | ||
259 | .owner = THIS_MODULE, | ||
260 | .name = "dvb-usb-vp702x", | ||
261 | .probe = vp702x_usb_probe, | ||
262 | .disconnect = dvb_usb_device_exit, | ||
263 | .id_table = vp702x_usb_table, | ||
264 | }; | ||
265 | |||
266 | /* module stuff */ | ||
267 | static int __init vp702x_usb_module_init(void) | ||
268 | { | ||
269 | int result; | ||
270 | if ((result = usb_register(&vp702x_usb_driver))) { | ||
271 | err("usb_register failed. (%d)",result); | ||
272 | return result; | ||
273 | } | ||
274 | |||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | static void __exit vp702x_usb_module_exit(void) | ||
279 | { | ||
280 | /* deregister this driver from the USB subsystem */ | ||
281 | usb_deregister(&vp702x_usb_driver); | ||
282 | } | ||
283 | |||
284 | module_init(vp702x_usb_module_init); | ||
285 | module_exit(vp702x_usb_module_exit); | ||
286 | |||
287 | MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>"); | ||
288 | MODULE_DESCRIPTION("Driver for Twinhan StarBox DVB-S USB2.0 and clones"); | ||
289 | MODULE_VERSION("1.0-alpha"); | ||
290 | MODULE_LICENSE("GPL"); | ||