File Index
Files and directories
Sign in
FILE

iguanair.c

File preview with syntax highlighting.

linux-6.19.14/drivers/media/rc/iguanair.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * IguanaWorks USB IR Transceiver support
  4 *
  5 * Copyright (C) 2012 Sean Young <sean@mess.org>
  6 */
  7
  8#include <linux/device.h>
  9#include <linux/kernel.h>
 10#include <linux/module.h>
 11#include <linux/usb.h>
 12#include <linux/usb/input.h>
 13#include <linux/slab.h>
 14#include <linux/completion.h>
 15#include <media/rc-core.h>
 16
 17#define BUF_SIZE 152
 18
 19struct iguanair {
 20	struct rc_dev *rc;
 21
 22	struct device *dev;
 23	struct usb_device *udev;
 24
 25	uint16_t version;
 26	uint8_t bufsize;
 27	uint8_t cycle_overhead;
 28
 29	/* receiver support */
 30	bool receiver_on;
 31	dma_addr_t dma_in, dma_out;
 32	uint8_t *buf_in;
 33	struct urb *urb_in, *urb_out;
 34	struct completion completion;
 35
 36	/* transmit support */
 37	bool tx_overflow;
 38	uint32_t carrier;
 39	struct send_packet *packet;
 40
 41	char name[64];
 42	char phys[64];
 43};
 44
 45#define CMD_NOP			0x00
 46#define CMD_GET_VERSION		0x01
 47#define CMD_GET_BUFSIZE		0x11
 48#define CMD_GET_FEATURES	0x10
 49#define CMD_SEND		0x15
 50#define CMD_EXECUTE		0x1f
 51#define CMD_RX_OVERFLOW		0x31
 52#define CMD_TX_OVERFLOW		0x32
 53#define CMD_RECEIVER_ON		0x12
 54#define CMD_RECEIVER_OFF	0x14
 55
 56#define DIR_IN			0xdc
 57#define DIR_OUT			0xcd
 58
 59#define MAX_IN_PACKET		8u
 60#define MAX_OUT_PACKET		(sizeof(struct send_packet) + BUF_SIZE)
 61#define TIMEOUT			1000
 62#define RX_RESOLUTION		21
 63
 64struct packet {
 65	uint16_t start;
 66	uint8_t direction;
 67	uint8_t cmd;
 68};
 69
 70struct send_packet {
 71	struct packet header;
 72	uint8_t length;
 73	uint8_t channels;
 74	uint8_t busy7;
 75	uint8_t busy4;
 76	uint8_t payload[];
 77};
 78
 79static void process_ir_data(struct iguanair *ir, unsigned len)
 80{
 81	if (len >= 4 && ir->buf_in[0] == 0 && ir->buf_in[1] == 0) {
 82		switch (ir->buf_in[3]) {
 83		case CMD_GET_VERSION:
 84			if (len == 6) {
 85				ir->version = (ir->buf_in[5] << 8) |
 86							ir->buf_in[4];
 87				complete(&ir->completion);
 88			}
 89			break;
 90		case CMD_GET_BUFSIZE:
 91			if (len >= 5) {
 92				ir->bufsize = ir->buf_in[4];
 93				complete(&ir->completion);
 94			}
 95			break;
 96		case CMD_GET_FEATURES:
 97			if (len > 5) {
 98				ir->cycle_overhead = ir->buf_in[5];
 99				complete(&ir->completion);
100			}
101			break;
102		case CMD_TX_OVERFLOW:
103			ir->tx_overflow = true;
104			fallthrough;
105		case CMD_RECEIVER_OFF:
106		case CMD_RECEIVER_ON:
107		case CMD_SEND:
108			complete(&ir->completion);
109			break;
110		case CMD_RX_OVERFLOW:
111			dev_warn(ir->dev, "receive overflow\n");
112			ir_raw_event_overflow(ir->rc);
113			break;
114		default:
115			dev_warn(ir->dev, "control code %02x received\n",
116							ir->buf_in[3]);
117			break;
118		}
119	} else if (len >= 7) {
120		struct ir_raw_event rawir = {};
121		unsigned i;
122		bool event = false;
123
124		for (i = 0; i < 7; i++) {
125			if (ir->buf_in[i] == 0x80) {
126				rawir.pulse = false;
127				rawir.duration = 21845;
128			} else {
129				rawir.pulse = (ir->buf_in[i] & 0x80) == 0;
130				rawir.duration = ((ir->buf_in[i] & 0x7f) + 1) *
131								 RX_RESOLUTION;
132			}
133
134			if (ir_raw_event_store_with_filter(ir->rc, &rawir))
135				event = true;
136		}
137
138		if (event)
139			ir_raw_event_handle(ir->rc);
140	}
141}
142
143static void iguanair_rx(struct urb *urb)
144{
145	struct iguanair *ir;
146	int rc;
147
148	if (!urb)
149		return;
150
151	ir = urb->context;
152	if (!ir)
153		return;
154
155	switch (urb->status) {
156	case 0:
157		process_ir_data(ir, urb->actual_length);
158		break;
159	case -ECONNRESET:
160	case -ENOENT:
161	case -ESHUTDOWN:
162		return;
163	case -EPIPE:
164	default:
165		dev_dbg(ir->dev, "Error: urb status = %d\n", urb->status);
166		break;
167	}
168
169	rc = usb_submit_urb(urb, GFP_ATOMIC);
170	if (rc && rc != -ENODEV)
171		dev_warn(ir->dev, "failed to resubmit urb: %d\n", rc);
172}
173
174static void iguanair_irq_out(struct urb *urb)
175{
176	struct iguanair *ir = urb->context;
177
178	if (urb->status)
179		dev_dbg(ir->dev, "Error: out urb status = %d\n", urb->status);
180
181	/* if we sent an nop packet, do not expect a response */
182	if (urb->status == 0 && ir->packet->header.cmd == CMD_NOP)
183		complete(&ir->completion);
184}
185
186static int iguanair_send(struct iguanair *ir, unsigned size)
187{
188	int rc;
189
190	reinit_completion(&ir->completion);
191
192	ir->urb_out->transfer_buffer_length = size;
193	rc = usb_submit_urb(ir->urb_out, GFP_KERNEL);
194	if (rc)
195		return rc;
196
197	if (wait_for_completion_timeout(&ir->completion, TIMEOUT) == 0) {
198		usb_kill_urb(ir->urb_out);
199		return -ETIMEDOUT;
200	}
201
202	return rc;
203}
204
205static int iguanair_get_features(struct iguanair *ir)
206{
207	int rc;
208
209	/*
210	 * On cold boot, the iguanair initializes on the first packet
211	 * received but does not process that packet. Send an empty
212	 * packet.
213	 */
214	ir->packet->header.start = 0;
215	ir->packet->header.direction = DIR_OUT;
216	ir->packet->header.cmd = CMD_NOP;
217	iguanair_send(ir, sizeof(ir->packet->header));
218
219	ir->packet->header.cmd = CMD_GET_VERSION;
220	rc = iguanair_send(ir, sizeof(ir->packet->header));
221	if (rc) {
222		dev_info(ir->dev, "failed to get version\n");
223		goto out;
224	}
225
226	if (ir->version < 0x205) {
227		dev_err(ir->dev, "firmware 0x%04x is too old\n", ir->version);
228		rc = -ENODEV;
229		goto out;
230	}
231
232	ir->bufsize = 150;
233	ir->cycle_overhead = 65;
234
235	ir->packet->header.cmd = CMD_GET_BUFSIZE;
236
237	rc = iguanair_send(ir, sizeof(ir->packet->header));
238	if (rc) {
239		dev_info(ir->dev, "failed to get buffer size\n");
240		goto out;
241	}
242
243	if (ir->bufsize > BUF_SIZE) {
244		dev_info(ir->dev, "buffer size %u larger than expected\n",
245								ir->bufsize);
246		ir->bufsize = BUF_SIZE;
247	}
248
249	ir->packet->header.cmd = CMD_GET_FEATURES;
250
251	rc = iguanair_send(ir, sizeof(ir->packet->header));
252	if (rc)
253		dev_info(ir->dev, "failed to get features\n");
254out:
255	return rc;
256}
257
258static int iguanair_receiver(struct iguanair *ir, bool enable)
259{
260	ir->packet->header.start = 0;
261	ir->packet->header.direction = DIR_OUT;
262	ir->packet->header.cmd = enable ? CMD_RECEIVER_ON : CMD_RECEIVER_OFF;
263
264	return iguanair_send(ir, sizeof(ir->packet->header));
265}
266
267/*
268 * The iguanair creates the carrier by busy spinning after each half period.
269 * This is counted in CPU cycles, with the CPU running at 24MHz. It is
270 * broken down into 7-cycles and 4-cyles delays, with a preference for
271 * 4-cycle delays, minus the overhead of the loop itself (cycle_overhead).
272 */
273static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
274{
275	struct iguanair *ir = dev->priv;
276
277	if (carrier < 25000 || carrier > 150000)
278		return -EINVAL;
279
280	if (carrier != ir->carrier) {
281		uint32_t cycles, fours, sevens;
282
283		ir->carrier = carrier;
284
285		cycles = DIV_ROUND_CLOSEST(24000000, carrier * 2) -
286							ir->cycle_overhead;
287
288		/*
289		 * Calculate minimum number of 7 cycles needed so
290		 * we are left with a multiple of 4; so we want to have
291		 * (sevens * 7) & 3 == cycles & 3
292		 */
293		sevens = (4 - cycles) & 3;
294		fours = (cycles - sevens * 7) / 4;
295
296		/*
297		 * The firmware interprets these values as a relative offset
298		 * for a branch. Immediately following the branches, there
299		 * 4 instructions of 7 cycles (2 bytes each) and 110
300		 * instructions of 4 cycles (1 byte each). A relative branch
301		 * of 0 will execute all of them, branch further for less
302		 * cycle burning.
303		 */
304		ir->packet->busy7 = (4 - sevens) * 2;
305		ir->packet->busy4 = 110 - fours;
306	}
307
308	return 0;
309}
310
311static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask)
312{
313	struct iguanair *ir = dev->priv;
314
315	if (mask > 15)
316		return 4;
317
318	ir->packet->channels = mask << 4;
319
320	return 0;
321}
322
323static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
324{
325	struct iguanair *ir = dev->priv;
326	unsigned int i, size, p, periods;
327	int rc;
328
329	/* convert from us to carrier periods */
330	for (i = size = 0; i < count; i++) {
331		periods = DIV_ROUND_CLOSEST(txbuf[i] * ir->carrier, 1000000);
332		while (periods) {
333			p = min(periods, 127u);
334			if (size >= ir->bufsize) {
335				rc = -EINVAL;
336				goto out;
337			}
338			ir->packet->payload[size++] = p | ((i & 1) ? 0x80 : 0);
339			periods -= p;
340		}
341	}
342
343	ir->packet->header.start = 0;
344	ir->packet->header.direction = DIR_OUT;
345	ir->packet->header.cmd = CMD_SEND;
346	ir->packet->length = size;
347
348	ir->tx_overflow = false;
349
350	rc = iguanair_send(ir, sizeof(*ir->packet) + size);
351
352	if (rc == 0 && ir->tx_overflow)
353		rc = -EOVERFLOW;
354
355out:
356	return rc ? rc : count;
357}
358
359static int iguanair_open(struct rc_dev *rdev)
360{
361	struct iguanair *ir = rdev->priv;
362	int rc;
363
364	rc = iguanair_receiver(ir, true);
365	if (rc == 0)
366		ir->receiver_on = true;
367
368	return rc;
369}
370
371static void iguanair_close(struct rc_dev *rdev)
372{
373	struct iguanair *ir = rdev->priv;
374	int rc;
375
376	rc = iguanair_receiver(ir, false);
377	ir->receiver_on = false;
378	if (rc && rc != -ENODEV)
379		dev_warn(ir->dev, "failed to disable receiver: %d\n", rc);
380}
381
382static int iguanair_probe(struct usb_interface *intf,
383			  const struct usb_device_id *id)
384{
385	struct usb_device *udev = interface_to_usbdev(intf);
386	struct iguanair *ir;
387	struct rc_dev *rc;
388	int ret, pipein, pipeout;
389	struct usb_host_interface *idesc;
390
391	idesc = intf->cur_altsetting;
392	if (idesc->desc.bNumEndpoints < 2)
393		return -ENODEV;
394
395	ir = kzalloc(sizeof(*ir), GFP_KERNEL);
396	rc = rc_allocate_device(RC_DRIVER_IR_RAW);
397	if (!ir || !rc) {
398		ret = -ENOMEM;
399		goto out;
400	}
401
402	ir->buf_in = usb_alloc_coherent(udev, MAX_IN_PACKET, GFP_KERNEL,
403								&ir->dma_in);
404	ir->packet = usb_alloc_coherent(udev, MAX_OUT_PACKET, GFP_KERNEL,
405								&ir->dma_out);
406	ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
407	ir->urb_out = usb_alloc_urb(0, GFP_KERNEL);
408
409	if (!ir->buf_in || !ir->packet || !ir->urb_in || !ir->urb_out ||
410	    !usb_endpoint_is_int_in(&idesc->endpoint[0].desc) ||
411	    !usb_endpoint_is_int_out(&idesc->endpoint[1].desc)) {
412		ret = -ENOMEM;
413		goto out;
414	}
415
416	ir->rc = rc;
417	ir->dev = &intf->dev;
418	ir->udev = udev;
419
420	init_completion(&ir->completion);
421	pipeout = usb_sndintpipe(udev,
422				idesc->endpoint[1].desc.bEndpointAddress);
423	usb_fill_int_urb(ir->urb_out, udev, pipeout, ir->packet, MAX_OUT_PACKET,
424						iguanair_irq_out, ir, 1);
425	ir->urb_out->transfer_dma = ir->dma_out;
426	ir->urb_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
427
428	pipein = usb_rcvintpipe(udev, idesc->endpoint[0].desc.bEndpointAddress);
429	usb_fill_int_urb(ir->urb_in, udev, pipein, ir->buf_in, MAX_IN_PACKET,
430							 iguanair_rx, ir, 1);
431	ir->urb_in->transfer_dma = ir->dma_in;
432	ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
433
434	ret = usb_submit_urb(ir->urb_in, GFP_KERNEL);
435	if (ret) {
436		dev_warn(&intf->dev, "failed to submit urb: %d\n", ret);
437		goto out;
438	}
439
440	ret = iguanair_get_features(ir);
441	if (ret)
442		goto out2;
443
444	snprintf(ir->name, sizeof(ir->name),
445		"IguanaWorks USB IR Transceiver version 0x%04x", ir->version);
446
447	usb_make_path(ir->udev, ir->phys, sizeof(ir->phys));
448
449	rc->device_name = ir->name;
450	rc->input_phys = ir->phys;
451	usb_to_input_id(ir->udev, &rc->input_id);
452	rc->dev.parent = &intf->dev;
453	rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
454	rc->priv = ir;
455	rc->open = iguanair_open;
456	rc->close = iguanair_close;
457	rc->s_tx_mask = iguanair_set_tx_mask;
458	rc->s_tx_carrier = iguanair_set_tx_carrier;
459	rc->tx_ir = iguanair_tx;
460	rc->driver_name = KBUILD_MODNAME;
461	rc->map_name = RC_MAP_RC6_MCE;
462	rc->min_timeout = 1;
463	rc->timeout = IR_DEFAULT_TIMEOUT;
464	rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT;
465	rc->rx_resolution = RX_RESOLUTION;
466
467	iguanair_set_tx_carrier(rc, 38000);
468	iguanair_set_tx_mask(rc, 0);
469
470	ret = rc_register_device(rc);
471	if (ret < 0) {
472		dev_err(&intf->dev, "failed to register rc device %d", ret);
473		goto out2;
474	}
475
476	usb_set_intfdata(intf, ir);
477
478	return 0;
479out2:
480	usb_kill_urb(ir->urb_in);
481	usb_kill_urb(ir->urb_out);
482out:
483	if (ir) {
484		usb_free_urb(ir->urb_in);
485		usb_free_urb(ir->urb_out);
486		usb_free_coherent(udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
487		usb_free_coherent(udev, MAX_OUT_PACKET, ir->packet,
488								ir->dma_out);
489	}
490	rc_free_device(rc);
491	kfree(ir);
492	return ret;
493}
494
495static void iguanair_disconnect(struct usb_interface *intf)
496{
497	struct iguanair *ir = usb_get_intfdata(intf);
498
499	rc_unregister_device(ir->rc);
500	usb_set_intfdata(intf, NULL);
501	usb_kill_urb(ir->urb_in);
502	usb_kill_urb(ir->urb_out);
503	usb_free_urb(ir->urb_in);
504	usb_free_urb(ir->urb_out);
505	usb_free_coherent(ir->udev, MAX_IN_PACKET, ir->buf_in, ir->dma_in);
506	usb_free_coherent(ir->udev, MAX_OUT_PACKET, ir->packet, ir->dma_out);
507	kfree(ir);
508}
509
510static int iguanair_suspend(struct usb_interface *intf, pm_message_t message)
511{
512	struct iguanair *ir = usb_get_intfdata(intf);
513	int rc = 0;
514
515	if (ir->receiver_on) {
516		rc = iguanair_receiver(ir, false);
517		if (rc)
518			dev_warn(ir->dev, "failed to disable receiver for suspend\n");
519	}
520
521	usb_kill_urb(ir->urb_in);
522	usb_kill_urb(ir->urb_out);
523
524	return rc;
525}
526
527static int iguanair_resume(struct usb_interface *intf)
528{
529	struct iguanair *ir = usb_get_intfdata(intf);
530	int rc;
531
532	rc = usb_submit_urb(ir->urb_in, GFP_KERNEL);
533	if (rc)
534		dev_warn(&intf->dev, "failed to submit urb: %d\n", rc);
535
536	if (ir->receiver_on) {
537		rc = iguanair_receiver(ir, true);
538		if (rc)
539			dev_warn(ir->dev, "failed to enable receiver after resume\n");
540	}
541
542	return rc;
543}
544
545static const struct usb_device_id iguanair_table[] = {
546	{ USB_DEVICE(0x1781, 0x0938) },
547	{ }
548};
549
550static struct usb_driver iguanair_driver = {
551	.name =	KBUILD_MODNAME,
552	.probe = iguanair_probe,
553	.disconnect = iguanair_disconnect,
554	.suspend = iguanair_suspend,
555	.resume = iguanair_resume,
556	.reset_resume = iguanair_resume,
557	.id_table = iguanair_table,
558	.soft_unbind = 1	/* we want to disable receiver on unbind */
559};
560
561module_usb_driver(iguanair_driver);
562
563MODULE_DESCRIPTION("IguanaWorks USB IR Transceiver");
564MODULE_AUTHOR("Sean Young <sean@mess.org>");
565MODULE_LICENSE("GPL");
566MODULE_DEVICE_TABLE(usb, iguanair_table);
567
~/ Read-only file preview.