Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 1 | /* |
| 2 | * lirc_igorplugusb - USB remote support for LIRC |
| 3 | * |
| 4 | * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware. |
| 5 | * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm |
| 6 | * |
| 7 | * The device can only record bursts of up to 36 pulses/spaces. |
| 8 | * Works fine with RC5. Longer commands lead to device buffer overrun. |
| 9 | * (Maybe a better firmware or a microcontroller with more ram can help?) |
| 10 | * |
| 11 | * Version 0.1 [beta status] |
| 12 | * |
| 13 | * Copyright (C) 2004 Jan M. Hochstein |
| 14 | * <hochstein@algo.informatik.tu-darmstadt.de> |
| 15 | * |
| 16 | * This driver was derived from: |
| 17 | * Paul Miller <pmiller9@users.sourceforge.net> |
| 18 | * "lirc_atiusb" module |
| 19 | * Vladimir Dergachev <volodya@minspring.com>'s 2002 |
| 20 | * "USB ATI Remote support" (input device) |
| 21 | * Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002 |
| 22 | * "USB StreamZap remote driver" (LIRC) |
| 23 | * Artur Lipowski <alipowski@kki.net.pl>'s 2002 |
| 24 | * "lirc_dev" and "lirc_gpio" LIRC modules |
| 25 | */ |
| 26 | |
| 27 | /* |
| 28 | * This program is free software; you can redistribute it and/or modify |
| 29 | * it under the terms of the GNU General Public License as published by |
| 30 | * the Free Software Foundation; either version 2 of the License, or |
| 31 | * (at your option) any later version. |
| 32 | * |
| 33 | * This program is distributed in the hope that it will be useful, |
| 34 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 35 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 36 | * GNU General Public License for more details. |
| 37 | * |
| 38 | * You should have received a copy of the GNU General Public License |
| 39 | * along with this program; if not, write to the Free Software |
| 40 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 41 | */ |
| 42 | |
| 43 | #include <linux/module.h> |
| 44 | #include <linux/kernel.h> |
| 45 | #include <linux/kmod.h> |
| 46 | #include <linux/sched.h> |
| 47 | #include <linux/errno.h> |
| 48 | #include <linux/fs.h> |
| 49 | #include <linux/usb.h> |
| 50 | #include <linux/time.h> |
| 51 | |
| 52 | #include <media/lirc.h> |
| 53 | #include <media/lirc_dev.h> |
| 54 | |
| 55 | |
| 56 | /* module identification */ |
Jarod Wilson | d653c0a | 2010-10-19 14:25:29 -0300 | [diff] [blame] | 57 | #define DRIVER_VERSION "0.2" |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 58 | #define DRIVER_AUTHOR \ |
| 59 | "Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>" |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 60 | #define DRIVER_DESC "Igorplug USB remote driver for LIRC" |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 61 | #define DRIVER_NAME "lirc_igorplugusb" |
| 62 | |
| 63 | /* debugging support */ |
| 64 | #ifdef CONFIG_USB_DEBUG |
Rusty Russell | 90ab5ee | 2012-01-13 09:32:20 +1030 | [diff] [blame] | 65 | static bool debug = 1; |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 66 | #else |
Rusty Russell | 90ab5ee | 2012-01-13 09:32:20 +1030 | [diff] [blame] | 67 | static bool debug; |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 68 | #endif |
| 69 | |
| 70 | #define dprintk(fmt, args...) \ |
| 71 | do { \ |
| 72 | if (debug) \ |
| 73 | printk(KERN_DEBUG fmt, ## args); \ |
| 74 | } while (0) |
| 75 | |
| 76 | /* One mode2 pulse/space has 4 bytes. */ |
| 77 | #define CODE_LENGTH sizeof(int) |
| 78 | |
| 79 | /* Igor's firmware cannot record bursts longer than 36. */ |
| 80 | #define DEVICE_BUFLEN 36 |
| 81 | |
| 82 | /* |
| 83 | * Header at the beginning of the device's buffer: |
| 84 | * unsigned char data_length |
| 85 | * unsigned char data_start (!=0 means ring-buffer overrun) |
| 86 | * unsigned char counter (incremented by each burst) |
| 87 | */ |
| 88 | #define DEVICE_HEADERLEN 3 |
| 89 | |
| 90 | /* This is for the gap */ |
| 91 | #define ADDITIONAL_LIRC_BYTES 2 |
| 92 | |
| 93 | /* times to poll per second */ |
| 94 | #define SAMPLE_RATE 100 |
| 95 | static int sample_rate = SAMPLE_RATE; |
| 96 | |
| 97 | |
| 98 | /**** Igor's USB Request Codes */ |
| 99 | |
| 100 | #define SET_INFRABUFFER_EMPTY 1 |
| 101 | /** |
| 102 | * Params: none |
| 103 | * Answer: empty |
| 104 | */ |
| 105 | |
| 106 | #define GET_INFRACODE 2 |
| 107 | /** |
| 108 | * Params: |
| 109 | * wValue: offset to begin reading infra buffer |
| 110 | * |
| 111 | * Answer: infra data |
| 112 | */ |
| 113 | |
| 114 | #define SET_DATAPORT_DIRECTION 3 |
| 115 | /** |
| 116 | * Params: |
| 117 | * wValue: (byte) 1 bit for each data port pin (0=in, 1=out) |
| 118 | * |
| 119 | * Answer: empty |
| 120 | */ |
| 121 | |
| 122 | #define GET_DATAPORT_DIRECTION 4 |
| 123 | /** |
| 124 | * Params: none |
| 125 | * |
| 126 | * Answer: (byte) 1 bit for each data port pin (0=in, 1=out) |
| 127 | */ |
| 128 | |
| 129 | #define SET_OUT_DATAPORT 5 |
| 130 | /** |
| 131 | * Params: |
| 132 | * wValue: byte to write to output data port |
| 133 | * |
| 134 | * Answer: empty |
| 135 | */ |
| 136 | |
| 137 | #define GET_OUT_DATAPORT 6 |
| 138 | /** |
| 139 | * Params: none |
| 140 | * |
| 141 | * Answer: least significant 3 bits read from output data port |
| 142 | */ |
| 143 | |
| 144 | #define GET_IN_DATAPORT 7 |
| 145 | /** |
| 146 | * Params: none |
| 147 | * |
| 148 | * Answer: least significant 3 bits read from input data port |
| 149 | */ |
| 150 | |
| 151 | #define READ_EEPROM 8 |
| 152 | /** |
| 153 | * Params: |
| 154 | * wValue: offset to begin reading EEPROM |
| 155 | * |
| 156 | * Answer: EEPROM bytes |
| 157 | */ |
| 158 | |
| 159 | #define WRITE_EEPROM 9 |
| 160 | /** |
| 161 | * Params: |
| 162 | * wValue: offset to EEPROM byte |
| 163 | * wIndex: byte to write |
| 164 | * |
| 165 | * Answer: empty |
| 166 | */ |
| 167 | |
| 168 | #define SEND_RS232 10 |
| 169 | /** |
| 170 | * Params: |
| 171 | * wValue: byte to send |
| 172 | * |
| 173 | * Answer: empty |
| 174 | */ |
| 175 | |
| 176 | #define RECV_RS232 11 |
| 177 | /** |
| 178 | * Params: none |
| 179 | * |
| 180 | * Answer: byte received |
| 181 | */ |
| 182 | |
| 183 | #define SET_RS232_BAUD 12 |
| 184 | /** |
| 185 | * Params: |
| 186 | * wValue: byte to write to UART bit rate register (UBRR) |
| 187 | * |
| 188 | * Answer: empty |
| 189 | */ |
| 190 | |
| 191 | #define GET_RS232_BAUD 13 |
| 192 | /** |
| 193 | * Params: none |
| 194 | * |
| 195 | * Answer: byte read from UART bit rate register (UBRR) |
| 196 | */ |
| 197 | |
| 198 | |
| 199 | /* data structure for each usb remote */ |
| 200 | struct igorplug { |
| 201 | |
| 202 | /* usb */ |
| 203 | struct usb_device *usbdev; |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 204 | int devnum; |
| 205 | |
| 206 | unsigned char *buf_in; |
| 207 | unsigned int len_in; |
| 208 | int in_space; |
| 209 | struct timeval last_time; |
| 210 | |
| 211 | dma_addr_t dma_in; |
| 212 | |
| 213 | /* lirc */ |
| 214 | struct lirc_driver *d; |
| 215 | |
| 216 | /* handle sending (init strings) */ |
| 217 | int send_flags; |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 218 | }; |
| 219 | |
| 220 | static int unregister_from_lirc(struct igorplug *ir) |
| 221 | { |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 222 | struct lirc_driver *d; |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 223 | int devnum; |
| 224 | |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 225 | if (!ir) { |
| 226 | printk(KERN_ERR "%s: called with NULL device struct!\n", |
| 227 | __func__); |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 228 | return -EINVAL; |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 229 | } |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 230 | |
| 231 | devnum = ir->devnum; |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 232 | d = ir->d; |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 233 | |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 234 | if (!d) { |
| 235 | printk(KERN_ERR "%s: called with NULL lirc driver struct!\n", |
| 236 | __func__); |
| 237 | return -EINVAL; |
| 238 | } |
| 239 | |
| 240 | dprintk(DRIVER_NAME "[%d]: calling lirc_unregister_driver\n", devnum); |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 241 | lirc_unregister_driver(d->minor); |
| 242 | |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 243 | kfree(d); |
| 244 | ir->d = NULL; |
| 245 | kfree(ir); |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 246 | |
| 247 | return devnum; |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 248 | } |
| 249 | |
| 250 | static int set_use_inc(void *data) |
| 251 | { |
| 252 | struct igorplug *ir = data; |
| 253 | |
| 254 | if (!ir) { |
| 255 | printk(DRIVER_NAME "[?]: set_use_inc called with no context\n"); |
| 256 | return -EIO; |
| 257 | } |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 258 | |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 259 | dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum); |
| 260 | |
| 261 | if (!ir->usbdev) |
| 262 | return -ENODEV; |
| 263 | |
| 264 | return 0; |
| 265 | } |
| 266 | |
| 267 | static void set_use_dec(void *data) |
| 268 | { |
| 269 | struct igorplug *ir = data; |
| 270 | |
| 271 | if (!ir) { |
| 272 | printk(DRIVER_NAME "[?]: set_use_dec called with no context\n"); |
| 273 | return; |
| 274 | } |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 275 | |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 276 | dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum); |
| 277 | } |
| 278 | |
Jarod Wilson | d653c0a | 2010-10-19 14:25:29 -0300 | [diff] [blame] | 279 | static void send_fragment(struct igorplug *ir, struct lirc_buffer *buf, |
| 280 | int i, int max) |
| 281 | { |
| 282 | int code; |
| 283 | |
| 284 | /* MODE2: pulse/space (PULSE_BIT) in 1us units */ |
| 285 | while (i < max) { |
| 286 | /* 1 Igor-tick = 85.333333 us */ |
| 287 | code = (unsigned int)ir->buf_in[i] * 85 + |
| 288 | (unsigned int)ir->buf_in[i] / 3; |
| 289 | ir->last_time.tv_usec += code; |
| 290 | if (ir->in_space) |
| 291 | code |= PULSE_BIT; |
| 292 | lirc_buffer_write(buf, (unsigned char *)&code); |
| 293 | /* 1 chunk = CODE_LENGTH bytes */ |
| 294 | ir->in_space ^= 1; |
| 295 | ++i; |
| 296 | } |
| 297 | } |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 298 | |
| 299 | /** |
| 300 | * Called in user context. |
| 301 | * return 0 if data was added to the buffer and |
| 302 | * -ENODATA if none was available. This should add some number of bits |
| 303 | * evenly divisible by code_length to the buffer |
| 304 | */ |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 305 | static int igorplugusb_remote_poll(void *data, struct lirc_buffer *buf) |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 306 | { |
| 307 | int ret; |
| 308 | struct igorplug *ir = (struct igorplug *)data; |
| 309 | |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 310 | if (!ir || !ir->usbdev) /* Has the device been removed? */ |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 311 | return -ENODEV; |
| 312 | |
| 313 | memset(ir->buf_in, 0, ir->len_in); |
| 314 | |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 315 | ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), |
| 316 | GET_INFRACODE, USB_TYPE_VENDOR | USB_DIR_IN, |
| 317 | 0/* offset */, /*unused*/0, |
| 318 | ir->buf_in, ir->len_in, |
| 319 | /*timeout*/HZ * USB_CTRL_GET_TIMEOUT); |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 320 | if (ret > 0) { |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 321 | int code, timediff; |
| 322 | struct timeval now; |
| 323 | |
Jarod Wilson | d653c0a | 2010-10-19 14:25:29 -0300 | [diff] [blame] | 324 | /* ACK packet has 1 byte --> ignore */ |
| 325 | if (ret < DEVICE_HEADERLEN) |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 326 | return -ENODATA; |
| 327 | |
| 328 | dprintk(DRIVER_NAME ": Got %d bytes. Header: %02x %02x %02x\n", |
| 329 | ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]); |
| 330 | |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 331 | do_gettimeofday(&now); |
| 332 | timediff = now.tv_sec - ir->last_time.tv_sec; |
| 333 | if (timediff + 1 > PULSE_MASK / 1000000) |
| 334 | timediff = PULSE_MASK; |
| 335 | else { |
| 336 | timediff *= 1000000; |
| 337 | timediff += now.tv_usec - ir->last_time.tv_usec; |
| 338 | } |
| 339 | ir->last_time.tv_sec = now.tv_sec; |
| 340 | ir->last_time.tv_usec = now.tv_usec; |
| 341 | |
| 342 | /* create leading gap */ |
| 343 | code = timediff; |
| 344 | lirc_buffer_write(buf, (unsigned char *)&code); |
| 345 | ir->in_space = 1; /* next comes a pulse */ |
| 346 | |
Jarod Wilson | d653c0a | 2010-10-19 14:25:29 -0300 | [diff] [blame] | 347 | if (ir->buf_in[2] == 0) |
| 348 | send_fragment(ir, buf, DEVICE_HEADERLEN, ret); |
| 349 | else { |
| 350 | printk(KERN_WARNING DRIVER_NAME |
| 351 | "[%d]: Device buffer overrun.\n", ir->devnum); |
| 352 | /* HHHNNNNNNNNNNNOOOOOOOO H = header |
| 353 | <---[2]---> N = newer |
| 354 | <---------ret--------> O = older */ |
| 355 | ir->buf_in[2] %= ret - DEVICE_HEADERLEN; /* sanitize */ |
| 356 | /* keep even-ness to not desync pulse/pause */ |
| 357 | send_fragment(ir, buf, DEVICE_HEADERLEN + |
| 358 | ir->buf_in[2] - (ir->buf_in[2] & 1), ret); |
| 359 | send_fragment(ir, buf, DEVICE_HEADERLEN, |
| 360 | DEVICE_HEADERLEN + ir->buf_in[2]); |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 361 | } |
| 362 | |
| 363 | ret = usb_control_msg( |
| 364 | ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), |
| 365 | SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN, |
| 366 | /*unused*/0, /*unused*/0, |
| 367 | /*dummy*/ir->buf_in, /*dummy*/ir->len_in, |
| 368 | /*timeout*/HZ * USB_CTRL_GET_TIMEOUT); |
| 369 | if (ret < 0) |
| 370 | printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: " |
| 371 | "error %d\n", ir->devnum, ret); |
| 372 | return 0; |
| 373 | } else if (ret < 0) |
| 374 | printk(DRIVER_NAME "[%d]: GET_INFRACODE: error %d\n", |
| 375 | ir->devnum, ret); |
| 376 | |
| 377 | return -ENODATA; |
| 378 | } |
| 379 | |
| 380 | |
| 381 | |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 382 | static int igorplugusb_remote_probe(struct usb_interface *intf, |
| 383 | const struct usb_device_id *id) |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 384 | { |
| 385 | struct usb_device *dev = NULL; |
| 386 | struct usb_host_interface *idesc = NULL; |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 387 | struct usb_endpoint_descriptor *ep; |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 388 | struct igorplug *ir = NULL; |
| 389 | struct lirc_driver *driver = NULL; |
| 390 | int devnum, pipe, maxp; |
| 391 | int minor = 0; |
| 392 | char buf[63], name[128] = ""; |
| 393 | int mem_failure = 0; |
| 394 | int ret; |
| 395 | |
| 396 | dprintk(DRIVER_NAME ": usb probe called.\n"); |
| 397 | |
| 398 | dev = interface_to_usbdev(intf); |
| 399 | |
| 400 | idesc = intf->cur_altsetting; |
| 401 | |
| 402 | if (idesc->desc.bNumEndpoints != 1) |
| 403 | return -ENODEV; |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 404 | |
| 405 | ep = &idesc->endpoint->desc; |
| 406 | if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 407 | != USB_DIR_IN) |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 408 | || (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 409 | != USB_ENDPOINT_XFER_CONTROL) |
| 410 | return -ENODEV; |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 411 | |
| 412 | pipe = usb_rcvctrlpipe(dev, ep->bEndpointAddress); |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 413 | devnum = dev->devnum; |
| 414 | maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); |
| 415 | |
Mauro Carvalho Chehab | 2b361ab | 2010-10-07 09:36:43 -0300 | [diff] [blame] | 416 | dprintk(DRIVER_NAME "[%d]: bytes_in_key=%zu maxp=%d\n", |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 417 | devnum, CODE_LENGTH, maxp); |
| 418 | |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 419 | mem_failure = 0; |
| 420 | ir = kzalloc(sizeof(struct igorplug), GFP_KERNEL); |
| 421 | if (!ir) { |
| 422 | mem_failure = 1; |
| 423 | goto mem_failure_switch; |
| 424 | } |
| 425 | driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); |
| 426 | if (!driver) { |
| 427 | mem_failure = 2; |
| 428 | goto mem_failure_switch; |
| 429 | } |
| 430 | |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 431 | ir->buf_in = usb_alloc_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN, |
| 432 | GFP_ATOMIC, &ir->dma_in); |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 433 | if (!ir->buf_in) { |
| 434 | mem_failure = 3; |
| 435 | goto mem_failure_switch; |
| 436 | } |
| 437 | |
| 438 | strcpy(driver->name, DRIVER_NAME " "); |
| 439 | driver->minor = -1; |
| 440 | driver->code_length = CODE_LENGTH * 8; /* in bits */ |
| 441 | driver->features = LIRC_CAN_REC_MODE2; |
| 442 | driver->data = ir; |
| 443 | driver->chunk_size = CODE_LENGTH; |
| 444 | driver->buffer_size = DEVICE_BUFLEN + ADDITIONAL_LIRC_BYTES; |
| 445 | driver->set_use_inc = &set_use_inc; |
| 446 | driver->set_use_dec = &set_use_dec; |
| 447 | driver->sample_rate = sample_rate; /* per second */ |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 448 | driver->add_to_buf = &igorplugusb_remote_poll; |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 449 | driver->dev = &intf->dev; |
| 450 | driver->owner = THIS_MODULE; |
| 451 | |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 452 | minor = lirc_register_driver(driver); |
| 453 | if (minor < 0) |
| 454 | mem_failure = 9; |
| 455 | |
| 456 | mem_failure_switch: |
| 457 | |
| 458 | switch (mem_failure) { |
| 459 | case 9: |
Jarod Wilson | d653c0a | 2010-10-19 14:25:29 -0300 | [diff] [blame] | 460 | usb_free_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN, |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 461 | ir->buf_in, ir->dma_in); |
| 462 | case 3: |
| 463 | kfree(driver); |
| 464 | case 2: |
| 465 | kfree(ir); |
| 466 | case 1: |
| 467 | printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n", |
| 468 | devnum, mem_failure); |
| 469 | return -ENOMEM; |
| 470 | } |
| 471 | |
| 472 | driver->minor = minor; |
| 473 | ir->d = driver; |
| 474 | ir->devnum = devnum; |
| 475 | ir->usbdev = dev; |
Jarod Wilson | d653c0a | 2010-10-19 14:25:29 -0300 | [diff] [blame] | 476 | ir->len_in = DEVICE_BUFLEN + DEVICE_HEADERLEN; |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 477 | ir->in_space = 1; /* First mode2 event is a space. */ |
| 478 | do_gettimeofday(&ir->last_time); |
| 479 | |
| 480 | if (dev->descriptor.iManufacturer |
| 481 | && usb_string(dev, dev->descriptor.iManufacturer, |
| 482 | buf, sizeof(buf)) > 0) |
| 483 | strlcpy(name, buf, sizeof(name)); |
| 484 | if (dev->descriptor.iProduct |
| 485 | && usb_string(dev, dev->descriptor.iProduct, buf, sizeof(buf)) > 0) |
| 486 | snprintf(name + strlen(name), sizeof(name) - strlen(name), |
| 487 | " %s", buf); |
| 488 | printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name, |
| 489 | dev->bus->busnum, devnum); |
| 490 | |
| 491 | /* clear device buffer */ |
| 492 | ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), |
| 493 | SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN, |
| 494 | /*unused*/0, /*unused*/0, |
| 495 | /*dummy*/ir->buf_in, /*dummy*/ir->len_in, |
| 496 | /*timeout*/HZ * USB_CTRL_GET_TIMEOUT); |
| 497 | if (ret < 0) |
| 498 | printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n", |
| 499 | devnum, ret); |
| 500 | |
| 501 | usb_set_intfdata(intf, ir); |
| 502 | return 0; |
| 503 | } |
| 504 | |
| 505 | |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 506 | static void igorplugusb_remote_disconnect(struct usb_interface *intf) |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 507 | { |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 508 | struct usb_device *usbdev = interface_to_usbdev(intf); |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 509 | struct igorplug *ir = usb_get_intfdata(intf); |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 510 | struct device *dev = &intf->dev; |
| 511 | int devnum; |
| 512 | |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 513 | usb_set_intfdata(intf, NULL); |
| 514 | |
| 515 | if (!ir || !ir->d) |
| 516 | return; |
| 517 | |
| 518 | ir->usbdev = NULL; |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 519 | |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 520 | usb_free_coherent(usbdev, ir->len_in, ir->buf_in, ir->dma_in); |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 521 | |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 522 | devnum = unregister_from_lirc(ir); |
| 523 | |
| 524 | dev_info(dev, DRIVER_NAME "[%d]: %s done\n", devnum, __func__); |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 525 | } |
| 526 | |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 527 | static struct usb_device_id igorplugusb_remote_id_table[] = { |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 528 | /* Igor Plug USB (Atmel's Manufact. ID) */ |
| 529 | { USB_DEVICE(0x03eb, 0x0002) }, |
Jarod Wilson | 966ea5b | 2010-10-19 14:26:01 -0300 | [diff] [blame] | 530 | /* Fit PC2 Infrared Adapter */ |
| 531 | { USB_DEVICE(0x03eb, 0x21fe) }, |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 532 | |
| 533 | /* Terminating entry */ |
| 534 | { } |
| 535 | }; |
| 536 | |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 537 | static struct usb_driver igorplugusb_remote_driver = { |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 538 | .name = DRIVER_NAME, |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 539 | .probe = igorplugusb_remote_probe, |
| 540 | .disconnect = igorplugusb_remote_disconnect, |
| 541 | .id_table = igorplugusb_remote_id_table |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 542 | }; |
| 543 | |
Greg Kroah-Hartman | bac2c12 | 2011-11-18 09:42:11 -0800 | [diff] [blame] | 544 | module_usb_driver(igorplugusb_remote_driver); |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 545 | |
| 546 | #include <linux/vermagic.h> |
| 547 | MODULE_INFO(vermagic, VERMAGIC_STRING); |
| 548 | |
| 549 | MODULE_DESCRIPTION(DRIVER_DESC); |
| 550 | MODULE_AUTHOR(DRIVER_AUTHOR); |
| 551 | MODULE_LICENSE("GPL"); |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 552 | MODULE_DEVICE_TABLE(usb, igorplugusb_remote_id_table); |
Jarod Wilson | eedaf1a | 2010-07-26 20:29:00 -0300 | [diff] [blame] | 553 | |
| 554 | module_param(sample_rate, int, S_IRUGO | S_IWUSR); |
| 555 | MODULE_PARM_DESC(sample_rate, "Sampling rate in Hz (default: 100)"); |
| 556 | |
Jarod Wilson | 917167e | 2010-10-18 11:57:06 -0300 | [diff] [blame] | 557 | module_param(debug, bool, S_IRUGO | S_IWUSR); |
| 558 | MODULE_PARM_DESC(debug, "Debug enabled or not"); |