satip-axe/kernel/drivers/char/lirc/lirc_mceusb2.c

1146 lines
30 KiB
C

/*
* 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
*
*/
#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 <linux/lirc.h>
#include "kcompat.h"
#include "lirc_dev.h"
#define DRIVER_VERSION "$Revision: 1.81 $"
#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 for LIRC"
#define DRIVER_NAME "lirc_mceusb2"
#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 PHILUSB_INBOUND 1
#define PHILUSB_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[] = {
/* 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) },
/* 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) },
/* 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) },
/* Northstar Systems eHome Infrared Transceiver */
{ USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) },
/* Terminating entry */
{ }
};
static struct usb_device_id pinnacle_list[] = {
{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
{}
};
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, 0x0007) },
{ USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
{ USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
{}
};
/* data structure for each usb transceiver */
struct mceusb2_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;
/* 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 reserved:29;
} 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 mceusb2_dev *ir, char *buf, int len)
{
char codes[USB_BUFLEN*3 + 1];
int i;
if (len <= 0)
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 mceusb2_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 mceusb2_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 (likely(async_urb)) {
/* alloc buffer */
async_buf = kmalloc(size, GFP_KERNEL);
if (async_buf) {
if (urb_type == PHILUSB_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 {
usb_free_urb(async_urb);
return;
}
}
} 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 mceusb2_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 set_use_inc(void *data)
{
struct mceusb2_dev *ir = data;
if (!ir) {
printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
return -EIO;
}
dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
MOD_INC_USE_COUNT;
if (!ir->flags.connected) {
if (!ir->usbdev)
return -ENOENT;
ir->flags.connected = 1;
}
return 0;
}
static void set_use_dec(void *data)
{
struct mceusb2_dev *ir = data;
if (!ir) {
printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
return;
}
dprintk(DRIVER_NAME "[%d]: set use dec\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 mceusb2_dev *ir)
{
if (ir->lircdata != 0) {
lirc_buffer_write(ir->d->rbuf,
(unsigned char *) &ir->lircdata);
wake_up(&ir->d->rbuf->wait_poll);
ir->lircdata = 0;
}
}
static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
{
struct mceusb2_dev *ir;
int buf_len, packet_len;
int i, j;
if (!urb)
return;
ir = urb->context;
if (!ir) {
urb->transfer_flags |= URB_ASYNC_UNLINK;
usb_unlink_urb(urb);
return;
}
buf_len = urb->actual_length;
packet_len = 0;
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:
for (i = 0; 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;
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
*/
/*
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);
*/
/* end decode loop */
i = 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 lirc_write(struct file *file, const char *buf,
size_t n, loff_t *ppos)
{
int i, count = 0, cmdcount = 0;
struct mceusb2_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, PHILUSB_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 mceusb2_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 mceusb2_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),
PHILUSB_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),
PHILUSB_OUTBOUND);
return carrier;
}
}
return -EINVAL;
}
return carrier;
}
static int lirc_ioctl(struct inode *node, struct file *filep,
unsigned int cmd, unsigned long arg)
{
int result;
unsigned int ivalue;
unsigned long lvalue;
struct mceusb2_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 = lirc_write,
.ioctl = lirc_ioctl,
};
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 mceusb2_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;
dprintk(DRIVER_NAME ": 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))) {
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 mceusb2_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 = &set_use_inc;
driver->set_use_dec = &set_use_dec;
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->flags.connected = 0;
ir->flags.pinnacle = is_pinnacle;
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, PHILUSB_INBOUND);
request_packet_async(ir, ep_out, pin_init1, sizeof(pin_init1),
PHILUSB_OUTBOUND);
request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
request_packet_async(ir, ep_out, pin_init2, sizeof(pin_init2),
PHILUSB_OUTBOUND);
request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
request_packet_async(ir, ep_out, pin_init3, sizeof(pin_init3),
PHILUSB_OUTBOUND);
/* if we don't issue the correct number of receives
* (PHILUSB_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
* mceusb_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);
} else {
request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
request_packet_async(ir, ep_out, init1,
sizeof(init1), PHILUSB_OUTBOUND);
request_packet_async(ir, ep_in, NULL, maxp, PHILUSB_INBOUND);
request_packet_async(ir, ep_out, init2,
sizeof(init2), PHILUSB_OUTBOUND);
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 mceusb2_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 mceusb2_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 mceusb2_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,
#endif
.id_table = mceusb_dev_table
};
static int __init mceusb_dev_init(void)
{
int i;
printk(KERN_INFO "\n");
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);
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");
EXPORT_NO_SYMBOLS;