lkml.org 
[lkml]   [2008]   [Sep]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC PATCH 4/4] Microsoft mceusb2 driver for in-kernel IR subsystem
    Date
    USB device commonly found on Microsoft Media Center boxes.

    Hardware can send and recieve at all common IR frequencies - 36K, 38K, 40K, 56K
    ---
    drivers/input/ir/Kconfig | 6
    drivers/input/ir/Makefile | 1
    drivers/input/ir/ir-mceusb2.c | 729 +++++++++++++++++++++++++++++++++++++++++
    3 files changed, 736 insertions(+), 0 deletions(-)
    create mode 100644 drivers/input/ir/ir-mceusb2.c

    diff --git a/drivers/input/ir/Kconfig b/drivers/input/ir/Kconfig
    index b80ab31..29657d0 100644
    --- a/drivers/input/ir/Kconfig
    +++ b/drivers/input/ir/Kconfig
    @@ -16,5 +16,11 @@ config IR_GPT
    default m
    help
    Driver for GPT-based IR receiver found on Digispeaker
    +
    +config IR_MCEUSB2
    + tristate "Microsoft Media Center Ed. Receiver, v2"
    + default m
    + help
    + Driver for the Microsoft Media Center Ed. Receiver, v2

    endif
    diff --git a/drivers/input/ir/Makefile b/drivers/input/ir/Makefile
    index 7082f1d..780de6c 100644
    --- a/drivers/input/ir/Makefile
    +++ b/drivers/input/ir/Makefile
    @@ -4,3 +4,4 @@
    # Each configuration option enables a list of files.

    obj-$(CONFIG_IR_GPT) += ir-gpt.o
    +obj-$(CONFIG_IR_MCEUSB2) += ir-mceusb2.o
    diff --git a/drivers/input/ir/ir-mceusb2.c b/drivers/input/ir/ir-mceusb2.c
    new file mode 100644
    index 0000000..14f5a53
    --- /dev/null
    +++ b/drivers/input/ir/ir-mceusb2.c
    @@ -0,0 +1,729 @@
    +/*
    + * LIRC driver for Philips eHome USB Infrared Transceiver
    + * and the Microsoft MCE 2005 Remote Control
    + *
    + * (C) by Martin A. Blatter <martin_a_blatter@yahoo.com>
    + *
    + * Transmitter support and reception code cleanup.
    + * (C) by Daniel Melander <lirc@rajidae.se>
    + *
    + * Derived from ATI USB driver by Paul Miller and the original
    + * MCE USB driver by Dan Corti
    + *
    + * 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
    + *
    + */
    +
    +#define DEBUG
    +
    +#include <linux/device.h>
    +#include <linux/module.h>
    +#include <linux/usb.h>
    +#include <linux/input.h>
    +
    +#define DRIVER_AUTHOR "Daniel Melander <lirc@rajidae.se>, " \
    + "Martin Blatter <martin_a_blatter@yahoo.com>"
    +#define DRIVER_DESC "Philips eHome USB IR Transceiver and Microsoft " \
    + "MCE 2005 Remote Control driver"
    +#define DRIVER_NAME "ir_mceusb2"
    +
    +#define USB_BUFLEN 16 /* 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 0x7 /* Packet length */
    +
    +
    +/* 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 PHILUSB_RECEIVE 1
    +#define PHILUSB_SEND 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
    +
    +static struct usb_device_id usb_remote_table[] = {
    + /* Philips eHome Infrared Transceiver */
    + { USB_DEVICE(VENDOR_PHILIPS, 0x0815) },
    + /* 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) },
    + /* 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) },
    + /* 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) },
    + /* Fintek eHome Infrared Transceiver */
    + { USB_DEVICE(VENDOR_FINTEK, 0x0602) },
    + /* Pinnacle Remote Kit */
    + { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
    + /* Terminating entry */
    + { }
    +};
    +
    +static struct usb_device_id pinnacle_list[] = {
    + { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
    + {}
    +};
    +
    +static struct usb_device_id xmit_inverted[] = {
    + { USB_DEVICE(VENDOR_SMK, 0x031d) },
    + { USB_DEVICE(VENDOR_SMK, 0x0322) },
    + { USB_DEVICE(VENDOR_SMK, 0x0334) },
    + { USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
    + { USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
    + { USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
    + { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
    + {}
    +};
    +
    +/* data structure for each usb remote */
    +struct irctl {
    +
    + /* usb */
    + struct usb_device *usbdev;
    + struct urb *urb_in;
    + 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;
    +
    + struct {
    + u32 connected:1;
    + u32 pinnacle:1;
    + u32 transmitter_mask_inverted:1;
    + u32 reserved:29;
    + } flags;
    +
    + /* handle sending (init strings) */
    + int send_flags;
    +
    + int carrier;
    +
    + struct {
    + unsigned int command, partial, delta, bit;
    + } last;
    + struct input_dev *input;
    +};
    +
    +/* 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};
    +
    +static void usb_remote_printdata(struct irctl *ir, char *buf, int len)
    +{
    +#ifdef DEBUG
    + int i;
    +
    + if (len <= 0)
    + return;
    +
    + dev_dbg(&ir->usbdev->dev, "data received ");
    + for (i = 0; i < len && i < USB_BUFLEN; i++)
    + printk("%02x ", buf[i] & 0xFF);
    + printk("\n");
    +#endif
    +}
    +
    +static void usb_send_callback(struct urb *urb, struct pt_regs *regs)
    +{
    + struct irctl *ir = urb->context;
    + int len= urb->actual_length;
    +
    + dev_dbg(&ir->usbdev->dev, "usb_send_callback (status=%d len=%d)\n", urb->status, len);
    + usb_remote_printdata(ir, urb->transfer_buffer, len);
    +}
    +
    +
    +static void usb_receive_callback(struct urb *urb, struct pt_regs *regs)
    +{
    + struct irctl *ir = urb->context;
    + int len= urb->actual_length;
    +
    + dev_dbg(&ir->usbdev->dev, "usb_receive_callback (status=%d len=%d)\n", urb->status, len);
    + usb_remote_printdata(ir, urb->transfer_buffer, len);
    +}
    +
    +
    +/* request incoming or send outgoing usb packet - used to initialize remote */
    +static int request_packet_async(struct irctl *ir, struct usb_endpoint_descriptor *ep,
    + unsigned char *data, int size, int urb_type)
    +{
    + int res;
    + struct urb *async_urb;
    + unsigned char *async_buf;
    +
    + switch (urb_type) {
    + case PHILUSB_SEND:
    + case PHILUSB_RECEIVE:
    + async_urb = usb_alloc_urb(0, GFP_KERNEL);
    + if (!async_urb) {
    + dev_dbg(&ir->usbdev->dev, "Filed to allocate URB\n");
    + return -ENOMEM;
    + }
    + /* alloc buffer */
    + async_buf = kmalloc(size, GFP_KERNEL);
    + if (!async_buf) {
    + usb_free_urb(async_urb);
    + dev_dbg(&ir->usbdev->dev, "Filed to allocate async_buf\n");
    + return -ENOMEM;
    + }
    +
    + if (urb_type == PHILUSB_SEND) {
    + /* outbound data */
    + usb_fill_int_urb(async_urb, ir->usbdev,
    + usb_sndintpipe(ir->usbdev, ep->bEndpointAddress),
    + async_buf, size, (usb_complete_t) usb_send_callback,
    + ir, ep->bInterval);
    +
    + memcpy(async_buf, data, size);
    + dev_dbg(&ir->usbdev->dev, "request_packet_async called (size=%#x, send)\n", 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_receive_callback,
    + ir, ep->bInterval);
    + dev_dbg(&ir->usbdev->dev, "request_packet_async called (size=%#x, receive)\n", size);
    + }
    + break;
    + default:
    + case 0:
    + /* standard request */
    + async_urb = ir->urb_in;
    + ir->send_flags = RECV_FLAG_IN_PROGRESS;
    + dev_dbg(&ir->usbdev->dev, "request_packet_async called (size=%#x, standard)\n", size);
    + break;
    + }
    +
    + async_urb->transfer_buffer_length = size;
    + async_urb->dev = ir->usbdev;
    +
    + res = usb_submit_urb(async_urb, GFP_ATOMIC);
    + if (res)
    + dev_dbg(&ir->usbdev->dev, "request_packet_async (res=%d)\n", res);
    + return res;
    +}
    +
    +static void usb_remote_recv(struct urb *urb, struct pt_regs *regs)
    +{
    + struct irctl *ir;
    + int buf_len;
    + int i, delta, bit;
    +
    + if (!urb)
    + return;
    +
    + ir = urb->context;
    + if (!ir) {
    + usb_unlink_urb(urb);
    + return;
    + }
    + buf_len = urb->actual_length;
    +
    + usb_remote_printdata(ir, urb->transfer_buffer, buf_len);
    +
    + if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
    + ir->send_flags = SEND_FLAG_COMPLETE;
    + dev_dbg(&ir->usbdev->dev, "setup answer received %d bytes\n", buf_len);
    + }
    + switch (urb->status) {
    + /* success */
    + case 0:
    + for (i = 0; i < buf_len;) {
    + if (ir->last.partial == 0) {
    + /* decode mce packets of the form (84),AA,BB,CC,DD */
    + /* IR data packets can span USB messages - partial */
    + ir->last.partial = (ir->buf_in[i] & MCE_PACKET_LENGTH_MASK);
    + ir->last.command = ir->buf_in[i] & ~MCE_PACKET_LENGTH_MASK;
    + i++;
    + }
    + for (; (ir->last.partial > 0) && (i < buf_len); i++) {
    + ir->last.partial--;
    +
    + if (ir->last.command == 0x80) {
    + bit = ((ir->buf_in[i] & MCE_PULSE_BIT) != 0);
    + delta = (ir->buf_in[i] & MCE_PULSE_MASK) * MCE_TIME_UNIT;
    +
    + if ((ir->buf_in[i] & MCE_PULSE_MASK) == 0x7f) {
    + if (ir->last.bit == bit)
    + ir->last.delta += delta;
    + else {
    + ir->last.delta = delta;
    + ir->last.bit = bit;
    + }
    + continue;
    + }
    + delta += ir->last.delta;
    + ir->last.delta = 0;
    + ir->last.bit = bit;
    +
    + dev_dbg(&ir->usbdev->dev, "bit %d delta %d\n", bit, delta);
    + input_ir_decode(ir->input, delta, bit);
    + }
    + }
    + }
    + break;
    +
    + /* unlink */
    + case -ECONNRESET:
    + case -ENOENT:
    + case -ESHUTDOWN:
    + usb_unlink_urb(urb);
    + return;
    +
    + case -EPIPE:
    + default:
    + break;
    + }
    +
    + /* resubmit urb */
    + usb_submit_urb(urb, GFP_ATOMIC);
    +}
    +
    +
    +/* Sets the send carrier frequency */
    +static int set_carrier(struct irctl *ir, int carrier)
    +{
    + int clk = 10000000;
    + int prescaler = 0, divisor = 0;
    + unsigned char cmdbuf[] = { 0x9F, 0x06, 0x01, 0x80 };
    +
    + /* Carrier is changed */
    + if (ir->carrier != carrier) {
    +
    + if (carrier <= 0) {
    + ir->carrier = carrier;
    + dev_dbg(&ir->usbdev->dev, "SET_CARRIER disabling carrier modulation\n");
    + request_packet_async(ir, ir->usb_ep_out,
    + cmdbuf, sizeof(cmdbuf), PHILUSB_SEND);
    + return carrier;
    + }
    +
    + for (prescaler = 0; prescaler < 4; ++prescaler) {
    + divisor = (clk >> (2 * prescaler)) / carrier;
    + if (divisor <= 0xFF) {
    + ir->carrier = carrier;
    + cmdbuf[2] = prescaler;
    + cmdbuf[3] = divisor;
    + dev_dbg(&ir->usbdev->dev, "SET_CARRIER requesting %d Hz\n", carrier);
    +
    + /* Transmit the new carrier to the mce
    + device */
    + request_packet_async(ir, ir->usb_ep_out,
    + cmdbuf, sizeof(cmdbuf), PHILUSB_SEND);
    + return carrier;
    + }
    + }
    + return -EINVAL;
    + }
    + return carrier;
    +}
    +
    +static int send(void *private, unsigned int *buffer, unsigned int count,
    + unsigned int frequency, unsigned int xmitters)
    +{
    + struct irctl *ir = (struct irctl *)private;
    +
    + int i, cmdcount = 0;
    + unsigned char cmdbuf[MCE_CMDBUF_SIZE]; /* MCE command buffer */
    + unsigned long signal_duration = 0; /* Singnal length in us */
    +
    + if (!ir && !ir->usb_ep_out)
    + return -EFAULT;
    +
    + set_carrier(ir, frequency);
    +
    + /* MCE tx init header */
    + cmdbuf[cmdcount++] = MCE_CONTROL_HEADER;
    + cmdbuf[cmdcount++] = 0x08;
    + cmdbuf[cmdcount++] = (ir->flags.transmitter_mask_inverted ? ~xmitters : xmitters) << 1;
    +
    + /* Generate mce packet data */
    + for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) {
    + signal_duration += buffer[i];
    + buffer[i] = buffer[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++] = (buffer[i] < MCE_PULSE_BIT ? buffer[i] :
    + MCE_MAX_PULSE_LENGTH) | (i & 1 ? 0x00 : MCE_PULSE_BIT);
    + else
    + return -EINVAL;
    + } while ((buffer[i] > MCE_MAX_PULSE_LENGTH) && (buffer[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, PHILUSB_SEND);
    +
    + return 0;
    +}
    +
    +static int usb_remote_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 irctl *ir = NULL;
    + int i, devnum, pipe, maxp, ret, is_pinnacle;
    + char name[128];
    +
    + dev_dbg(&dev->dev, "usb probe called\n");
    +
    + usb_reset_device(dev);
    +
    + config = dev->actconfig;
    +
    + idesc = intf->cur_altsetting;
    +
    + is_pinnacle = usb_match_id(intf, pinnacle_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))) {
    +
    + dev_dbg(&dev->dev, "acceptable inbound endpoint found\n");
    + ep_in = ep;
    + ep_in->bmAttributes = USB_ENDPOINT_XFER_INT;
    +
    + /*
    + * setting seems to 1 seem to cause issues with
    + * Pinnacle timing out on transfer.
    + */
    + if (is_pinnacle)
    + 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))) {
    +
    + dev_dbg(&dev->dev, "acceptable outbound endpoint found\n");
    + ep_out = ep;
    + ep_out->bmAttributes = USB_ENDPOINT_XFER_INT;
    +
    + /*
    + * setting seems to 1 seem to cause issues with
    + * Pinnacle timing out on transfer.
    + */
    + if (is_pinnacle)
    + ep_out->bInterval = ep->bInterval;
    + else
    + ep_out->bInterval = 1;
    + }
    + }
    + if (ep_in == NULL) {
    + dev_dbg(&dev->dev, "inbound and/or endpoint not found\n");
    + return -ENODEV;
    + }
    + devnum = dev->devnum;
    + pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
    + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
    +
    + /* allocate kernel memory */
    + ir = kzalloc(sizeof(struct irctl), GFP_KERNEL);
    + if (!ir)
    + return -ENOMEM;
    +
    + ir->buf_in = usb_buffer_alloc(dev, maxp, GFP_ATOMIC, &ir->dma_in);
    + if (!ir->buf_in) {
    + ret = -ENOMEM;
    + goto free_mem;
    + }
    + ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
    + if (!ir->urb_in) {
    + ret = -ENOMEM;
    + goto free_buffer;
    + }
    +
    + ir->usbdev = dev;
    + ir->len_in = maxp;
    + ir->flags.connected = 0;
    + ir->flags.pinnacle = is_pinnacle;
    + ir->flags.transmitter_mask_inverted =
    + usb_match_id(intf, xmit_inverted) ? 0 : 1;
    +
    + /* Saving usb interface data for use by the transmitter routine */
    + ir->usb_ep_in = ep_in;
    + ir->usb_ep_out = ep_out;
    +
    + name[0] = '\0';
    + if (dev->descriptor.iManufacturer)
    + usb_string(dev, dev->descriptor.iManufacturer, name, sizeof(name));
    + i = strlen(name);
    + name[i++] = ' ';
    + if (dev->descriptor.iProduct)
    + usb_string(dev, dev->descriptor.iProduct, &name[i], sizeof(name) - i);
    + dev_info(&dev->dev, "%s\n", name);
    +
    + /* inbound data */
    + usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in,
    + maxp, (usb_complete_t) usb_remote_recv, ir, ep_in->bInterval);
    +
    + /* 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);
    + dev_info(&dev->dev, "usb reset config ret %x\n", usbret);
    +
    + /*
    + * its possible we really should wait for a return
    + * for each of these...
    + */
    + request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_RECEIVE);
    + request_packet_async(ir, ep_out, pin_init1, sizeof(pin_init1), PHILUSB_SEND);
    + request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_RECEIVE);
    + request_packet_async(ir, ep_out, pin_init2, sizeof(pin_init2), PHILUSB_SEND);
    + request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_RECEIVE);
    + request_packet_async(ir, ep_out, pin_init3, sizeof(pin_init3), PHILUSB_SEND);
    + /* if we dont issue the correct number of receives
    + * (PHILUSB_RECEIVE) 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
    + * usb_remote_recv routine will handle the control packets OK -
    + * they start with 0x9f - but the async callback doesnt handle
    + * ir pulse packets
    + */
    + request_packet_async(ir, ep_in, NULL, maxp, 0);
    + } else {
    + request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_RECEIVE);
    + request_packet_async(ir, ep_out, init1, sizeof(init1), PHILUSB_SEND);
    + request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_RECEIVE);
    + request_packet_async(ir, ep_out, init2, sizeof(init2), PHILUSB_SEND);
    + request_packet_async(ir, ep_in, NULL, maxp, 0);
    + }
    + usb_set_intfdata(intf, ir);
    +
    + ir->input = input_allocate_device();
    + if (!ir->input) {
    + ret = -ENOMEM;
    + goto free_urb;
    + }
    + ret = input_ir_create(ir->input, ir, send);
    + if (ret)
    + goto free_input;
    +
    + ir->input->name = " Microsoft MCE2 IR Transceiver";
    + ret = input_register_device(ir->input);
    + if (ret)
    + goto free_input;
    +
    + return 0;
    +
    +free_input:
    + input_ir_destroy(ir->input);
    + input_free_device(ir->input);
    +free_urb:
    + usb_free_urb(ir->urb_in);
    +free_buffer:
    + usb_buffer_free(dev, maxp, ir->buf_in, ir->dma_in);
    +free_mem:
    + kfree(ir);
    + dev_err(&dev->dev, "failed to load (code=%d)\n", ret);
    + return ret;
    +}
    +
    +
    +static void usb_remote_disconnect(struct usb_interface *intf)
    +{
    + struct usb_device *dev = interface_to_usbdev(intf);
    + struct irctl *ir = usb_get_intfdata(intf);
    +
    + usb_set_intfdata(intf, NULL);
    +
    + if (!ir)
    + return;
    +
    + ir->usbdev = NULL;
    +
    + 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);
    +}
    +
    +static int usb_remote_suspend(struct usb_interface *intf, pm_message_t message)
    +{
    + struct irctl *ir = usb_get_intfdata(intf);
    + dev_dbg(&ir->usbdev->dev, "suspend\n");
    + usb_kill_urb(ir->urb_in);
    + return 0;
    +}
    +
    +static int usb_remote_resume(struct usb_interface *intf)
    +{
    + struct irctl *ir = usb_get_intfdata(intf);
    + dev_dbg(&ir->usbdev->dev, "resume\n");
    + if (usb_submit_urb(ir->urb_in, GFP_ATOMIC))
    + return -EIO;
    + return 0;
    +}
    +
    +static struct usb_driver usb_remote_driver = {
    + .name = DRIVER_NAME,
    + .probe = usb_remote_probe,
    + .disconnect = usb_remote_disconnect,
    + .suspend = usb_remote_suspend,
    + .resume = usb_remote_resume,
    + .id_table = usb_remote_table
    +};
    +
    +static int __init usb_remote_init(void)
    +{
    + int ret;
    +
    + ret = usb_register(&usb_remote_driver);
    + if (ret < 0) {
    + printk(DRIVER_NAME ": usb register failed, result = %d\n", ret);
    + return ret;
    + }
    + return 0;
    +}
    +
    +static void __exit usb_remote_exit(void)
    +{
    + usb_deregister(&usb_remote_driver);
    +}
    +
    +module_init(usb_remote_init);
    +module_exit(usb_remote_exit);
    +
    +MODULE_DESCRIPTION(DRIVER_DESC);
    +MODULE_AUTHOR(DRIVER_AUTHOR);
    +MODULE_LICENSE("GPL");
    +MODULE_DEVICE_TABLE(usb, usb_remote_table);


    \
     
     \ /
      Last update: 2008-09-29 18:21    [W:4.626 / U:0.104 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site