File Index
Files and directories
Sign in
FILE

streamzap.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/rc/streamzap.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Streamzap Remote Control driver
  4 *
  5 * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
  6 * Copyright (c) 2010 Jarod Wilson <jarod@wilsonet.com>
  7 *
  8 * This driver was based on the work of Greg Wickham and Adrian
  9 * Dewhurst. It was substantially rewritten to support correct signal
 10 * gaps and now maintains a delay buffer, which is used to present
 11 * consistent timing behaviour to user space applications. Without the
 12 * delay buffer an ugly hack would be required in lircd, which can
 13 * cause sluggish signal decoding in certain situations.
 14 *
 15 * Ported to in-kernel ir-core interface by Jarod Wilson
 16 *
 17 * This driver is based on the USB skeleton driver packaged with the
 18 * kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com)
 19 */
 20
 21#include <linux/device.h>
 22#include <linux/module.h>
 23#include <linux/slab.h>
 24#include <linux/usb.h>
 25#include <linux/usb/input.h>
 26#include <media/rc-core.h>
 27
 28#define DRIVER_NAME	"streamzap"
 29#define DRIVER_DESC	"Streamzap Remote Control driver"
 30
 31#define USB_STREAMZAP_VENDOR_ID		0x0e9c
 32#define USB_STREAMZAP_PRODUCT_ID	0x0000
 33
 34/* table of devices that work with this driver */
 35static const struct usb_device_id streamzap_table[] = {
 36	/* Streamzap Remote Control */
 37	{ USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
 38	/* Terminating entry */
 39	{ }
 40};
 41
 42MODULE_DEVICE_TABLE(usb, streamzap_table);
 43
 44#define SZ_PULSE_MASK 0xf0
 45#define SZ_SPACE_MASK 0x0f
 46#define SZ_TIMEOUT    0xff
 47#define SZ_RESOLUTION 256
 48
 49/* number of samples buffered */
 50#define SZ_BUF_LEN 128
 51
 52enum StreamzapDecoderState {
 53	PulseSpace,
 54	FullPulse,
 55	FullSpace,
 56	IgnorePulse
 57};
 58
 59/* structure to hold our device specific stuff */
 60struct streamzap_ir {
 61	/* ir-core */
 62	struct rc_dev *rdev;
 63
 64	/* core device info */
 65	struct device *dev;
 66
 67	/* usb */
 68	struct urb		*urb_in;
 69
 70	/* buffer & dma */
 71	unsigned char		*buf_in;
 72	dma_addr_t		dma_in;
 73	unsigned int		buf_in_len;
 74
 75	/* track what state we're in */
 76	enum StreamzapDecoderState decoder_state;
 77
 78	char			phys[64];
 79};
 80
 81
 82/* local function prototypes */
 83static int streamzap_probe(struct usb_interface *interface,
 84			   const struct usb_device_id *id);
 85static void streamzap_disconnect(struct usb_interface *interface);
 86static void streamzap_callback(struct urb *urb);
 87static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
 88static int streamzap_resume(struct usb_interface *intf);
 89
 90/* usb specific object needed to register this driver with the usb subsystem */
 91static struct usb_driver streamzap_driver = {
 92	.name =		DRIVER_NAME,
 93	.probe =	streamzap_probe,
 94	.disconnect =	streamzap_disconnect,
 95	.suspend =	streamzap_suspend,
 96	.resume =	streamzap_resume,
 97	.id_table =	streamzap_table,
 98};
 99
100static void sz_push(struct streamzap_ir *sz, struct ir_raw_event rawir)
101{
102	dev_dbg(sz->dev, "Storing %s with duration %u us\n",
103		(rawir.pulse ? "pulse" : "space"), rawir.duration);
104	ir_raw_event_store_with_filter(sz->rdev, &rawir);
105}
106
107static void sz_push_full_pulse(struct streamzap_ir *sz,
108			       unsigned char value)
109{
110	struct ir_raw_event rawir = {
111		.pulse = true,
112		.duration = value * SZ_RESOLUTION + SZ_RESOLUTION / 2,
113	};
114
115	sz_push(sz, rawir);
116}
117
118static void sz_push_half_pulse(struct streamzap_ir *sz,
119			       unsigned char value)
120{
121	sz_push_full_pulse(sz, (value & SZ_PULSE_MASK) >> 4);
122}
123
124static void sz_push_full_space(struct streamzap_ir *sz,
125			       unsigned char value)
126{
127	struct ir_raw_event rawir = {
128		.pulse = false,
129		.duration = value * SZ_RESOLUTION + SZ_RESOLUTION / 2,
130	};
131
132	sz_push(sz, rawir);
133}
134
135static void sz_push_half_space(struct streamzap_ir *sz,
136			       unsigned long value)
137{
138	sz_push_full_space(sz, value & SZ_SPACE_MASK);
139}
140
141static void sz_process_ir_data(struct streamzap_ir *sz, int len)
142{
143	unsigned int i;
144
145	for (i = 0; i < len; i++) {
146		dev_dbg(sz->dev, "sz->buf_in[%d]: %x\n",
147			i, (unsigned char)sz->buf_in[i]);
148		switch (sz->decoder_state) {
149		case PulseSpace:
150			if ((sz->buf_in[i] & SZ_PULSE_MASK) ==
151				SZ_PULSE_MASK) {
152				sz->decoder_state = FullPulse;
153				continue;
154			} else if ((sz->buf_in[i] & SZ_SPACE_MASK)
155					== SZ_SPACE_MASK) {
156				sz_push_half_pulse(sz, sz->buf_in[i]);
157				sz->decoder_state = FullSpace;
158				continue;
159			} else {
160				sz_push_half_pulse(sz, sz->buf_in[i]);
161				sz_push_half_space(sz, sz->buf_in[i]);
162			}
163			break;
164		case FullPulse:
165			sz_push_full_pulse(sz, sz->buf_in[i]);
166			sz->decoder_state = IgnorePulse;
167			break;
168		case FullSpace:
169			if (sz->buf_in[i] == SZ_TIMEOUT) {
170				struct ir_raw_event rawir = {
171					.pulse = false,
172					.duration = sz->rdev->timeout
173				};
174				sz_push(sz, rawir);
175			} else {
176				sz_push_full_space(sz, sz->buf_in[i]);
177			}
178			sz->decoder_state = PulseSpace;
179			break;
180		case IgnorePulse:
181			if ((sz->buf_in[i] & SZ_SPACE_MASK) ==
182				SZ_SPACE_MASK) {
183				sz->decoder_state = FullSpace;
184				continue;
185			}
186			sz_push_half_space(sz, sz->buf_in[i]);
187			sz->decoder_state = PulseSpace;
188			break;
189		}
190	}
191
192	ir_raw_event_handle(sz->rdev);
193}
194
195/*
196 * streamzap_callback - usb IRQ handler callback
197 *
198 * This procedure is invoked on reception of data from
199 * the usb remote.
200 */
201static void streamzap_callback(struct urb *urb)
202{
203	struct streamzap_ir *sz;
204	int len;
205
206	if (!urb)
207		return;
208
209	sz = urb->context;
210	len = urb->actual_length;
211
212	switch (urb->status) {
213	case 0:
214		dev_dbg(sz->dev, "%s: received urb, len %d\n", __func__, len);
215		sz_process_ir_data(sz, len);
216		break;
217	case -ECONNRESET:
218	case -ENOENT:
219	case -ESHUTDOWN:
220		/*
221		 * this urb is terminated, clean up.
222		 * sz might already be invalid at this point
223		 */
224		dev_err(sz->dev, "urb terminated, status: %d\n", urb->status);
225		return;
226	default:
227		break;
228	}
229
230	usb_submit_urb(urb, GFP_ATOMIC);
231}
232
233static struct rc_dev *streamzap_init_rc_dev(struct streamzap_ir *sz,
234					    struct usb_device *usbdev)
235{
236	struct rc_dev *rdev;
237	struct device *dev = sz->dev;
238	int ret;
239
240	rdev = rc_allocate_device(RC_DRIVER_IR_RAW);
241	if (!rdev)
242		goto out;
243
244	usb_make_path(usbdev, sz->phys, sizeof(sz->phys));
245	strlcat(sz->phys, "/input0", sizeof(sz->phys));
246
247	rdev->device_name = "Streamzap PC Remote Infrared Receiver";
248	rdev->input_phys = sz->phys;
249	usb_to_input_id(usbdev, &rdev->input_id);
250	rdev->dev.parent = dev;
251	rdev->priv = sz;
252	rdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
253	rdev->driver_name = DRIVER_NAME;
254	rdev->map_name = RC_MAP_STREAMZAP;
255	rdev->rx_resolution = SZ_RESOLUTION;
256
257	ret = rc_register_device(rdev);
258	if (ret < 0) {
259		dev_err(dev, "remote input device register failed\n");
260		goto out;
261	}
262
263	return rdev;
264
265out:
266	rc_free_device(rdev);
267	return NULL;
268}
269
270/*
271 *	streamzap_probe
272 *
273 *	Called by usb-core to associated with a candidate device
274 *	On any failure the return value is the ERROR
275 *	On success return 0
276 */
277static int streamzap_probe(struct usb_interface *intf,
278			   const struct usb_device_id *id)
279{
280	struct usb_device *usbdev = interface_to_usbdev(intf);
281	struct usb_endpoint_descriptor *endpoint;
282	struct usb_host_interface *iface_host;
283	struct streamzap_ir *sz = NULL;
284	int retval = -ENOMEM;
285	int pipe, maxp;
286
287	/* Allocate space for device driver specific data */
288	sz = kzalloc(sizeof(struct streamzap_ir), GFP_KERNEL);
289	if (!sz)
290		return -ENOMEM;
291
292	/* Check to ensure endpoint information matches requirements */
293	iface_host = intf->cur_altsetting;
294
295	if (iface_host->desc.bNumEndpoints != 1) {
296		dev_err(&intf->dev, "%s: Unexpected desc.bNumEndpoints (%d)\n",
297			__func__, iface_host->desc.bNumEndpoints);
298		retval = -ENODEV;
299		goto free_sz;
300	}
301
302	endpoint = &iface_host->endpoint[0].desc;
303	if (!usb_endpoint_dir_in(endpoint)) {
304		dev_err(&intf->dev, "%s: endpoint doesn't match input device 02%02x\n",
305			__func__, endpoint->bEndpointAddress);
306		retval = -ENODEV;
307		goto free_sz;
308	}
309
310	if (!usb_endpoint_xfer_int(endpoint)) {
311		dev_err(&intf->dev, "%s: endpoint attributes don't match xfer 02%02x\n",
312			__func__, endpoint->bmAttributes);
313		retval = -ENODEV;
314		goto free_sz;
315	}
316
317	pipe = usb_rcvintpipe(usbdev, endpoint->bEndpointAddress);
318	maxp = usb_maxpacket(usbdev, pipe);
319
320	if (maxp == 0) {
321		dev_err(&intf->dev, "%s: endpoint Max Packet Size is 0!?!\n",
322			__func__);
323		retval = -ENODEV;
324		goto free_sz;
325	}
326
327	/* Allocate the USB buffer and IRQ URB */
328	sz->buf_in = usb_alloc_coherent(usbdev, maxp, GFP_ATOMIC, &sz->dma_in);
329	if (!sz->buf_in)
330		goto free_sz;
331
332	sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
333	if (!sz->urb_in)
334		goto free_buf_in;
335
336	sz->dev = &intf->dev;
337	sz->buf_in_len = maxp;
338
339	sz->rdev = streamzap_init_rc_dev(sz, usbdev);
340	if (!sz->rdev)
341		goto rc_dev_fail;
342
343	sz->decoder_state = PulseSpace;
344	/* FIXME: don't yet have a way to set this */
345	sz->rdev->timeout = SZ_TIMEOUT * SZ_RESOLUTION;
346	#if 0
347	/* not yet supported, depends on patches from maxim */
348	/* see also: LIRC_GET_REC_RESOLUTION and LIRC_SET_REC_TIMEOUT */
349	sz->min_timeout = SZ_TIMEOUT * SZ_RESOLUTION;
350	sz->max_timeout = SZ_TIMEOUT * SZ_RESOLUTION;
351	#endif
352
353	/* Complete final initialisations */
354	usb_fill_int_urb(sz->urb_in, usbdev, pipe, sz->buf_in,
355			 maxp, streamzap_callback, sz, endpoint->bInterval);
356	sz->urb_in->transfer_dma = sz->dma_in;
357	sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
358
359	usb_set_intfdata(intf, sz);
360
361	if (usb_submit_urb(sz->urb_in, GFP_ATOMIC))
362		dev_err(sz->dev, "urb submit failed\n");
363
364	return 0;
365
366rc_dev_fail:
367	usb_free_urb(sz->urb_in);
368free_buf_in:
369	usb_free_coherent(usbdev, maxp, sz->buf_in, sz->dma_in);
370free_sz:
371	kfree(sz);
372
373	return retval;
374}
375
376/*
377 * streamzap_disconnect
378 *
379 * Called by the usb core when the device is removed from the system.
380 *
381 * This routine guarantees that the driver will not submit any more urbs
382 * by clearing dev->usbdev.  It is also supposed to terminate any currently
383 * active urbs.  Unfortunately, usb_bulk_msg(), used in streamzap_read(),
384 * does not provide any way to do this.
385 */
386static void streamzap_disconnect(struct usb_interface *interface)
387{
388	struct streamzap_ir *sz = usb_get_intfdata(interface);
389	struct usb_device *usbdev = interface_to_usbdev(interface);
390
391	usb_set_intfdata(interface, NULL);
392
393	if (!sz)
394		return;
395
396	usb_kill_urb(sz->urb_in);
397	rc_unregister_device(sz->rdev);
398	usb_free_urb(sz->urb_in);
399	usb_free_coherent(usbdev, sz->buf_in_len, sz->buf_in, sz->dma_in);
400
401	kfree(sz);
402}
403
404static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
405{
406	struct streamzap_ir *sz = usb_get_intfdata(intf);
407
408	usb_kill_urb(sz->urb_in);
409
410	return 0;
411}
412
413static int streamzap_resume(struct usb_interface *intf)
414{
415	struct streamzap_ir *sz = usb_get_intfdata(intf);
416
417	if (usb_submit_urb(sz->urb_in, GFP_NOIO)) {
418		dev_err(sz->dev, "Error submitting urb\n");
419		return -EIO;
420	}
421
422	return 0;
423}
424
425module_usb_driver(streamzap_driver);
426
427MODULE_AUTHOR("Jarod Wilson <jarod@wilsonet.com>");
428MODULE_DESCRIPTION(DRIVER_DESC);
429MODULE_LICENSE("GPL");
~/ Read-only file preview.