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
dtt200u_power_ctrl(struct dvb_usb_device * d,int onoff)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(&d->data_mutex);
37 return ret;
38 }
39
dtt200u_streaming_ctrl(struct dvb_usb_adapter * adap,int onoff)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(&d->data_mutex);
62
63 return ret;
64 }
65
dtt200u_pid_filter(struct dvb_usb_adapter * adap,int index,u16 pid,int onoff)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(&d->data_mutex);
82
83 return ret;
84 }
85
dtt200u_rc_query(struct dvb_usb_device * d)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(d->rc_dev, proto, scancode, 0);
115 else
116 rc_keyup(d->rc_dev);
117 } else if (st->data[0] == 2) {
118 rc_repeat(d->rc_dev);
119 } else {
120 rc_keyup(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(&d->data_mutex);
128 return ret;
129 }
130
dtt200u_frontend_attach(struct dvb_usb_adapter * adap)131 static int dtt200u_frontend_attach(struct dvb_usb_adapter *adap)
132 {
133 adap->fe_adap[0].fe = dtt200u_fe_attach(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
dtt200u_usb_probe(struct usb_interface * intf,const struct usb_device_id * id)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_nr) ||
148 0 == dvb_usb_device_init(intf, &wt220u_properties,
149 THIS_MODULE, NULL, adapter_nr) ||
150 0 == dvb_usb_device_init(intf, &wt220u_fc_properties,
151 THIS_MODULE, NULL, adapter_nr) ||
152 0 == dvb_usb_device_init(intf, &wt220u_zl0353_properties,
153 THIS_MODULE, NULL, adapter_nr) ||
154 0 == dvb_usb_device_init(intf, &wt220u_miglia_properties,
155 THIS_MODULE, NULL, 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