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 */
14int dvb_usb_dtt200u_debug;
15module_param_named(debug,dvb_usb_dtt200u_debug, int, 0644);
16MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2 (or-able))." DVB_USB_DEBUG_STATUS);
17
18DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
19
20struct dtt200u_state {
21	unsigned char data[80];
22};
23
24static 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
40static 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
60ret:
61	mutex_unlock(&d->data_mutex);
62
63	return ret;
64}
65
66static 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
86static 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
126ret:
127	mutex_unlock(&d->data_mutex);
128	return ret;
129}
130
131static 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
137static struct dvb_usb_device_properties dtt200u_properties;
138static struct dvb_usb_device_properties wt220u_fc_properties;
139static struct dvb_usb_device_properties wt220u_properties;
140static struct dvb_usb_device_properties wt220u_zl0353_properties;
141static struct dvb_usb_device_properties wt220u_miglia_properties;
142
143static 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
161enum {
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
174static 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
188MODULE_DEVICE_TABLE(usb, dtt200u_usb_table);
189
190static 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
242static 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
294static 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
346static 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
398static 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 */
420static 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
427module_usb_driver(dtt200u_usb_driver);
428
429MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
430MODULE_DESCRIPTION("Driver for the WideView/Yakumo/Hama/Typhoon/Club3D/Miglia DVB-T USB2.0 devices");
431MODULE_VERSION("1.0");
432MODULE_LICENSE("GPL");
433