Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[media] rc: port IgorPlug-USB to rc-core

This is a complete re-write inspired by the original lirc driver.

Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>

authored by

Sean Young and committed by
Mauro Carvalho Chehab
b1c97193 4954578e

+283
+6
MAINTAINERS
··· 4695 4695 F: drivers/net/ieee802154/ 4696 4696 F: Documentation/networking/ieee802154.txt 4697 4697 4698 + IGORPLUG-USB IR RECEIVER 4699 + M: Sean Young <sean@mess.org> 4700 + L: linux-media@vger.kernel.org 4701 + S: Maintained 4702 + F: drivers/media/rc/igorplugusb.c 4703 + 4698 4704 IGUANAWORKS USB IR TRANSCEIVER 4699 4705 M: Sean Young <sean@mess.org> 4700 4706 L: linux-media@vger.kernel.org
+15
drivers/media/rc/Kconfig
··· 277 277 To compile this driver as a module, choose M here: the module will 278 278 be called winbond_cir. 279 279 280 + config IR_IGORPLUGUSB 281 + tristate "IgorPlug-USB IR Receiver" 282 + depends on USB_ARCH_HAS_HCD 283 + depends on RC_CORE 284 + select USB 285 + ---help--- 286 + Say Y here if you want to use the IgorPlug-USB IR Receiver by 287 + Igor Cesko. This device is included on the Fit-PC2. 288 + 289 + Note that this device can only record bursts of 36 IR pulses and 290 + spaces, which is not enough for the NEC, Sanyo and RC-6 protocol. 291 + 292 + To compile this driver as a module, choose M here: the module will 293 + be called igorplugusb. 294 + 280 295 config IR_IGUANA 281 296 tristate "IguanaWorks USB IR Transceiver" 282 297 depends on USB_ARCH_HAS_HCD
+1
drivers/media/rc/Makefile
··· 30 30 obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o 31 31 obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o 32 32 obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o 33 + obj-$(CONFIG_IR_IGORPLUGUSB) += igorplugusb.o 33 34 obj-$(CONFIG_IR_IGUANA) += iguanair.o 34 35 obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o 35 36 obj-$(CONFIG_RC_ST) += st_rc.o
+261
drivers/media/rc/igorplugusb.c
··· 1 + /* 2 + * IgorPlug-USB IR Receiver 3 + * 4 + * Copyright (C) 2014 Sean Young <sean@mess.org> 5 + * 6 + * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware. 7 + * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm 8 + * 9 + * Based on the lirc_igorplugusb.c driver: 10 + * Copyright (C) 2004 Jan M. Hochstein 11 + * <hochstein@algo.informatik.tu-darmstadt.de> 12 + * 13 + * This program is free software; you can redistribute it and/or modify 14 + * it under the terms of the GNU General Public License as published by 15 + * the Free Software Foundation; either version 2 of the License, or 16 + * (at your option) any later version. 17 + * 18 + * This program is distributed in the hope that it will be useful, 19 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 + * GNU General Public License for more details. 22 + */ 23 + #include <linux/device.h> 24 + #include <linux/kernel.h> 25 + #include <linux/module.h> 26 + #include <linux/usb.h> 27 + #include <linux/usb/input.h> 28 + #include <media/rc-core.h> 29 + 30 + #define DRIVER_DESC "IgorPlug-USB IR Receiver" 31 + #define DRIVER_NAME "igorplugusb" 32 + 33 + #define HEADERLEN 3 34 + #define BUFLEN 36 35 + #define MAX_PACKET (HEADERLEN + BUFLEN) 36 + 37 + #define SET_INFRABUFFER_EMPTY 1 38 + #define GET_INFRACODE 2 39 + 40 + 41 + struct igorplugusb { 42 + struct rc_dev *rc; 43 + struct device *dev; 44 + 45 + struct urb *urb; 46 + struct usb_ctrlrequest request; 47 + 48 + struct timer_list timer; 49 + 50 + uint8_t buf_in[MAX_PACKET]; 51 + 52 + char phys[64]; 53 + }; 54 + 55 + static void igorplugusb_cmd(struct igorplugusb *ir, int cmd); 56 + 57 + static void igorplugusb_irdata(struct igorplugusb *ir, unsigned len) 58 + { 59 + DEFINE_IR_RAW_EVENT(rawir); 60 + unsigned i, start, overflow; 61 + 62 + dev_dbg(ir->dev, "irdata: %*ph (len=%u)", len, ir->buf_in, len); 63 + 64 + /* 65 + * If more than 36 pulses and spaces follow each other, the igorplugusb 66 + * overwrites its buffer from the beginning. The overflow value is the 67 + * last offset which was not overwritten. Everything from this offset 68 + * onwards occurred before everything until this offset. 69 + */ 70 + overflow = ir->buf_in[2]; 71 + i = start = overflow + HEADERLEN; 72 + 73 + if (start >= len) { 74 + dev_err(ir->dev, "receive overflow invalid: %u", overflow); 75 + } else { 76 + if (overflow > 0) 77 + dev_warn(ir->dev, "receive overflow, at least %u lost", 78 + overflow); 79 + 80 + do { 81 + rawir.duration = ir->buf_in[i] * 85333; 82 + rawir.pulse = i & 1; 83 + 84 + ir_raw_event_store_with_filter(ir->rc, &rawir); 85 + 86 + if (++i == len) 87 + i = HEADERLEN; 88 + } while (i != start); 89 + 90 + /* add a trailing space */ 91 + rawir.duration = ir->rc->timeout; 92 + rawir.pulse = false; 93 + ir_raw_event_store_with_filter(ir->rc, &rawir); 94 + 95 + ir_raw_event_handle(ir->rc); 96 + } 97 + 98 + igorplugusb_cmd(ir, SET_INFRABUFFER_EMPTY); 99 + } 100 + 101 + static void igorplugusb_callback(struct urb *urb) 102 + { 103 + struct usb_ctrlrequest *req; 104 + struct igorplugusb *ir = urb->context; 105 + 106 + req = (struct usb_ctrlrequest *)urb->setup_packet; 107 + 108 + switch (urb->status) { 109 + case 0: 110 + if (req->bRequest == GET_INFRACODE && 111 + urb->actual_length > HEADERLEN) 112 + igorplugusb_irdata(ir, urb->actual_length); 113 + else /* request IR */ 114 + mod_timer(&ir->timer, jiffies + msecs_to_jiffies(50)); 115 + break; 116 + case -EPROTO: 117 + case -ECONNRESET: 118 + case -ENOENT: 119 + case -ESHUTDOWN: 120 + usb_unlink_urb(urb); 121 + return; 122 + default: 123 + dev_warn(ir->dev, "Error: urb status = %d\n", urb->status); 124 + igorplugusb_cmd(ir, SET_INFRABUFFER_EMPTY); 125 + break; 126 + } 127 + } 128 + 129 + static void igorplugusb_cmd(struct igorplugusb *ir, int cmd) 130 + { 131 + int ret; 132 + 133 + ir->request.bRequest = cmd; 134 + ir->urb->transfer_flags = 0; 135 + ret = usb_submit_urb(ir->urb, GFP_ATOMIC); 136 + if (ret) 137 + dev_err(ir->dev, "submit urb failed: %d", ret); 138 + } 139 + 140 + static void igorplugusb_timer(unsigned long data) 141 + { 142 + struct igorplugusb *ir = (struct igorplugusb *)data; 143 + 144 + igorplugusb_cmd(ir, GET_INFRACODE); 145 + } 146 + 147 + static int igorplugusb_probe(struct usb_interface *intf, 148 + const struct usb_device_id *id) 149 + { 150 + struct usb_device *udev; 151 + struct usb_host_interface *idesc; 152 + struct usb_endpoint_descriptor *ep; 153 + struct igorplugusb *ir; 154 + struct rc_dev *rc; 155 + int ret; 156 + 157 + udev = interface_to_usbdev(intf); 158 + idesc = intf->cur_altsetting; 159 + 160 + if (idesc->desc.bNumEndpoints != 1) { 161 + dev_err(&intf->dev, "incorrect number of endpoints"); 162 + return -ENODEV; 163 + } 164 + 165 + ep = &idesc->endpoint[0].desc; 166 + if (!usb_endpoint_dir_in(ep) || !usb_endpoint_xfer_control(ep)) { 167 + dev_err(&intf->dev, "endpoint incorrect"); 168 + return -ENODEV; 169 + } 170 + 171 + ir = devm_kzalloc(&intf->dev, sizeof(*ir), GFP_KERNEL); 172 + if (!ir) 173 + return -ENOMEM; 174 + 175 + ir->dev = &intf->dev; 176 + 177 + setup_timer(&ir->timer, igorplugusb_timer, (unsigned long)ir); 178 + 179 + ir->request.bRequest = GET_INFRACODE; 180 + ir->request.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN; 181 + ir->request.wLength = cpu_to_le16(sizeof(ir->buf_in)); 182 + 183 + ir->urb = usb_alloc_urb(0, GFP_KERNEL); 184 + if (!ir->urb) 185 + return -ENOMEM; 186 + 187 + usb_fill_control_urb(ir->urb, udev, 188 + usb_rcvctrlpipe(udev, 0), (uint8_t *)&ir->request, 189 + ir->buf_in, sizeof(ir->buf_in), igorplugusb_callback, ir); 190 + 191 + usb_make_path(udev, ir->phys, sizeof(ir->phys)); 192 + 193 + rc = rc_allocate_device(); 194 + rc->input_name = DRIVER_DESC; 195 + rc->input_phys = ir->phys; 196 + usb_to_input_id(udev, &rc->input_id); 197 + rc->dev.parent = &intf->dev; 198 + rc->driver_type = RC_DRIVER_IR_RAW; 199 + /* 200 + * This device can only store 36 pulses + spaces, which is not enough 201 + * for the NEC protocol and many others. 202 + */ 203 + rc->allowed_protocols = RC_BIT_ALL & ~(RC_BIT_NEC | RC_BIT_RC6_6A_20 | 204 + RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | 205 + RC_BIT_SONY20 | RC_BIT_MCE_KBD | RC_BIT_SANYO); 206 + 207 + rc->priv = ir; 208 + rc->driver_name = DRIVER_NAME; 209 + rc->map_name = RC_MAP_HAUPPAUGE; 210 + rc->timeout = MS_TO_NS(100); 211 + rc->rx_resolution = 85333; 212 + 213 + ir->rc = rc; 214 + ret = rc_register_device(rc); 215 + if (ret) { 216 + dev_err(&intf->dev, "failed to register rc device: %d", ret); 217 + rc_free_device(rc); 218 + usb_free_urb(ir->urb); 219 + return ret; 220 + } 221 + 222 + usb_set_intfdata(intf, ir); 223 + 224 + igorplugusb_cmd(ir, SET_INFRABUFFER_EMPTY); 225 + 226 + return 0; 227 + } 228 + 229 + static void igorplugusb_disconnect(struct usb_interface *intf) 230 + { 231 + struct igorplugusb *ir = usb_get_intfdata(intf); 232 + 233 + rc_unregister_device(ir->rc); 234 + del_timer_sync(&ir->timer); 235 + usb_set_intfdata(intf, NULL); 236 + usb_kill_urb(ir->urb); 237 + usb_free_urb(ir->urb); 238 + } 239 + 240 + static struct usb_device_id igorplugusb_table[] = { 241 + /* Igor Plug USB (Atmel's Manufact. ID) */ 242 + { USB_DEVICE(0x03eb, 0x0002) }, 243 + /* Fit PC2 Infrared Adapter */ 244 + { USB_DEVICE(0x03eb, 0x21fe) }, 245 + /* Terminating entry */ 246 + { } 247 + }; 248 + 249 + static struct usb_driver igorplugusb_driver = { 250 + .name = DRIVER_NAME, 251 + .probe = igorplugusb_probe, 252 + .disconnect = igorplugusb_disconnect, 253 + .id_table = igorplugusb_table 254 + }; 255 + 256 + module_usb_driver(igorplugusb_driver); 257 + 258 + MODULE_DESCRIPTION(DRIVER_DESC); 259 + MODULE_AUTHOR("Sean Young <sean@mess.org>"); 260 + MODULE_LICENSE("GPL"); 261 + MODULE_DEVICE_TABLE(usb, igorplugusb_table);