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 static 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 };
174 MODULE_DEVICE_TABLE(usb, dtt200u_usb_table);
175 
176 static 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 
228 static 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 
280 static 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 
332 static 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 
384 static 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 */
406 static 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 
413 module_usb_driver(dtt200u_usb_driver);
414 
415 MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@posteo.de>");
416 MODULE_DESCRIPTION("Driver for the WideView/Yakumo/Hama/Typhoon/Club3D/Miglia DVB-T USB2.0 devices");
417 MODULE_VERSION("1.0");
418 MODULE_LICENSE("GPL");
419