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
161static struct usb_device_id dtt200u_usb_table [] = {
162	{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_DTT200U_COLD) },
163	{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_DTT200U_WARM) },
164	{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_COLD)  },
165	{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_WARM)  },
166	{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_COLD)  },
167	{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_WARM)  },
168	{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_FC_COLD)  },
169	{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_FC_WARM)  },
170	{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZAP250_COLD)  },
171	{ USB_DEVICE(USB_VID_MIGLIA, USB_PID_WT220U_ZAP250_COLD)  },
172	{ 0 },
173};
174MODULE_DEVICE_TABLE(usb, dtt200u_usb_table);
175
176static struct dvb_usb_device_properties dtt200u_properties = {
177	.usb_ctrl = CYPRESS_FX2,
178	.firmware = "dvb-usb-dtt200u-01.fw",
179
180	.size_of_priv     = sizeof(struct dtt200u_state),
181
182	.num_adapters = 1,
183	.adapter = {
184		{
185		.num_frontends = 1,
186		.fe = {{
187			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
188			.pid_filter_count = 15,
189
190	.streaming_ctrl  = dtt200u_streaming_ctrl,
191	.pid_filter      = dtt200u_pid_filter,
192	.frontend_attach = dtt200u_frontend_attach,
193	/* parameter for the MPEG2-data transfer */
194			.stream = {
195				.type = USB_BULK,
196		.count = 7,
197		.endpoint = 0x02,
198		.u = {
199			.bulk = {
200				.buffersize = 4096,
201			}
202		}
203	},
204		}},
205		}
206	},
207	.power_ctrl      = dtt200u_power_ctrl,
208
209	.rc.core = {
210		.rc_interval     = 300,
211		.rc_codes        = RC_MAP_DTT200U,
212		.rc_query        = dtt200u_rc_query,
213		.allowed_protos  = RC_PROTO_BIT_NEC,
214	},
215
216	.generic_bulk_ctrl_endpoint = 0x01,
217
218	.num_device_descs = 1,
219	.devices = {
220		{ .name = "WideView/Yuan/Yakumo/Hama/Typhoon DVB-T USB2.0 (WT-200U)",
221		  .cold_ids = { &dtt200u_usb_table[0], NULL },
222		  .warm_ids = { &dtt200u_usb_table[1], NULL },
223		},
224		{ NULL },
225	}
226};
227
228static struct dvb_usb_device_properties wt220u_properties = {
229	.usb_ctrl = CYPRESS_FX2,
230	.firmware = "dvb-usb-wt220u-02.fw",
231
232	.size_of_priv     = sizeof(struct dtt200u_state),
233
234	.num_adapters = 1,
235	.adapter = {
236		{
237		.num_frontends = 1,
238		.fe = {{
239			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
240			.pid_filter_count = 15,
241
242	.streaming_ctrl  = dtt200u_streaming_ctrl,
243	.pid_filter      = dtt200u_pid_filter,
244	.frontend_attach = dtt200u_frontend_attach,
245	/* parameter for the MPEG2-data transfer */
246			.stream = {
247				.type = USB_BULK,
248		.count = 7,
249		.endpoint = 0x02,
250		.u = {
251			.bulk = {
252				.buffersize = 4096,
253			}
254		}
255	},
256		}},
257		}
258	},
259	.power_ctrl      = dtt200u_power_ctrl,
260
261	.rc.core = {
262		.rc_interval     = 300,
263		.rc_codes        = RC_MAP_DTT200U,
264		.rc_query        = dtt200u_rc_query,
265		.allowed_protos  = RC_PROTO_BIT_NEC,
266	},
267
268	.generic_bulk_ctrl_endpoint = 0x01,
269
270	.num_device_descs = 1,
271	.devices = {
272		{ .name = "WideView WT-220U PenType Receiver (Typhoon/Freecom)",
273		  .cold_ids = { &dtt200u_usb_table[2], &dtt200u_usb_table[8], NULL },
274		  .warm_ids = { &dtt200u_usb_table[3], NULL },
275		},
276		{ NULL },
277	}
278};
279
280static struct dvb_usb_device_properties wt220u_fc_properties = {
281	.usb_ctrl = CYPRESS_FX2,
282	.firmware = "dvb-usb-wt220u-fc03.fw",
283
284	.size_of_priv     = sizeof(struct dtt200u_state),
285
286	.num_adapters = 1,
287	.adapter = {
288		{
289		.num_frontends = 1,
290		.fe = {{
291			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
292			.pid_filter_count = 15,
293
294	.streaming_ctrl  = dtt200u_streaming_ctrl,
295	.pid_filter      = dtt200u_pid_filter,
296	.frontend_attach = dtt200u_frontend_attach,
297	/* parameter for the MPEG2-data transfer */
298			.stream = {
299				.type = USB_BULK,
300		.count = 7,
301				.endpoint = 0x06,
302		.u = {
303			.bulk = {
304				.buffersize = 4096,
305			}
306		}
307	},
308		}},
309		}
310	},
311	.power_ctrl      = dtt200u_power_ctrl,
312
313	.rc.core = {
314		.rc_interval     = 300,
315		.rc_codes        = RC_MAP_DTT200U,
316		.rc_query        = dtt200u_rc_query,
317		.allowed_protos  = RC_PROTO_BIT_NEC,
318	},
319
320	.generic_bulk_ctrl_endpoint = 0x01,
321
322	.num_device_descs = 1,
323	.devices = {
324		{ .name = "WideView WT-220U PenType Receiver (Typhoon/Freecom)",
325		  .cold_ids = { &dtt200u_usb_table[6], NULL },
326		  .warm_ids = { &dtt200u_usb_table[7], NULL },
327		},
328		{ NULL },
329	}
330};
331
332static struct dvb_usb_device_properties wt220u_zl0353_properties = {
333	.usb_ctrl = CYPRESS_FX2,
334	.firmware = "dvb-usb-wt220u-zl0353-01.fw",
335
336	.size_of_priv     = sizeof(struct dtt200u_state),
337
338	.num_adapters = 1,
339	.adapter = {
340		{
341		.num_frontends = 1,
342		.fe = {{
343			.caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_NEED_PID_FILTERING,
344			.pid_filter_count = 15,
345
346			.streaming_ctrl  = dtt200u_streaming_ctrl,
347			.pid_filter      = dtt200u_pid_filter,
348			.frontend_attach = dtt200u_frontend_attach,
349			/* parameter for the MPEG2-data transfer */
350			.stream = {
351				.type = USB_BULK,
352				.count = 7,
353				.endpoint = 0x02,
354				.u = {
355					.bulk = {
356						.buffersize = 4096,
357					}
358				}
359			},
360		}},
361		}
362	},
363	.power_ctrl      = dtt200u_power_ctrl,
364
365	.rc.core = {
366		.rc_interval     = 300,
367		.rc_codes        = RC_MAP_DTT200U,
368		.rc_query        = dtt200u_rc_query,
369		.allowed_protos  = RC_PROTO_BIT_NEC,
370	},
371
372	.generic_bulk_ctrl_endpoint = 0x01,
373
374	.num_device_descs = 1,
375	.devices = {
376		{ .name = "WideView WT-220U PenType Receiver (based on ZL353)",
377		  .cold_ids = { &dtt200u_usb_table[4], NULL },
378		  .warm_ids = { &dtt200u_usb_table[5], NULL },
379		},
380		{ NULL },
381	}
382};
383
384static struct dvb_usb_device_properties wt220u_miglia_properties = {
385	.usb_ctrl = CYPRESS_FX2,
386	.firmware = "dvb-usb-wt220u-miglia-01.fw",
387
388	.size_of_priv     = sizeof(struct dtt200u_state),
389
390	.num_adapters = 1,
391	.generic_bulk_ctrl_endpoint = 0x01,
392
393	.num_device_descs = 1,
394	.devices = {
395		{ .name = "WideView WT-220U PenType Receiver (Miglia)",
396		  .cold_ids = { &dtt200u_usb_table[9], NULL },
397		  /* This device turns into WT220U_ZL0353_WARM when fw
398		     has been uploaded */
399		  .warm_ids = { NULL },
400		},
401		{ NULL },
402	}
403};
404
405/* usb specific object needed to register this driver with the usb subsystem */
406static struct usb_driver dtt200u_usb_driver = {
407	.name		= "dvb_usb_dtt200u",
408	.probe		= dtt200u_usb_probe,
409	.disconnect = dvb_usb_device_exit,
410	.id_table	= dtt200u_usb_table,
411};
412
413module_usb_driver(dtt200u_usb_driver);
414
415MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
416MODULE_DESCRIPTION("Driver for the WideView/Yakumo/Hama/Typhoon/Club3D/Miglia DVB-T USB2.0 devices");
417MODULE_VERSION("1.0");
418MODULE_LICENSE("GPL");
419