Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * LIRC driver for Windows Media Center Edition USB Infrared Transceivers
- *
- * (C) by Martin A. Blatter <martin_a_blatter@yahoo.com>
- *
- * Transmitter support and reception code cleanup.
- * (C) by Daniel Melander <lirc@rajidae.se>
- *
- * Original lirc_mceusb driver for 1st-gen device:
- * Copyright (c) 2003-2004 Dan Conti <dconti@acm.wwu.edu>
- *
- * Original lirc_mceusb driver deprecated in favor of this driver, which
- * supports the 1st-gen device now too. Transmitting on the 1st-gen device
- * only functions on port #2 at the moment.
- *
- * Support for 1st-gen device added June 2009,
- * by Jarod Wilson <jarod@wilsonet.com>
- *
- * Initial transmission support for 1st-gen device added August 2009,
- * by Patrick Calhoun <phineas@ou.edu>
- *
- * Derived from ATI USB driver by Paul Miller and the original
- * MCE USB driver by Dan Conti ((and now including chunks of the latter
- * relevant to the 1st-gen device initialization)
- *
- * This driver will only work reliably with kernel version 2.6.10
- * or higher, probably because of differences in USB device enumeration
- * in the kernel code. Device initialization fails most of the time
- * with earlier kernel versions.
- *
- **********************************************************************
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
- #include <linux/version.h>
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 5)
- #error "*******************************************************"
- #error "Sorry, this driver needs kernel version 2.6.5 or higher"
- #error "*******************************************************"
- #endif
- #include <linux/autoconf.h>
- #include <linux/kernel.h>
- #include <linux/errno.h>
- #include <linux/init.h>
- #include <linux/slab.h>
- #include <linux/module.h>
- #include <linux/kmod.h>
- #include <linux/smp_lock.h>
- #include <linux/completion.h>
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
- #include <asm/uaccess.h>
- #else
- #include <linux/uaccess.h>
- #endif
- #include <linux/usb.h>
- #include <linux/wait.h>
- #include <linux/time.h>
- #include "drivers/lirc.h"
- #include "drivers/kcompat.h"
- #include "drivers/lirc_dev/lirc_dev.h"
- #define DRIVER_VERSION "1.90"
- #define DRIVER_AUTHOR "Daniel Melander <lirc@rajidae.se>, " \
- "Martin Blatter <martin_a_blatter@yahoo.com>, " \
- "Dan Conti <dconti@acm.wwu.edu>"
- #define DRIVER_DESC "Windows Media Center Edition USB IR Transceiver " \
- "driver for LIRC"
- #define DRIVER_NAME "lirc_mceusb"
- #define USB_BUFLEN 32 /* USB reception buffer length */
- #define LIRCBUF_SIZE 256 /* LIRC work buffer length */
- /* MCE constants */
- #define MCE_CMDBUF_SIZE 384 /* MCE Command buffer length */
- #define MCE_TIME_UNIT 50 /* Approx 50us resolution */
- #define MCE_CODE_LENGTH 5 /* Normal length of packet (with header) */
- #define MCE_PACKET_SIZE 4 /* Normal length of packet (without header) */
- #define MCE_PACKET_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */
- #define MCE_CONTROL_HEADER 0x9F /* MCE status header */
- #define MCE_TX_HEADER_LENGTH 3 /* # of bytes in the initializing tx header */
- #define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */
- #define MCE_DEFAULT_TX_MASK 0x03 /* Val opts: TX1=0x01, TX2=0x02, ALL=0x03 */
- #define MCE_PULSE_BIT 0x80 /* Pulse bit, MSB set == PULSE else SPACE */
- #define MCE_PULSE_MASK 0x7F /* Pulse mask */
- #define MCE_MAX_PULSE_LENGTH 0x7F /* Longest transmittable pulse symbol */
- #define MCE_PACKET_LENGTH_MASK 0x7F /* Pulse mask */
- /* module parameters */
- #ifdef CONFIG_USB_DEBUG
- static int debug = 1;
- #else
- static int debug;
- #endif
- #define dprintk(fmt, args...) \
- do { \
- if (debug) \
- printk(KERN_DEBUG fmt, ## args); \
- } while (0)
- /* general constants */
- #define SEND_FLAG_IN_PROGRESS 1
- #define SEND_FLAG_COMPLETE 2
- #define RECV_FLAG_IN_PROGRESS 3
- #define RECV_FLAG_COMPLETE 4
- #define MCEUSB_INBOUND 1
- #define MCEUSB_OUTBOUND 2
- #define VENDOR_PHILIPS 0x0471
- #define VENDOR_SMK 0x0609
- #define VENDOR_TATUNG 0x1460
- #define VENDOR_GATEWAY 0x107b
- #define VENDOR_SHUTTLE 0x1308
- #define VENDOR_SHUTTLE2 0x051c
- #define VENDOR_MITSUMI 0x03ee
- #define VENDOR_TOPSEED 0x1784
- #define VENDOR_RICAVISION 0x179d
- #define VENDOR_ITRON 0x195d
- #define VENDOR_FIC 0x1509
- #define VENDOR_LG 0x043e
- #define VENDOR_MICROSOFT 0x045e
- #define VENDOR_FORMOSA 0x147a
- #define VENDOR_FINTEK 0x1934
- #define VENDOR_PINNACLE 0x2304
- #define VENDOR_ECS 0x1019
- #define VENDOR_WISTRON 0x0fb8
- #define VENDOR_COMPRO 0x185b
- #define VENDOR_NORTHSTAR 0x04eb
- static struct usb_device_id mceusb_dev_table[] = {
- /* Original Microsoft MCE IR Transceiver (often HP-branded) */
- { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) },
- /* Philips Infrared Transceiver - Sahara branded */
- { USB_DEVICE(VENDOR_PHILIPS, 0x0608) },
- /* Philips Infrared Transceiver - HP branded */
- { USB_DEVICE(VENDOR_PHILIPS, 0x060c) },
- /* Philips SRM5100 */
- { USB_DEVICE(VENDOR_PHILIPS, 0x060d) },
- /* Philips Infrared Transceiver - Omaura */
- { USB_DEVICE(VENDOR_PHILIPS, 0x060f) },
- /* Philips Infrared Transceiver - Spinel plus */
- { USB_DEVICE(VENDOR_PHILIPS, 0x0613) },
- /* Philips eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_PHILIPS, 0x0815) },
- /* SMK/Toshiba G83C0004D410 */
- { USB_DEVICE(VENDOR_SMK, 0x031d) },
- /* SMK eHome Infrared Transceiver (Sony VAIO) */
- { USB_DEVICE(VENDOR_SMK, 0x0322) },
- /* bundled with Hauppauge PVR-150 */
- { USB_DEVICE(VENDOR_SMK, 0x0334) },
- /* Tatung eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_TATUNG, 0x9150) },
- /* Shuttle eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_SHUTTLE, 0xc001) },
- /* Shuttle eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_SHUTTLE2, 0xc001) },
- /* Gateway eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_GATEWAY, 0x3009) },
- /* Mitsumi */
- { USB_DEVICE(VENDOR_MITSUMI, 0x2501) },
- /* Topseed eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
- /* Topseed HP eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_TOPSEED, 0x0006) },
- /* Topseed eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
- /* Topseed eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
- /* Topseed eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_TOPSEED, 0x000a) },
- /* Ricavision internal Infrared Transceiver */
- { USB_DEVICE(VENDOR_RICAVISION, 0x0010) },
- /* Itron ione Libra Q-11 */
- { USB_DEVICE(VENDOR_ITRON, 0x7002) },
- /* FIC eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_FIC, 0x9242) },
- /* LG eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_LG, 0x9803) },
- /* Microsoft MCE Infrared Transceiver */
- { USB_DEVICE(VENDOR_MICROSOFT, 0x00a0) },
- /* Formosa eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_FORMOSA, 0xe015) },
- /* Formosa21 / eHome Infrared Receiver */
- { USB_DEVICE(VENDOR_FORMOSA, 0xe016) },
- /* Formosa aim / Trust MCE Infrared Receiver */
- { USB_DEVICE(VENDOR_FORMOSA, 0xe017) },
- /* Formosa Industrial Computing / Beanbag Emulation Device */
- { USB_DEVICE(VENDOR_FORMOSA, 0xe018) },
- /* Formosa21 / eHome Infrared Receiver */
- { USB_DEVICE(VENDOR_FORMOSA, 0xe03a) },
- /* Formosa Industrial Computing AIM IR605/A */
- { USB_DEVICE(VENDOR_FORMOSA, 0xe03c) },
- /* Fintek eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_FINTEK, 0x0602) },
- /* Fintek eHome Infrared Transceiver (in the AOpen MP45) */
- { USB_DEVICE(VENDOR_FINTEK, 0x0702) },
- /* Pinnacle Remote Kit */
- { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
- /* Elitegroup Computer Systems IR */
- { USB_DEVICE(VENDOR_ECS, 0x0f38) },
- /* Wistron Corp. eHome Infrared Receiver */
- { USB_DEVICE(VENDOR_WISTRON, 0x0002) },
- /* Compro K100 */
- { USB_DEVICE(VENDOR_COMPRO, 0x3020) },
- /* Compro K100 v2 */
- { USB_DEVICE(VENDOR_COMPRO, 0x3082) },
- /* Northstar Systems eHome Infrared Transceiver */
- { USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) },
- /* HP Reciever From China */
- { USB_DEVICE(VENDOR_FINTEK, 0x5168) },
- /* Terminating entry */
- { }
- };
- static struct usb_device_id pinnacle_list[] = {
- { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
- {}
- };
- static struct usb_device_id microsoft_gen1_list[] = {
- { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) },
- {}
- };
- static struct usb_device_id transmitter_mask_list[] = {
- { USB_DEVICE(VENDOR_SMK, 0x031d) },
- { USB_DEVICE(VENDOR_SMK, 0x0322) },
- { USB_DEVICE(VENDOR_SMK, 0x0334) },
- { USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
- { USB_DEVICE(VENDOR_TOPSEED, 0x0006) },
- { USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
- { USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
- { USB_DEVICE(VENDOR_TOPSEED, 0x000a) },
- { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
- {}
- };
- /* data structure for each usb transceiver */
- struct mceusb_dev {
- /* usb */
- struct usb_device *usbdev;
- struct urb *urb_in;
- int devnum;
- struct usb_endpoint_descriptor *usb_ep_in;
- struct usb_endpoint_descriptor *usb_ep_out;
- /* buffers and dma */
- unsigned char *buf_in;
- unsigned int len_in;
- dma_addr_t dma_in;
- dma_addr_t dma_out;
- unsigned int overflow_len;
- /* lirc */
- struct lirc_driver *d;
- lirc_t lircdata;
- unsigned char is_pulse;
- struct {
- u32 connected:1;
- u32 pinnacle:1;
- u32 transmitter_mask_inverted:1;
- u32 microsoft_gen1:1;
- u32 reserved:28;
- } flags;
- unsigned char transmitter_mask;
- unsigned int carrier_freq;
- /* handle sending (init strings) */
- int send_flags;
- wait_queue_head_t wait_out;
- struct mutex lock;
- };
- /* init strings */
- static char init1[] = {0x00, 0xff, 0xaa, 0xff, 0x0b};
- static char init2[] = {0xff, 0x18};
- static char pin_init1[] = { 0x9f, 0x07};
- static char pin_init2[] = { 0x9f, 0x13};
- static char pin_init3[] = { 0x9f, 0x0d};
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11)
- static unsigned long usecs_to_jiffies(const unsigned int u)
- {
- if (u > jiffies_to_usecs(MAX_JIFFY_OFFSET))
- return MAX_JIFFY_OFFSET;
- #if HZ <= USEC_PER_SEC && !(USEC_PER_SEC % HZ)
- return (u + (USEC_PER_SEC / HZ) - 1) / (USEC_PER_SEC / HZ);
- #elif HZ > USEC_PER_SEC && !(HZ % USEC_PER_SEC)
- return u * (HZ / USEC_PER_SEC);
- #else
- return (u * HZ + USEC_PER_SEC - 1) / USEC_PER_SEC;
- #endif
- }
- #endif
- static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, int len)
- {
- char codes[USB_BUFLEN * 3 + 1];
- int i;
- if (len <= 0)
- return;
- if (ir->flags.microsoft_gen1 && len <= 2)
- return;
- for (i = 0; i < len && i < USB_BUFLEN; i++)
- snprintf(codes + i * 3, 4, "%02x ", buf[i] & 0xFF);
- printk(KERN_INFO "" DRIVER_NAME "[%d]: data received %s (length=%d)\n",
- ir->devnum, codes, len);
- }
- static void usb_async_callback(struct urb *urb, struct pt_regs *regs)
- {
- struct mceusb_dev *ir;
- int len;
- if (!urb)
- return;
- ir = urb->context;
- if (ir) {
- len = urb->actual_length;
- dprintk(DRIVER_NAME
- "[%d]: callback called (status=%d len=%d)\n",
- ir->devnum, urb->status, len);
- if (debug)
- mceusb_dev_printdata(ir, urb->transfer_buffer, len);
- }
- }
- /* request incoming or send outgoing usb packet - used to initialize remote */
- static void request_packet_async(struct mceusb_dev *ir,
- struct usb_endpoint_descriptor *ep,
- unsigned char *data, int size, int urb_type)
- {
- int res;
- struct urb *async_urb;
- unsigned char *async_buf;
- if (urb_type) {
- async_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (unlikely(!async_urb))
- return;
- async_buf = kmalloc(size, GFP_KERNEL);
- if (!async_buf) {
- usb_free_urb(async_urb);
- return;
- }
- if (urb_type == MCEUSB_OUTBOUND) {
- /* outbound data */
- usb_fill_int_urb(async_urb, ir->usbdev,
- usb_sndintpipe(ir->usbdev,
- ep->bEndpointAddress),
- async_buf, size,
- (usb_complete_t) usb_async_callback,
- ir, ep->bInterval);
- memcpy(async_buf, data, size);
- } else {
- /* inbound data */
- usb_fill_int_urb(async_urb, ir->usbdev,
- usb_rcvintpipe(ir->usbdev,
- ep->bEndpointAddress),
- async_buf, size,
- (usb_complete_t) usb_async_callback,
- ir, ep->bInterval);
- }
- async_urb->transfer_flags = URB_ASYNC_UNLINK;
- } else {
- /* standard request */
- async_urb = ir->urb_in;
- ir->send_flags = RECV_FLAG_IN_PROGRESS;
- }
- dprintk(DRIVER_NAME "[%d]: receive request called (size=%#x)\n",
- ir->devnum, size);
- async_urb->transfer_buffer_length = size;
- async_urb->dev = ir->usbdev;
- res = usb_submit_urb(async_urb, GFP_ATOMIC);
- if (res) {
- dprintk(DRIVER_NAME "[%d]: receive request FAILED! (res=%d)\n",
- ir->devnum, res);
- return;
- }
- dprintk(DRIVER_NAME "[%d]: receive request complete (res=%d)\n",
- ir->devnum, res);
- }
- static int unregister_from_lirc(struct mceusb_dev *ir)
- {
- struct lirc_driver *d = ir->d;
- int devnum;
- int rtn;
- devnum = ir->devnum;
- dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
- rtn = lirc_unregister_driver(d->minor);
- if (rtn > 0) {
- printk(DRIVER_NAME "[%d]: error in lirc_unregister minor: %d\n"
- "Trying again...\n", devnum, d->minor);
- if (rtn == -EBUSY) {
- printk(DRIVER_NAME
- "[%d]: device is opened, will unregister"
- " on close\n", devnum);
- return -EAGAIN;
- }
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ);
- rtn = lirc_unregister_driver(d->minor);
- if (rtn > 0)
- printk(DRIVER_NAME "[%d]: lirc_unregister failed\n",
- devnum);
- }
- if (rtn) {
- printk(DRIVER_NAME "[%d]: didn't free resources\n", devnum);
- return -EAGAIN;
- }
- printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
- lirc_buffer_free(d->rbuf);
- kfree(d->rbuf);
- kfree(d);
- kfree(ir);
- return 0;
- }
- static int mceusb_ir_open(void *data)
- {
- struct mceusb_dev *ir = data;
- if (!ir) {
- printk(DRIVER_NAME "[?]: %s called with no context\n",
- __func__);
- return -EIO;
- }
- dprintk(DRIVER_NAME "[%d]: mceusb IR device opened\n", ir->devnum);
- MOD_INC_USE_COUNT;
- if (!ir->flags.connected) {
- if (!ir->usbdev)
- return -ENOENT;
- ir->flags.connected = 1;
- }
- return 0;
- }
- static void mceusb_ir_close(void *data)
- {
- struct mceusb_dev *ir = data;
- if (!ir) {
- printk(DRIVER_NAME "[?]: %s called with no context\n",
- __func__);
- return;
- }
- dprintk(DRIVER_NAME "[%d]: mceusb IR device closed\n", ir->devnum);
- if (ir->flags.connected) {
- mutex_lock(&ir->lock);
- ir->flags.connected = 0;
- mutex_unlock(&ir->lock);
- }
- MOD_DEC_USE_COUNT;
- }
- static void send_packet_to_lirc(struct mceusb_dev *ir)
- {
- if (ir->lircdata) {
- lirc_buffer_write(ir->d->rbuf,
- (unsigned char *) &ir->lircdata);
- wake_up(&ir->d->rbuf->wait_poll);
- ir->lircdata = 0;
- }
- }
- static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
- {
- int i, j;
- int packet_len = 0;
- int start_index = 0;
- /* skip meaningless 0xb1 0x60 header bytes on orig receiver */
- if (ir->flags.microsoft_gen1)
- start_index = 2;
- /* this should only trigger w/the 1st-gen mce receiver */
- for (i = start_index; i < (start_index + ir->overflow_len) &&
- i < buf_len; i++) {
- /* rising/falling flank */
- if (ir->is_pulse != (ir->buf_in[i] & MCE_PULSE_BIT)) {
- send_packet_to_lirc(ir);
- ir->is_pulse = ir->buf_in[i] & MCE_PULSE_BIT;
- }
- /* accumulate mce pulse/space values */
- ir->lircdata += (ir->buf_in[i] & MCE_PULSE_MASK) *
- MCE_TIME_UNIT;
- ir->lircdata |= (ir->is_pulse ? PULSE_BIT : 0);
- }
- start_index += ir->overflow_len;
- ir->overflow_len = 0;
- for (i = start_index; i < buf_len; i++) {
- /* decode mce packets of the form (84),AA,BB,CC,DD */
- if (ir->buf_in[i] >= 0x80 && ir->buf_in[i] <= 0x9e) {
- /* data headers */
- /* decode packet data */
- packet_len = ir->buf_in[i] & MCE_PACKET_LENGTH_MASK;
- ir->overflow_len = i + 1 + packet_len - buf_len;
- for (j = 1; j <= packet_len && (i + j < buf_len); j++) {
- /* rising/falling flank */
- if (ir->is_pulse !=
- (ir->buf_in[i + j] & MCE_PULSE_BIT)) {
- send_packet_to_lirc(ir);
- ir->is_pulse =
- ir->buf_in[i + j] &
- MCE_PULSE_BIT;
- }
- /* accumulate mce pulse/space values */
- ir->lircdata +=
- (ir->buf_in[i + j] & MCE_PULSE_MASK) *
- MCE_TIME_UNIT;
- ir->lircdata |= (ir->is_pulse ? PULSE_BIT : 0);
- }
- i += packet_len;
- } else if (ir->buf_in[i] == MCE_CONTROL_HEADER) {
- /* status header (0x9F) */
- /*
- * A transmission containing one or more consecutive ir
- * commands always ends with a GAP of 100ms followed by
- * the sequence 0x9F 0x01 0x01 0x9F 0x15 0x00 0x00 0x80
- */
- #if 0
- Uncomment this if the last 100ms "infinity"-space should be transmitted
- to lirc directly instead of at the beginning of the next transmission.
- Changes pulse/space order.
- if (++i < buf_len && ir->buf_in[i]==0x01)
- send_packet_to_lirc(ir);
- #endif
- /* end decode loop */
- dprintk(DRIVER_NAME "[%d] %s: found control header\n",
- ir->devnum, __func__);
- ir->overflow_len = 0;
- break;
- } else {
- dprintk(DRIVER_NAME "[%d] %s: stray packet?\n",
- ir->devnum, __func__);
- ir->overflow_len = 0;
- }
- }
- return;
- }
- static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
- {
- struct mceusb_dev *ir;
- int buf_len;
- if (!urb)
- return;
- ir = urb->context;
- if (!ir) {
- urb->transfer_flags |= URB_ASYNC_UNLINK;
- usb_unlink_urb(urb);
- return;
- }
- buf_len = urb->actual_length;
- if (debug)
- mceusb_dev_printdata(ir, urb->transfer_buffer, buf_len);
- if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
- ir->send_flags = SEND_FLAG_COMPLETE;
- dprintk(DRIVER_NAME "[%d]: setup answer received %d bytes\n",
- ir->devnum, buf_len);
- }
- switch (urb->status) {
- /* success */
- case 0:
- mceusb_process_ir_data(ir, buf_len);
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- urb->transfer_flags |= URB_ASYNC_UNLINK;
- usb_unlink_urb(urb);
- return;
- case -EPIPE:
- default:
- break;
- }
- usb_submit_urb(urb, GFP_ATOMIC);
- }
- static ssize_t mceusb_transmit_ir(struct file *file, const char *buf,
- size_t n, loff_t *ppos)
- {
- int i, count = 0, cmdcount = 0;
- struct mceusb_dev *ir = NULL;
- lirc_t wbuf[LIRCBUF_SIZE]; /* Workbuffer with values from lirc */
- unsigned char cmdbuf[MCE_CMDBUF_SIZE]; /* MCE command buffer */
- unsigned long signal_duration = 0; /* Singnal length in us */
- struct timeval start_time, end_time;
- do_gettimeofday(&start_time);
- /* Retrieve lirc_driver data for the device */
- ir = lirc_get_pdata(file);
- if (!ir || !ir->usb_ep_out)
- return -EFAULT;
- if (n % sizeof(lirc_t))
- return -EINVAL;
- count = n / sizeof(lirc_t);
- /* Check if command is within limits */
- if (count > LIRCBUF_SIZE || count%2 == 0)
- return -EINVAL;
- if (copy_from_user(wbuf, buf, n))
- return -EFAULT;
- /* MCE tx init header */
- cmdbuf[cmdcount++] = MCE_CONTROL_HEADER;
- cmdbuf[cmdcount++] = 0x08;
- cmdbuf[cmdcount++] = ir->transmitter_mask;
- /* Generate mce packet data */
- for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) {
- signal_duration += wbuf[i];
- wbuf[i] = wbuf[i] / MCE_TIME_UNIT;
- do { /* loop to support long pulses/spaces > 127*50us=6.35ms */
- /* Insert mce packet header every 4th entry */
- if ((cmdcount < MCE_CMDBUF_SIZE) &&
- (cmdcount - MCE_TX_HEADER_LENGTH) %
- MCE_CODE_LENGTH == 0)
- cmdbuf[cmdcount++] = MCE_PACKET_HEADER;
- /* Insert mce packet data */
- if (cmdcount < MCE_CMDBUF_SIZE)
- cmdbuf[cmdcount++] =
- (wbuf[i] < MCE_PULSE_BIT ?
- wbuf[i] : MCE_MAX_PULSE_LENGTH) |
- (i & 1 ? 0x00 : MCE_PULSE_BIT);
- else
- return -EINVAL;
- } while ((wbuf[i] > MCE_MAX_PULSE_LENGTH) &&
- (wbuf[i] -= MCE_MAX_PULSE_LENGTH));
- }
- /* Fix packet length in last header */
- cmdbuf[cmdcount - (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH] =
- 0x80 + (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH - 1;
- /* Check if we have room for the empty packet at the end */
- if (cmdcount >= MCE_CMDBUF_SIZE)
- return -EINVAL;
- /* All mce commands end with an empty packet (0x80) */
- cmdbuf[cmdcount++] = 0x80;
- /* Transmit the command to the mce device */
- request_packet_async(ir, ir->usb_ep_out, cmdbuf,
- cmdcount, MCEUSB_OUTBOUND);
- /*
- * The lircd gap calculation expects the write function to
- * wait the time it takes for the ircommand to be sent before
- * it returns.
- */
- do_gettimeofday(&end_time);
- signal_duration -= (end_time.tv_usec - start_time.tv_usec) +
- (end_time.tv_sec - start_time.tv_sec) * 1000000;
- /* delay with the closest number of ticks */
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(usecs_to_jiffies(signal_duration));
- return n;
- }
- static void set_transmitter_mask(struct mceusb_dev *ir, unsigned int mask)
- {
- if (ir->flags.transmitter_mask_inverted)
- /*
- * The mask begins at 0x02 and has an inverted
- * numbering scheme
- */
- ir->transmitter_mask =
- (mask != 0x03 ? mask ^ 0x03 : mask) << 1;
- else
- ir->transmitter_mask = mask;
- }
- /* Sets the send carrier frequency */
- static int set_send_carrier(struct mceusb_dev *ir, int carrier)
- {
- int clk = 10000000;
- int prescaler = 0, divisor = 0;
- unsigned char cmdbuf[] = { 0x9F, 0x06, 0x01, 0x80 };
- /* Carrier is changed */
- if (ir->carrier_freq != carrier) {
- if (carrier <= 0) {
- ir->carrier_freq = carrier;
- dprintk(DRIVER_NAME "[%d]: SET_CARRIER disabling "
- "carrier modulation\n", ir->devnum);
- request_packet_async(ir, ir->usb_ep_out,
- cmdbuf, sizeof(cmdbuf),
- MCEUSB_OUTBOUND);
- return carrier;
- }
- for (prescaler = 0; prescaler < 4; ++prescaler) {
- divisor = (clk >> (2 * prescaler)) / carrier;
- if (divisor <= 0xFF) {
- ir->carrier_freq = carrier;
- cmdbuf[2] = prescaler;
- cmdbuf[3] = divisor;
- dprintk(DRIVER_NAME "[%d]: SET_CARRIER "
- "requesting %d Hz\n",
- ir->devnum, carrier);
- /* Transmit new carrier to mce device */
- request_packet_async(ir, ir->usb_ep_out,
- cmdbuf, sizeof(cmdbuf),
- MCEUSB_OUTBOUND);
- return carrier;
- }
- }
- return -EINVAL;
- }
- return carrier;
- }
- static int mceusb_lirc_ioctl(struct inode *node, struct file *filep,
- unsigned int cmd, unsigned long arg)
- {
- int result;
- unsigned int ivalue;
- unsigned long lvalue;
- struct mceusb_dev *ir = NULL;
- /* Retrieve lirc_driver data for the device */
- ir = lirc_get_pdata(filep);
- if (!ir || !ir->usb_ep_out)
- return -EFAULT;
- switch (cmd) {
- case LIRC_SET_TRANSMITTER_MASK:
- result = get_user(ivalue, (unsigned int *) arg);
- if (result)
- return result;
- switch (ivalue) {
- case 0x01: /* Transmitter 1 => 0x04 */
- case 0x02: /* Transmitter 2 => 0x02 */
- case 0x03: /* Transmitter 1 & 2 => 0x06 */
- set_transmitter_mask(ir, ivalue);
- break;
- default: /* Unsupported transmitter mask */
- return MCE_MAX_CHANNELS;
- }
- dprintk(DRIVER_NAME ": SET_TRANSMITTERS mask=%d\n", ivalue);
- break;
- case LIRC_GET_SEND_MODE:
- result = put_user(LIRC_SEND2MODE(LIRC_CAN_SEND_PULSE &
- LIRC_CAN_SEND_MASK),
- (unsigned long *) arg);
- if (result)
- return result;
- break;
- case LIRC_SET_SEND_MODE:
- result = get_user(lvalue, (unsigned long *) arg);
- if (result)
- return result;
- if (lvalue != (LIRC_MODE_PULSE&LIRC_CAN_SEND_MASK))
- return -EINVAL;
- break;
- case LIRC_SET_SEND_CARRIER:
- result = get_user(ivalue, (unsigned int *) arg);
- if (result)
- return result;
- set_send_carrier(ir, ivalue);
- break;
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
- }
- static struct file_operations lirc_fops = {
- .owner = THIS_MODULE,
- .write = mceusb_transmit_ir,
- .ioctl = mceusb_lirc_ioctl,
- };
- static int mceusb_gen1_init(struct mceusb_dev *ir)
- {
- int i, ret;
- char junk[64], data[8];
- int partial = 0;
- /*
- * Clear off the first few messages. These look like calibration
- * or test data, I can't really tell. This also flushes in case
- * we have random ir data queued up.
- */
- for (i = 0; i < 40; i++)
- usb_bulk_msg(ir->usbdev,
- usb_rcvbulkpipe(ir->usbdev,
- ir->usb_ep_in->bEndpointAddress),
- junk, 64, &partial, HZ * 10);
- ir->is_pulse = 1;
- memset(data, 0, 8);
- /* Get Status */
- ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
- USB_REQ_GET_STATUS, USB_DIR_IN,
- 0, 0, data, 2, HZ * 3);
- /* ret = usb_get_status( ir->usbdev, 0, 0, data ); */
- dprintk("%s - ret = %d status = 0x%x 0x%x\n", __func__,
- ret, data[0], data[1]);
- /*
- * This is a strange one. They issue a set address to the device
- * on the receive control pipe and expect a certain value pair back
- */
- memset(data, 0, 8);
- ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
- USB_REQ_SET_ADDRESS, USB_TYPE_VENDOR, 0, 0,
- data, 2, HZ * 3);
- dprintk("%s - ret = %d, devnum = %d\n",
- __func__, ret, ir->usbdev->devnum);
- dprintk("%s - data[0] = %d, data[1] = %d\n",
- __func__, data[0], data[1]);
- /* set feature */
- ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
- USB_REQ_SET_FEATURE, USB_TYPE_VENDOR,
- 0xc04e, 0x0000, NULL, 0, HZ * 3);
- dprintk("%s - ret = %d\n", __func__, ret);
- /* strange: bRequest == 4 */
- ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
- 4, USB_TYPE_VENDOR,
- 0x0808, 0x0000, NULL, 0, HZ * 3);
- dprintk("%s - retB = %d\n", __func__, ret);
- /* strange: bRequest == 2 */
- ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
- 2, USB_TYPE_VENDOR,
- 0x0000, 0x0100, NULL, 0, HZ * 3);
- dprintk("%s - retC = %d\n", __func__, ret);
- return ret;
- };
- static int mceusb_dev_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
- {
- struct usb_device *dev = interface_to_usbdev(intf);
- struct usb_host_interface *idesc;
- struct usb_endpoint_descriptor *ep = NULL;
- struct usb_endpoint_descriptor *ep_in = NULL;
- struct usb_endpoint_descriptor *ep_out = NULL;
- struct usb_host_config *config;
- struct mceusb_dev *ir = NULL;
- struct lirc_driver *driver = NULL;
- struct lirc_buffer *rbuf = NULL;
- int devnum, pipe, maxp;
- int minor = 0;
- int i;
- char buf[63], name[128] = "";
- int mem_failure = 0;
- int is_pinnacle;
- int is_microsoft_gen1;
- dprintk(DRIVER_NAME ": %s called\n", __func__);
- usb_reset_device(dev);
- config = dev->actconfig;
- idesc = intf->cur_altsetting;
- is_pinnacle = usb_match_id(intf, pinnacle_list) ? 1 : 0;
- is_microsoft_gen1 = usb_match_id(intf, microsoft_gen1_list) ? 1 : 0;
- /* step through the endpoints to find first bulk in and out endpoint */
- for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
- ep = &idesc->endpoint[i].desc;
- if ((ep_in == NULL)
- && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- == USB_DIR_IN)
- && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)
- || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_INT))) {
- dprintk(DRIVER_NAME ": acceptable inbound endpoint "
- "found\n");
- ep_in = ep;
- ep_in->bmAttributes = USB_ENDPOINT_XFER_INT;
- if (is_pinnacle)
- /*
- * setting seems to 1 seem to cause issues with
- * Pinnacle timing out on transfer.
- */
- ep_in->bInterval = ep->bInterval;
- else
- ep_in->bInterval = 1;
- }
- if ((ep_out == NULL)
- && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- == USB_DIR_OUT)
- && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)
- || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_INT))) {
- dprintk(DRIVER_NAME ": acceptable outbound endpoint "
- "found\n");
- ep_out = ep;
- ep_out->bmAttributes = USB_ENDPOINT_XFER_INT;
- if (is_pinnacle)
- /*
- * setting seems to 1 seem to cause issues with
- * Pinnacle timing out on transfer.
- */
- ep_out->bInterval = ep->bInterval;
- else
- ep_out->bInterval = 1;
- }
- }
- if (ep_in == NULL || ep_out == NULL) {
- dprintk(DRIVER_NAME ": inbound and/or "
- "outbound endpoint not found\n");
- return -ENODEV;
- }
- devnum = dev->devnum;
- pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
- maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
- mem_failure = 0;
- ir = kzalloc(sizeof(struct mceusb_dev), GFP_KERNEL);
- if (!ir) {
- mem_failure = 1;
- goto mem_failure_switch;
- }
- driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
- if (!driver) {
- mem_failure = 2;
- goto mem_failure_switch;
- }
- rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
- if (!rbuf) {
- mem_failure = 3;
- goto mem_failure_switch;
- }
- if (lirc_buffer_init(rbuf, sizeof(lirc_t), LIRCBUF_SIZE)) {
- mem_failure = 4;
- goto mem_failure_switch;
- }
- ir->buf_in = usb_buffer_alloc(dev, maxp, GFP_ATOMIC, &ir->dma_in);
- if (!ir->buf_in) {
- mem_failure = 5;
- goto mem_failure_switch;
- }
- ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
- if (!ir->urb_in) {
- mem_failure = 7;
- goto mem_failure_switch;
- }
- strcpy(driver->name, DRIVER_NAME " ");
- driver->minor = -1;
- driver->features = LIRC_CAN_SEND_PULSE |
- LIRC_CAN_SET_TRANSMITTER_MASK |
- LIRC_CAN_REC_MODE2 |
- LIRC_CAN_SET_SEND_CARRIER;
- driver->data = ir;
- driver->rbuf = rbuf;
- driver->set_use_inc = &mceusb_ir_open;
- driver->set_use_dec = &mceusb_ir_close;
- driver->code_length = sizeof(lirc_t) * 8;
- driver->fops = &lirc_fops;
- driver->dev = &intf->dev;
- driver->owner = THIS_MODULE;
- mutex_init(&ir->lock);
- init_waitqueue_head(&ir->wait_out);
- minor = lirc_register_driver(driver);
- if (minor < 0)
- mem_failure = 9;
- mem_failure_switch:
- switch (mem_failure) {
- case 9:
- usb_free_urb(ir->urb_in);
- case 7:
- usb_buffer_free(dev, maxp, ir->buf_in, ir->dma_in);
- case 5:
- lirc_buffer_free(rbuf);
- case 4:
- kfree(rbuf);
- case 3:
- kfree(driver);
- case 2:
- kfree(ir);
- case 1:
- printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
- devnum, mem_failure);
- return -ENOMEM;
- }
- driver->minor = minor;
- ir->d = driver;
- ir->devnum = devnum;
- ir->usbdev = dev;
- ir->len_in = maxp;
- ir->overflow_len = 0;
- ir->flags.connected = 0;
- ir->flags.pinnacle = is_pinnacle;
- ir->flags.microsoft_gen1 = is_microsoft_gen1;
- ir->flags.transmitter_mask_inverted =
- usb_match_id(intf, transmitter_mask_list) ? 0 : 1;
- ir->lircdata = PULSE_MASK;
- ir->is_pulse = 0;
- /* ir->flags.transmitter_mask_inverted must be set */
- set_transmitter_mask(ir, MCE_DEFAULT_TX_MASK);
- /* Saving usb interface data for use by the transmitter routine */
- ir->usb_ep_in = ep_in;
- ir->usb_ep_out = ep_out;
- if (dev->descriptor.iManufacturer
- && usb_string(dev, dev->descriptor.iManufacturer,
- buf, sizeof(buf)) > 0)
- strlcpy(name, buf, sizeof(name));
- if (dev->descriptor.iProduct
- && usb_string(dev, dev->descriptor.iProduct,
- buf, sizeof(buf)) > 0)
- snprintf(name + strlen(name), sizeof(name) - strlen(name),
- " %s", buf);
- printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name,
- dev->bus->busnum, devnum);
- /* inbound data */
- usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in,
- maxp, (usb_complete_t) mceusb_dev_recv, ir, ep_in->bInterval);
- ir->urb_in->transfer_dma = ir->dma_in;
- ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- /* initialize device */
- if (ir->flags.pinnacle) {
- int usbret;
- /*
- * I have no idea why but this reset seems to be crucial to
- * getting the device to do outbound IO correctly - without
- * this the device seems to hang, ignoring all input - although
- * IR signals are correctly sent from the device, no input is
- * interpreted by the device and the host never does the
- * completion routine
- */
- usbret = usb_reset_configuration(dev);
- printk(DRIVER_NAME "[%d]: usb reset config ret %x\n",
- devnum, usbret);
- /*
- * its possible we really should wait for a return
- * for each of these...
- */
- request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
- request_packet_async(ir, ep_out, pin_init1, sizeof(pin_init1),
- MCEUSB_OUTBOUND);
- request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
- request_packet_async(ir, ep_out, pin_init2, sizeof(pin_init2),
- MCEUSB_OUTBOUND);
- request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
- request_packet_async(ir, ep_out, pin_init3, sizeof(pin_init3),
- MCEUSB_OUTBOUND);
- } else if (ir->flags.microsoft_gen1) {
- /* original ms mce device requires some additional setup */
- mceusb_gen1_init(ir);
- } else {
- request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
- request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
- request_packet_async(ir, ep_out, init1,
- sizeof(init1), MCEUSB_OUTBOUND);
- request_packet_async(ir, ep_in, NULL, maxp, MCEUSB_INBOUND);
- request_packet_async(ir, ep_out, init2,
- sizeof(init2), MCEUSB_OUTBOUND);
- }
- /*
- * if we don't issue the correct number of receives (MCEUSB_INBOUND)
- * for each outbound, then the first few ir pulses will be interpreted
- * by the usb_async_callback routine - we should ensure we have the
- * right amount OR less - as the meusb_dev_recv routine will handle
- * the control packets OK - they start with 0x9f - but the async
- * callback doesn't handle ir pulse packets
- */
- request_packet_async(ir, ep_in, NULL, maxp, 0);
- usb_set_intfdata(intf, ir);
- return 0;
- }
- static void mceusb_dev_disconnect(struct usb_interface *intf)
- {
- struct usb_device *dev = interface_to_usbdev(intf);
- struct mceusb_dev *ir = usb_get_intfdata(intf);
- usb_set_intfdata(intf, NULL);
- if (!ir || !ir->d)
- return;
- ir->usbdev = NULL;
- wake_up_all(&ir->wait_out);
- mutex_lock(&ir->lock);
- usb_kill_urb(ir->urb_in);
- usb_free_urb(ir->urb_in);
- usb_buffer_free(dev, ir->len_in, ir->buf_in, ir->dma_in);
- mutex_unlock(&ir->lock);
- unregister_from_lirc(ir);
- }
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
- static int mceusb_dev_suspend(struct usb_interface *intf, pm_message_t message)
- {
- struct mceusb_dev *ir = usb_get_intfdata(intf);
- printk(DRIVER_NAME "[%d]: suspend\n", ir->devnum);
- usb_kill_urb(ir->urb_in);
- return 0;
- }
- static int mceusb_dev_resume(struct usb_interface *intf)
- {
- struct mceusb_dev *ir = usb_get_intfdata(intf);
- printk(DRIVER_NAME "[%d]: resume\n", ir->devnum);
- if (usb_submit_urb(ir->urb_in, GFP_ATOMIC))
- return -EIO;
- return 0;
- }
- #endif
- static struct usb_driver mceusb_dev_driver = {
- LIRC_THIS_MODULE(.owner = THIS_MODULE)
- .name = DRIVER_NAME,
- .probe = mceusb_dev_probe,
- .disconnect = mceusb_dev_disconnect,
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
- .suspend = mceusb_dev_suspend,
- .resume = mceusb_dev_resume,
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
- .reset_resume = mceusb_dev_resume,
- #endif
- #endif
- .id_table = mceusb_dev_table
- };
- static int __init mceusb_dev_init(void)
- {
- int i;
- printk(KERN_INFO DRIVER_NAME ": " DRIVER_DESC " " DRIVER_VERSION "\n");
- printk(KERN_INFO DRIVER_NAME ": " DRIVER_AUTHOR "\n");
- dprintk(DRIVER_NAME ": debug mode enabled\n");
- i = usb_register(&mceusb_dev_driver);
- if (i < 0) {
- printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
- return -ENODEV;
- }
- return 0;
- }
- static void __exit mceusb_dev_exit(void)
- {
- usb_deregister(&mceusb_dev_driver);
- }
- module_init(mceusb_dev_init);
- module_exit(mceusb_dev_exit);
- MODULE_DESCRIPTION(DRIVER_DESC);
- MODULE_AUTHOR(DRIVER_AUTHOR);
- MODULE_LICENSE("GPL");
- MODULE_DEVICE_TABLE(usb, mceusb_dev_table);
- /* this was originally lirc_mceusb2, lirc_mceusb and lirc_mceusb2 merged now */
- MODULE_ALIAS("lirc_mceusb2");
- module_param(debug, bool, S_IRUGO | S_IWUSR);
- MODULE_PARM_DESC(debug, "Debug enabled or not");
- EXPORT_NO_SYMBOLS;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement