]>
Commit | Line | Data |
---|---|---|
3706a4da PB |
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 | ||
703cb2cb AB |
56 | static int vp702x_usb_out_op(struct dvb_usb_device *d, u8 req, u16 value, |
57 | u16 index, u8 *b, int blen) | |
3706a4da PB |
58 | { |
59 | deb_xfer("out: req. %x, val: %x, ind: %x, buffer: ",req,value,index); | |
60 | debug_dump(b,blen,deb_xfer); | |
61 | ||
62 | if (usb_control_msg(d->udev, | |
63 | usb_sndctrlpipe(d->udev,0), | |
64 | req, | |
65 | USB_TYPE_VENDOR | USB_DIR_OUT, | |
66 | value,index,b,blen, | |
67 | 2000) != blen) { | |
68 | warn("usb out operation failed."); | |
69 | return -EIO; | |
70 | } else | |
71 | return 0; | |
72 | } | |
73 | ||
74 | int vp702x_usb_inout_op(struct dvb_usb_device *d, u8 *o, int olen, u8 *i, int ilen, int msec) | |
75 | { | |
76 | int ret; | |
77 | ||
3593cab5 | 78 | if ((ret = mutex_lock_interruptible(&d->usb_mutex))) |
3706a4da PB |
79 | return ret; |
80 | ||
81 | if ((ret = vp702x_usb_out_op(d,REQUEST_OUT,0,0,o,olen)) < 0) | |
82 | goto unlock; | |
83 | msleep(msec); | |
84 | ret = vp702x_usb_in_op(d,REQUEST_IN,0,0,i,ilen); | |
85 | ||
86 | unlock: | |
3593cab5 | 87 | mutex_unlock(&d->usb_mutex); |
3706a4da PB |
88 | |
89 | return ret; | |
90 | } | |
91 | ||
703cb2cb AB |
92 | static int vp702x_usb_inout_cmd(struct dvb_usb_device *d, u8 cmd, u8 *o, |
93 | int olen, u8 *i, int ilen, int msec) | |
3706a4da PB |
94 | { |
95 | u8 bout[olen+2]; | |
96 | u8 bin[ilen+1]; | |
97 | int ret = 0; | |
98 | ||
99 | bout[0] = 0x00; | |
100 | bout[1] = cmd; | |
101 | memcpy(&bout[2],o,olen); | |
102 | ||
103 | ret = vp702x_usb_inout_op(d, bout, olen+2, bin, ilen+1,msec); | |
104 | ||
105 | if (ret == 0) | |
106 | memcpy(i,&bin[1],ilen); | |
107 | ||
108 | return ret; | |
109 | } | |
110 | ||
111 | static int vp702x_pid_filter(struct dvb_usb_device *d, int index, u16 pid, int onoff) | |
112 | { | |
113 | struct vp702x_state *st = d->priv; | |
114 | u8 buf[9]; | |
115 | ||
116 | if (onoff) { | |
117 | st->pid_table[16] |= 1 << index; | |
118 | st->pid_table[index*2] = (pid >> 8) & 0xff; | |
119 | st->pid_table[index*2+1] = pid & 0xff; | |
120 | } else { | |
121 | st->pid_table[16] &= ~(1 << index); | |
122 | st->pid_table[index*2] = st->pid_table[index*2+1] = 0; | |
123 | } | |
124 | ||
125 | return vp702x_usb_inout_cmd(d,SET_PID_FILTER,st->pid_table,17,buf,9,10); | |
126 | } | |
127 | ||
128 | static int vp702x_power_ctrl(struct dvb_usb_device *d, int onoff) | |
129 | { | |
130 | vp702x_usb_in_op(d,RESET_TUNER,0,0,NULL,0); | |
131 | ||
132 | vp702x_usb_in_op(d,SET_TUNER_POWER_REQ,0,onoff,NULL,0); | |
133 | return vp702x_usb_in_op(d,SET_TUNER_POWER_REQ,0,onoff,NULL,0); | |
134 | } | |
135 | ||
136 | /* keys for the enclosed remote control */ | |
137 | static struct dvb_usb_rc_key vp702x_rc_keys[] = { | |
138 | { 0x00, 0x01, KEY_1 }, | |
139 | { 0x00, 0x02, KEY_2 }, | |
140 | }; | |
141 | ||
142 | /* remote control stuff (does not work with my box) */ | |
143 | static int vp702x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) | |
144 | { | |
145 | u8 key[10]; | |
146 | int i; | |
147 | ||
148 | /* remove the following return to enabled remote querying */ | |
149 | return 0; | |
150 | ||
151 | vp702x_usb_in_op(d,READ_REMOTE_REQ,0,0,key,10); | |
152 | ||
153 | deb_rc("remote query key: %x %d\n",key[1],key[1]); | |
154 | ||
155 | if (key[1] == 0x44) { | |
156 | *state = REMOTE_NO_KEY_PRESSED; | |
157 | return 0; | |
158 | } | |
159 | ||
160 | for (i = 0; i < ARRAY_SIZE(vp702x_rc_keys); i++) | |
161 | if (vp702x_rc_keys[i].custom == key[1]) { | |
162 | *state = REMOTE_KEY_PRESSED; | |
163 | *event = vp702x_rc_keys[i].event; | |
164 | break; | |
165 | } | |
166 | return 0; | |
167 | } | |
168 | ||
169 | static int vp702x_read_mac_addr(struct dvb_usb_device *d,u8 mac[6]) | |
170 | { | |
171 | u8 macb[9]; | |
172 | if (vp702x_usb_inout_cmd(d, GET_MAC_ADDRESS, NULL, 0, macb, 9, 10)) | |
173 | return -EIO; | |
174 | memcpy(mac,&macb[3],6); | |
175 | return 0; | |
176 | } | |
177 | ||
178 | static int vp702x_frontend_attach(struct dvb_usb_device *d) | |
179 | { | |
180 | u8 buf[9] = { 0 }; | |
181 | ||
182 | if (vp702x_usb_inout_cmd(d, GET_SYSTEM_STRING, NULL, 0, buf, 9, 10)) | |
183 | return -EIO; | |
184 | ||
185 | buf[8] = '\0'; | |
186 | info("system string: %s",&buf[1]); | |
187 | ||
188 | d->fe = vp702x_fe_attach(d); | |
189 | return 0; | |
190 | } | |
191 | ||
192 | static struct dvb_usb_properties vp702x_properties; | |
193 | ||
194 | static int vp702x_usb_probe(struct usb_interface *intf, | |
195 | const struct usb_device_id *id) | |
196 | { | |
197 | struct usb_device *udev = interface_to_usbdev(intf); | |
198 | ||
199 | usb_clear_halt(udev,usb_sndctrlpipe(udev,0)); | |
200 | usb_clear_halt(udev,usb_rcvctrlpipe(udev,0)); | |
201 | ||
47dc3d68 | 202 | return dvb_usb_device_init(intf,&vp702x_properties,THIS_MODULE,NULL); |
3706a4da PB |
203 | } |
204 | ||
205 | static struct usb_device_id vp702x_usb_table [] = { | |
206 | { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7021_COLD) }, | |
207 | { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7021_WARM) }, | |
208 | { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7020_COLD) }, | |
209 | { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TWINHAN_VP7020_WARM) }, | |
210 | { 0 }, | |
211 | }; | |
212 | MODULE_DEVICE_TABLE(usb, vp702x_usb_table); | |
213 | ||
214 | static struct dvb_usb_properties vp702x_properties = { | |
215 | .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_NEED_PID_FILTERING, | |
216 | .pid_filter_count = 8, /* !!! */ | |
217 | ||
218 | .usb_ctrl = CYPRESS_FX2, | |
219 | .firmware = "dvb-usb-vp702x-01.fw", | |
220 | ||
221 | .pid_filter = vp702x_pid_filter, | |
222 | .power_ctrl = vp702x_power_ctrl, | |
223 | .frontend_attach = vp702x_frontend_attach, | |
224 | .read_mac_address = vp702x_read_mac_addr, | |
225 | ||
226 | .rc_key_map = vp702x_rc_keys, | |
227 | .rc_key_map_size = ARRAY_SIZE(vp702x_rc_keys), | |
228 | .rc_interval = 400, | |
229 | .rc_query = vp702x_rc_query, | |
230 | ||
231 | .size_of_priv = sizeof(struct vp702x_state), | |
232 | ||
233 | /* parameter for the MPEG2-data transfer */ | |
234 | .urb = { | |
235 | .type = DVB_USB_BULK, | |
236 | .count = 7, | |
237 | .endpoint = 0x02, | |
238 | .u = { | |
239 | .bulk = { | |
240 | .buffersize = 4096, | |
241 | } | |
242 | } | |
243 | }, | |
244 | ||
245 | .num_device_descs = 2, | |
246 | .devices = { | |
247 | { .name = "TwinhanDTV StarBox DVB-S USB2.0 (VP7021)", | |
248 | .cold_ids = { &vp702x_usb_table[0], NULL }, | |
249 | .warm_ids = { &vp702x_usb_table[1], NULL }, | |
250 | }, | |
251 | { .name = "TwinhanDTV StarBox DVB-S USB2.0 (VP7020)", | |
252 | .cold_ids = { &vp702x_usb_table[2], NULL }, | |
253 | .warm_ids = { &vp702x_usb_table[3], NULL }, | |
254 | }, | |
255 | { 0 }, | |
256 | } | |
257 | }; | |
258 | ||
259 | /* usb specific object needed to register this driver with the usb subsystem */ | |
260 | static struct usb_driver vp702x_usb_driver = { | |
3706a4da PB |
261 | .name = "dvb-usb-vp702x", |
262 | .probe = vp702x_usb_probe, | |
263 | .disconnect = dvb_usb_device_exit, | |
264 | .id_table = vp702x_usb_table, | |
265 | }; | |
266 | ||
267 | /* module stuff */ | |
268 | static int __init vp702x_usb_module_init(void) | |
269 | { | |
270 | int result; | |
271 | if ((result = usb_register(&vp702x_usb_driver))) { | |
272 | err("usb_register failed. (%d)",result); | |
273 | return result; | |
274 | } | |
275 | ||
276 | return 0; | |
277 | } | |
278 | ||
279 | static void __exit vp702x_usb_module_exit(void) | |
280 | { | |
281 | /* deregister this driver from the USB subsystem */ | |
282 | usb_deregister(&vp702x_usb_driver); | |
283 | } | |
284 | ||
285 | module_init(vp702x_usb_module_init); | |
286 | module_exit(vp702x_usb_module_exit); | |
287 | ||
288 | MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>"); | |
289 | MODULE_DESCRIPTION("Driver for Twinhan StarBox DVB-S USB2.0 and clones"); | |
290 | MODULE_VERSION("1.0-alpha"); | |
291 | MODULE_LICENSE("GPL"); |