1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* DVB USB library compliant Linux driver for the WideView/ Yakumo/ Hama/ |
3 | * Typhoon/ Yuan/ Miglia DVB-T USB2.0 receiver. |
4 | * |
5 | * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@posteo.de) |
6 | * |
7 | * Thanks to Steve Chang from WideView for providing support for the WT-220U. |
8 | * |
9 | * see Documentation/driver-api/media/drivers/dvb-usb.rst for more information |
10 | */ |
11 | #include "dtt200u.h" |
12 | |
13 | /* debug */ |
14 | int dvb_usb_dtt200u_debug; |
15 | module_param_named(debug,dvb_usb_dtt200u_debug, int, 0644); |
16 | MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2 (or-able))." DVB_USB_DEBUG_STATUS); |
17 | |
18 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); |
19 | |
20 | struct dtt200u_state { |
21 | unsigned char data[80]; |
22 | }; |
23 | |
24 | static int dtt200u_power_ctrl(struct dvb_usb_device *d, int onoff) |
25 | { |
26 | struct dtt200u_state *st = d->priv; |
27 | int ret = 0; |
28 | |
29 | mutex_lock(&d->data_mutex); |
30 | |
31 | st->data[0] = SET_INIT; |
32 | |
33 | if (onoff) |
34 | ret = dvb_usb_generic_write(d, st->data, 2); |
35 | |
36 | mutex_unlock(lock: &d->data_mutex); |
37 | return ret; |
38 | } |
39 | |
40 | static int dtt200u_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) |
41 | { |
42 | struct dvb_usb_device *d = adap->dev; |
43 | struct dtt200u_state *st = d->priv; |
44 | int ret; |
45 | |
46 | mutex_lock(&d->data_mutex); |
47 | st->data[0] = SET_STREAMING; |
48 | st->data[1] = onoff; |
49 | |
50 | ret = dvb_usb_generic_write(adap->dev, st->data, 2); |
51 | if (ret < 0) |
52 | goto ret; |
53 | |
54 | if (onoff) |
55 | goto ret; |
56 | |
57 | st->data[0] = RESET_PID_FILTER; |
58 | ret = dvb_usb_generic_write(adap->dev, st->data, 1); |
59 | |
60 | ret: |
61 | mutex_unlock(lock: &d->data_mutex); |
62 | |
63 | return ret; |
64 | } |
65 | |
66 | static int dtt200u_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, int onoff) |
67 | { |
68 | struct dvb_usb_device *d = adap->dev; |
69 | struct dtt200u_state *st = d->priv; |
70 | int ret; |
71 | |
72 | pid = onoff ? pid : 0; |
73 | |
74 | mutex_lock(&d->data_mutex); |
75 | st->data[0] = SET_PID_FILTER; |
76 | st->data[1] = index; |
77 | st->data[2] = pid & 0xff; |
78 | st->data[3] = (pid >> 8) & 0x1f; |
79 | |
80 | ret = dvb_usb_generic_write(adap->dev, st->data, 4); |
81 | mutex_unlock(lock: &d->data_mutex); |
82 | |
83 | return ret; |
84 | } |
85 | |
86 | static int dtt200u_rc_query(struct dvb_usb_device *d) |
87 | { |
88 | struct dtt200u_state *st = d->priv; |
89 | u32 scancode; |
90 | int ret; |
91 | |
92 | mutex_lock(&d->data_mutex); |
93 | st->data[0] = GET_RC_CODE; |
94 | |
95 | ret = dvb_usb_generic_rw(d, st->data, 1, st->data, 5, 0); |
96 | if (ret < 0) |
97 | goto ret; |
98 | |
99 | if (st->data[0] == 1) { |
100 | enum rc_proto proto = RC_PROTO_NEC; |
101 | |
102 | scancode = st->data[1]; |
103 | if ((u8) ~st->data[1] != st->data[2]) { |
104 | /* Extended NEC */ |
105 | scancode = scancode << 8; |
106 | scancode |= st->data[2]; |
107 | proto = RC_PROTO_NECX; |
108 | } |
109 | scancode = scancode << 8; |
110 | scancode |= st->data[3]; |
111 | |
112 | /* Check command checksum is ok */ |
113 | if ((u8) ~st->data[3] == st->data[4]) |
114 | rc_keydown(dev: d->rc_dev, protocol: proto, scancode, toggle: 0); |
115 | else |
116 | rc_keyup(dev: d->rc_dev); |
117 | } else if (st->data[0] == 2) { |
118 | rc_repeat(dev: d->rc_dev); |
119 | } else { |
120 | rc_keyup(dev: d->rc_dev); |
121 | } |
122 | |
123 | if (st->data[0] != 0) |
124 | deb_info("st->data: %*ph\n" , 5, st->data); |
125 | |
126 | ret: |
127 | mutex_unlock(lock: &d->data_mutex); |
128 | return ret; |
129 | } |
130 | |
131 | static int dtt200u_frontend_attach(struct dvb_usb_adapter *adap) |
132 | { |
133 | adap->fe_adap[0].fe = dtt200u_fe_attach(d: adap->dev); |
134 | return 0; |
135 | } |
136 | |
137 | static struct dvb_usb_device_properties dtt200u_properties; |
138 | static struct dvb_usb_device_properties wt220u_fc_properties; |
139 | static struct dvb_usb_device_properties wt220u_properties; |
140 | static struct dvb_usb_device_properties wt220u_zl0353_properties; |
141 | static struct dvb_usb_device_properties wt220u_miglia_properties; |
142 | |
143 | static int dtt200u_usb_probe(struct usb_interface *intf, |
144 | const struct usb_device_id *id) |
145 | { |
146 | if (0 == dvb_usb_device_init(intf, &dtt200u_properties, |
147 | THIS_MODULE, NULL, adapter_nums: adapter_nr) || |
148 | 0 == dvb_usb_device_init(intf, &wt220u_properties, |
149 | THIS_MODULE, NULL, adapter_nums: adapter_nr) || |
150 | 0 == dvb_usb_device_init(intf, &wt220u_fc_properties, |
151 | THIS_MODULE, NULL, adapter_nums: adapter_nr) || |
152 | 0 == dvb_usb_device_init(intf, &wt220u_zl0353_properties, |
153 | THIS_MODULE, NULL, adapter_nums: adapter_nr) || |
154 | 0 == dvb_usb_device_init(intf, &wt220u_miglia_properties, |
155 | THIS_MODULE, NULL, adapter_nums: adapter_nr)) |
156 | return 0; |
157 | |
158 | return -ENODEV; |
159 | } |
160 | |
161 | enum { |
162 | WIDEVIEW_DTT200U_COLD, |
163 | WIDEVIEW_DTT200U_WARM, |
164 | WIDEVIEW_WT220U_COLD, |
165 | WIDEVIEW_WT220U_WARM, |
166 | WIDEVIEW_WT220U_ZL0353_COLD, |
167 | WIDEVIEW_WT220U_ZL0353_WARM, |
168 | WIDEVIEW_WT220U_FC_COLD, |
169 | WIDEVIEW_WT220U_FC_WARM, |
170 | WIDEVIEW_WT220U_ZAP250_COLD, |
171 | MIGLIA_WT220U_ZAP250_COLD, |
172 | }; |
173 | |
174 | static struct usb_device_id dtt200u_usb_table[] = { |
175 | DVB_USB_DEV(WIDEVIEW, WIDEVIEW_DTT200U_COLD), |
176 | DVB_USB_DEV(WIDEVIEW, WIDEVIEW_DTT200U_WARM), |
177 | DVB_USB_DEV(WIDEVIEW, WIDEVIEW_WT220U_COLD), |
178 | DVB_USB_DEV(WIDEVIEW, WIDEVIEW_WT220U_WARM), |
179 | DVB_USB_DEV(WIDEVIEW, WIDEVIEW_WT220U_ZL0353_COLD), |
180 | DVB_USB_DEV(WIDEVIEW, WIDEVIEW_WT220U_ZL0353_WARM), |
181 | DVB_USB_DEV(WIDEVIEW, WIDEVIEW_WT220U_FC_COLD), |
182 | DVB_USB_DEV(WIDEVIEW, WIDEVIEW_WT220U_FC_WARM), |
183 | DVB_USB_DEV(WIDEVIEW, WIDEVIEW_WT220U_ZAP250_COLD), |
184 | DVB_USB_DEV(MIGLIA, MIGLIA_WT220U_ZAP250_COLD), |
185 | { } |
186 | }; |
187 | |
188 | MODULE_DEVICE_TABLE(usb, dtt200u_usb_table); |
189 | |
190 | static struct dvb_usb_device_properties dtt200u_properties = { |
191 | .usb_ctrl = CYPRESS_FX2, |
192 | .firmware = "dvb-usb-dtt200u-01.fw" , |
193 | |
194 | .size_of_priv = sizeof(struct dtt200u_state), |
195 | |
196 | .num_adapters = 1, |
197 | .adapter = { |
198 | { |
199 | .num_frontends = 1, |
200 | .fe = {{ |
201 | .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, |
202 | .pid_filter_count = 15, |
203 | |
204 | .streaming_ctrl = dtt200u_streaming_ctrl, |
205 | .pid_filter = dtt200u_pid_filter, |
206 | .frontend_attach = dtt200u_frontend_attach, |
207 | /* parameter for the MPEG2-data transfer */ |
208 | .stream = { |
209 | .type = USB_BULK, |
210 | .count = 7, |
211 | .endpoint = 0x02, |
212 | .u = { |
213 | .bulk = { |
214 | .buffersize = 4096, |
215 | } |
216 | } |
217 | }, |
218 | }}, |
219 | } |
220 | }, |
221 | .power_ctrl = dtt200u_power_ctrl, |
222 | |
223 | .rc.core = { |
224 | .rc_interval = 300, |
225 | .rc_codes = RC_MAP_DTT200U, |
226 | .rc_query = dtt200u_rc_query, |
227 | .allowed_protos = RC_PROTO_BIT_NEC, |
228 | }, |
229 | |
230 | .generic_bulk_ctrl_endpoint = 0x01, |
231 | |
232 | .num_device_descs = 1, |
233 | .devices = { |
234 | { .name = "WideView/Yuan/Yakumo/Hama/Typhoon DVB-T USB2.0 (WT-200U)" , |
235 | .cold_ids = { &dtt200u_usb_table[WIDEVIEW_DTT200U_COLD], NULL }, |
236 | .warm_ids = { &dtt200u_usb_table[WIDEVIEW_DTT200U_WARM], NULL }, |
237 | }, |
238 | { NULL }, |
239 | } |
240 | }; |
241 | |
242 | static struct dvb_usb_device_properties wt220u_properties = { |
243 | .usb_ctrl = CYPRESS_FX2, |
244 | .firmware = "dvb-usb-wt220u-02.fw" , |
245 | |
246 | .size_of_priv = sizeof(struct dtt200u_state), |
247 | |
248 | .num_adapters = 1, |
249 | .adapter = { |
250 | { |
251 | .num_frontends = 1, |
252 | .fe = {{ |
253 | .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, |
254 | .pid_filter_count = 15, |
255 | |
256 | .streaming_ctrl = dtt200u_streaming_ctrl, |
257 | .pid_filter = dtt200u_pid_filter, |
258 | .frontend_attach = dtt200u_frontend_attach, |
259 | /* parameter for the MPEG2-data transfer */ |
260 | .stream = { |
261 | .type = USB_BULK, |
262 | .count = 7, |
263 | .endpoint = 0x02, |
264 | .u = { |
265 | .bulk = { |
266 | .buffersize = 4096, |
267 | } |
268 | } |
269 | }, |
270 | }}, |
271 | } |
272 | }, |
273 | .power_ctrl = dtt200u_power_ctrl, |
274 | |
275 | .rc.core = { |
276 | .rc_interval = 300, |
277 | .rc_codes = RC_MAP_DTT200U, |
278 | .rc_query = dtt200u_rc_query, |
279 | .allowed_protos = RC_PROTO_BIT_NEC, |
280 | }, |
281 | |
282 | .generic_bulk_ctrl_endpoint = 0x01, |
283 | |
284 | .num_device_descs = 1, |
285 | .devices = { |
286 | { .name = "WideView WT-220U PenType Receiver (Typhoon/Freecom)" , |
287 | .cold_ids = { &dtt200u_usb_table[WIDEVIEW_WT220U_COLD], &dtt200u_usb_table[WIDEVIEW_WT220U_ZAP250_COLD], NULL }, |
288 | .warm_ids = { &dtt200u_usb_table[WIDEVIEW_WT220U_WARM], NULL }, |
289 | }, |
290 | { NULL }, |
291 | } |
292 | }; |
293 | |
294 | static struct dvb_usb_device_properties wt220u_fc_properties = { |
295 | .usb_ctrl = CYPRESS_FX2, |
296 | .firmware = "dvb-usb-wt220u-fc03.fw" , |
297 | |
298 | .size_of_priv = sizeof(struct dtt200u_state), |
299 | |
300 | .num_adapters = 1, |
301 | .adapter = { |
302 | { |
303 | .num_frontends = 1, |
304 | .fe = {{ |
305 | .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, |
306 | .pid_filter_count = 15, |
307 | |
308 | .streaming_ctrl = dtt200u_streaming_ctrl, |
309 | .pid_filter = dtt200u_pid_filter, |
310 | .frontend_attach = dtt200u_frontend_attach, |
311 | /* parameter for the MPEG2-data transfer */ |
312 | .stream = { |
313 | .type = USB_BULK, |
314 | .count = 7, |
315 | .endpoint = 0x06, |
316 | .u = { |
317 | .bulk = { |
318 | .buffersize = 4096, |
319 | } |
320 | } |
321 | }, |
322 | }}, |
323 | } |
324 | }, |
325 | .power_ctrl = dtt200u_power_ctrl, |
326 | |
327 | .rc.core = { |
328 | .rc_interval = 300, |
329 | .rc_codes = RC_MAP_DTT200U, |
330 | .rc_query = dtt200u_rc_query, |
331 | .allowed_protos = RC_PROTO_BIT_NEC, |
332 | }, |
333 | |
334 | .generic_bulk_ctrl_endpoint = 0x01, |
335 | |
336 | .num_device_descs = 1, |
337 | .devices = { |
338 | { .name = "WideView WT-220U PenType Receiver (Typhoon/Freecom)" , |
339 | .cold_ids = { &dtt200u_usb_table[WIDEVIEW_WT220U_FC_COLD], NULL }, |
340 | .warm_ids = { &dtt200u_usb_table[WIDEVIEW_WT220U_FC_WARM], NULL }, |
341 | }, |
342 | { NULL }, |
343 | } |
344 | }; |
345 | |
346 | static struct dvb_usb_device_properties wt220u_zl0353_properties = { |
347 | .usb_ctrl = CYPRESS_FX2, |
348 | .firmware = "dvb-usb-wt220u-zl0353-01.fw" , |
349 | |
350 | .size_of_priv = sizeof(struct dtt200u_state), |
351 | |
352 | .num_adapters = 1, |
353 | .adapter = { |
354 | { |
355 | .num_frontends = 1, |
356 | .fe = {{ |
357 | .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING, |
358 | .pid_filter_count = 15, |
359 | |
360 | .streaming_ctrl = dtt200u_streaming_ctrl, |
361 | .pid_filter = dtt200u_pid_filter, |
362 | .frontend_attach = dtt200u_frontend_attach, |
363 | /* parameter for the MPEG2-data transfer */ |
364 | .stream = { |
365 | .type = USB_BULK, |
366 | .count = 7, |
367 | .endpoint = 0x02, |
368 | .u = { |
369 | .bulk = { |
370 | .buffersize = 4096, |
371 | } |
372 | } |
373 | }, |
374 | }}, |
375 | } |
376 | }, |
377 | .power_ctrl = dtt200u_power_ctrl, |
378 | |
379 | .rc.core = { |
380 | .rc_interval = 300, |
381 | .rc_codes = RC_MAP_DTT200U, |
382 | .rc_query = dtt200u_rc_query, |
383 | .allowed_protos = RC_PROTO_BIT_NEC, |
384 | }, |
385 | |
386 | .generic_bulk_ctrl_endpoint = 0x01, |
387 | |
388 | .num_device_descs = 1, |
389 | .devices = { |
390 | { .name = "WideView WT-220U PenType Receiver (based on ZL353)" , |
391 | .cold_ids = { &dtt200u_usb_table[WIDEVIEW_WT220U_ZL0353_COLD], NULL }, |
392 | .warm_ids = { &dtt200u_usb_table[WIDEVIEW_WT220U_ZL0353_WARM], NULL }, |
393 | }, |
394 | { NULL }, |
395 | } |
396 | }; |
397 | |
398 | static struct dvb_usb_device_properties wt220u_miglia_properties = { |
399 | .usb_ctrl = CYPRESS_FX2, |
400 | .firmware = "dvb-usb-wt220u-miglia-01.fw" , |
401 | |
402 | .size_of_priv = sizeof(struct dtt200u_state), |
403 | |
404 | .num_adapters = 1, |
405 | .generic_bulk_ctrl_endpoint = 0x01, |
406 | |
407 | .num_device_descs = 1, |
408 | .devices = { |
409 | { .name = "WideView WT-220U PenType Receiver (Miglia)" , |
410 | .cold_ids = { &dtt200u_usb_table[MIGLIA_WT220U_ZAP250_COLD], NULL }, |
411 | /* This device turns into WT220U_ZL0353_WARM when fw |
412 | has been uploaded */ |
413 | .warm_ids = { NULL }, |
414 | }, |
415 | { NULL }, |
416 | } |
417 | }; |
418 | |
419 | /* usb specific object needed to register this driver with the usb subsystem */ |
420 | static struct usb_driver dtt200u_usb_driver = { |
421 | .name = "dvb_usb_dtt200u" , |
422 | .probe = dtt200u_usb_probe, |
423 | .disconnect = dvb_usb_device_exit, |
424 | .id_table = dtt200u_usb_table, |
425 | }; |
426 | |
427 | module_usb_driver(dtt200u_usb_driver); |
428 | |
429 | MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>" ); |
430 | MODULE_DESCRIPTION("Driver for the WideView/Yakumo/Hama/Typhoon/Club3D/Miglia DVB-T USB2.0 devices" ); |
431 | MODULE_VERSION("1.0" ); |
432 | MODULE_LICENSE("GPL" ); |
433 | |