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 | ||
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"); |