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(&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(&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(&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(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 
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 
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