[mythtv-users] Re: USB MCE IR Receiver

Joe Huffner huffner at yahoo.com
Mon Mar 14 04:20:20 UTC 2005


Just an FYI for those of us who bought one of the
updated MCE remotes and receivers. I was checking out
the lirc mailing list and someone has update the
driver to include support. From what I can tell the
update has not been updated in CVS.

I found the info at:
http://search.gmane.org/?query=mce&group=gmane.comp.hardware.lirc

Basically what I did was overwrite the lirc_mceusb.c
in the drivers/lirc_mceusb directory with the source
found in the mailing liste. (I have attached the new
source file.) Then went through the install process
documented. I used version .7.0 of lirc.

Please note, from what I have read and experienced,
there are some initialization problems. What seemed to
"fix it" was unplugging the receiver and plugging it
back in, until I no longer got an error message
stating the device did not initialize. You can check
this by running "tail -f /var/log/messages". Also, I
read that running kernel 2.6.11 seemed to fix the
initialization problem. However I cannot confirm this
as I am running Gentoo Dev Kernel 2.6.9-r9. Another
problem I am having is that I am getting non-stop
error messages stating:

"usb 2-2: bulk timeout on ep1in"

In fact, I was getting about 4 of those messages per
second. If/when I get time I am planning to upgrade to
2.6.11 to see if this fixes the issue.

-joe

--- Jim Oltman <cnliberal at gmail.com> wrote:
> After deleting the section in my rc.local about the
> mknod /dev/lirc0 c
> 61 0 and replacing it with:
> 
> ln -s /dev/lircd /dev/lirc0
> 
> I am no longer getting errors from lircd in the
> /var/log/messages. 
> IRW runs but I am not seeing anything when I press
> buttons on my
> Hauppauge remote (it has also been setup in the
> correct lircrc files).
>  When I run irrecord I am getting errors about lircd
> not being
> started.  I am going to try commenting out the other
> section in
> rc.local pertaining to MCEUSB and see what happens.
> 
> 
> On Sun, 13 Mar 2005 18:57:27 -0600, Jim Oltman
> <cnliberal at gmail.com> wrote:
> > Here is where I am at so far.  In my
> /etc/rc.d/rc.local I made these changes...
> > 
> > #!/bin/sh
> > #
> > # This script will be executed *after* all the
> other init scripts.
> > # You can put your own initialization stuff in
> here if you don't
> > # want to do the full Sys V style init stuff.
> > 
> > touch /var/lock/subsys/local
> > mknod /dev/lirc0 c 61 0 -------------I added this
> line in hopes it
> > would attach to a line in my modprobe
> > 
> > /usr/X11R6/bin/xset -dpms
> > /usr/X11R6/bin/xset s off
> > /usr/bin/mtd --daemon
> > 
> > # I added these next two lines
> > 
> > /sbin/modprobe lirc_mceusb
> > /usr/sbin/lircd --device=/dev/lirc0
> --output=/dev/lircd
> > 
> > In my /etc/modprobe.conf I have added this line:
> > 
> > alias char-major-61 lirc_mceusb
> > 
> > After implementing these changes, I can do a cat
> /var/log/messages and
> > see what I showed you before about the driver
> being loaded for the USB
> > Microsoft IR Transceiver.  But I also get this
> listed:
> > 
> > Mar 13 18:54:11 mythfrontend lircd 0.7.0[4134]:
> accepted new client on
> > /dev/lircd
> > Mar 13 18:54:11 mythfrontend lircd 0.7.0[4134]:
> could not open /dev/lirc0
> > Mar 13 18:54:11 mythfrontend lircd 0.7.0[4134]:
> default_init(): No such device
> > Mar 13 18:54:11 mythfrontend lircd 0.7.0[4134]:
> caught signal
> > 
> > I am however getting errors on boot in the
> Show/Hide Details window.
> > Where can I view the errors that it is showing me?
>  They disappear to
> > quickly to read.  I am pretty sure the info I want
> to see (about the
> > errors on boot in Show/Hide window) is not listed
> in
> > /var/log/messages.
> > 
> > On Sun, 13 Mar 2005 18:38:09 -0600, Ron Johnson
> <ron.l.johnson at cox.net> wrote:
> > > On Sun, 2005-03-13 at 17:58 -0600, Jim Oltman
> wrote:
> > > > What I meant in that statement is that I got
> it to recognize the
> > > > receiver in the /var/log/messages but it is
> still not working.
> > >
> > > Yes, but *how* did you get "it" to recognize the
> receiver?
> > >
> > > P.S. - Please do not erase the whole text of the
> email you are
> > > replying to.  That makes it quite difficult to
> follow the thread.
> > >
> > > --
> > >
>
-----------------------------------------------------------------
> > > Ron Johnson, Jr.
> > > Jefferson, LA USA
> > > PGP Key ID 8834C06B I prefer encrypted mail.
> > >
> > > Legality/morality of using open wireless points:
> > > "If I leave my door unlocked are you going to
> come into my house
> > > in the middle of the night because you need to
> use the restroom?
> > > I pay a fixed rate for water. It's cold water so
> there is no
> > > electricity usage. No financial loss. I have 2.5
> bathrooms, so no
> > > loss of usage on my end. Is this OK? Please, try
> this and we'll
> > > see if it's OK."
> > >
>
http://www.warchalking.org/comments/2002/9/22/223831/236/135
> > >
> > >
> > > _______________________________________________
> > > mythtv-users mailing list
> > > mythtv-users at mythtv.org
> > >
>
http://mythtv.org/cgi-bin/mailman/listinfo/mythtv-users
> > >
> > >
> > >
> > >
> >
> > _______________________________________________
> mythtv-users mailing list
> mythtv-users at mythtv.org
>
http://mythtv.org/cgi-bin/mailman/listinfo/mythtv-users
> 

__________________________________________________
Do You Yahoo!?
Tired of spam?  Yahoo! Mail has the best spam protection around 
http://mail.yahoo.com 
-------------- next part --------------
/*
 * USB Microsoft IR Transceiver driver - 0.2
 *
 * Copyright (c) 2003-2004 Dan Conti (dconti at ...)
 *
 * This driver is based on the USB skeleton driver packaged with the
 * kernel, and the notice from that package has been retained below.
 *
 * The Microsoft IR Transceiver is a neat little IR receiver with two
 * emitters on it designed for Windows Media Center. This driver might
 * work for all media center remotes, but I have only tested it with
 * the philips model. The first revision of this driver only supports
 * the receive function - the transmit function will be much more
 * tricky due to the nature of the hardware. Microsoft chose to build
 * this device inexpensively, therefore making it extra dumb.
 * There is no interrupt endpoint on this device; all usb traffic
 * happens over two bulk endpoints. As a result of this, poll() for
 * this device is an actual hardware poll (instead of a receive queue
 * check) and is rather expensive.
 *
 * All trademarks property of their respective owners. This driver was
 * originally based on the USB skeleton driver, although significant
 * portions of that code have been removed as the driver has evolved.
 *
 * 2003_11_11 - Restructured to minimalize code interpretation in the
 *              driver. The normal use case will be with lirc.
 *
 * 2004_01_01 - Removed all code interpretation. Generate mode2 data
 *              for passing off to lirc. Cleanup
 *
 * 2004_01_04 - Removed devfs handle. Put in a temporary workaround
 *              for a known issue where repeats generate two
 *              sequential spaces (last_was_repeat_gap)
 *
 * 2004_02_17 - Changed top level api to no longer use fops, and
 *              instead use new interface for polling via
 *              lirc_thread. Restructure data read/mode2 generation to
 *              a single pass, reducing number of buffers. Rev to .2
 *
 * 2004_02_27 - Last of fixups to plugin->add_to_buf API. Properly
 *              handle broken fragments from the receiver. Up the
 *              sample rate and remove any pacing from
 *              fetch_more_data. Fixes all known issues.
 *
 * 2005_02_17 - Updated to support new Phillips remote.
 * 
 * TODO
 *   - Fix up minor number, registration of major/minor with usb subsystem
 *
 */
/*
 * USB Skeleton driver - 1.1
 *
 * Copyright (C) 2001-2003 Greg Kroah-Hartman (greg at ...)
 *
 *	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, version 2.
 *
 *
 * This driver is to be used as a skeleton driver to be able to create a
 * USB driver quickly.  The design of it is based on the usb-serial and
 * dc2xx drivers.
 *
 * Thanks to Oliver Neukum, David Brownell, and Alan Stern for their help
 * in debugging this driver.
 *
 *
 * History:
 *
 * 2003-05-06 - 1.1 - changes due to usb core changes with usb_register_dev()
 * 2003-02-25 - 1.0 - fix races involving urb->status, unlink_urb(), and
 *			disconnect.  Fix transfer amount in read().  Use
 *			macros instead of magic numbers in probe().  Change
 *			size variables to size_t.  Show how to eliminate
 *			DMA bounce buffer.
 * 2002_12_12 - 0.9 - compile fixes and got rid of fixed minor array.
 * 2002_09_26 - 0.8 - changes due to USB core conversion to struct device
 *			driver.
 * 2002_02_12 - 0.7 - zero out dev in probe function for devices that do
 *			not have both a bulk in and bulk out endpoint.
 *			Thanks to Holger Waechtler for the fix.
 * 2001_11_05 - 0.6 - fix minor locking problem in skel_disconnect.
 *			Thanks to Pete Zaitcev for the fix.
 * 2001_09_04 - 0.5 - fix devfs bug in skel_disconnect. Thanks to wim delvaux
 * 2001_08_21 - 0.4 - more small bug fixes.
 * 2001_05_29 - 0.3 - more bug fixes based on review from linux-usb-devel
 * 2001_05_24 - 0.2 - bug fixes based on review from linux-usb-devel people
 * 2001_05_01 - 0.1 - first version
 *
 */

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/usb.h>
#ifdef KERNEL_2_5
#include <linux/completion.h>
#include <asm/uaccess.h>
#else
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/signal.h>
#endif

#ifdef CONFIG_USB_DEBUG
	static int debug = 1;
#else
	static int debug = 0;
#endif

#include "drivers/kcompat.h"
#include "drivers/lirc.h"
#include "drivers/lirc_dev/lirc_dev.h"

/* Use our own dbg macro */
#define dprintk(fmt, args...)                             \
	do{                                               \
		if(debug) printk(KERN_DEBUG __FILE__ ": " \
				 fmt "\n", ## args);      \
	}while(0)

/* Version Information */
#define DRIVER_VERSION "v0.2"
#define DRIVER_AUTHOR "Dan Conti, dconti at ..."
#define DRIVER_DESC "USB Microsoft IR Transceiver Driver"
#define DRIVER_NAME "lirc_mceusb"

/* IDs for old and new products */
#define USB_MCEUSB_VENDOR_ID1	0x045e
#define USB_MCEUSB_PRODUCT_ID1	0x006d
#define USB_MCEUSB_VENDOR_ID2	0x0471
#define USB_MCEUSB_PRODUCT_ID2	0x0815

/* table of devices that work with this driver */
static struct usb_device_id mceusb_table [] = {
	{ USB_DEVICE(USB_MCEUSB_VENDOR_ID1, USB_MCEUSB_PRODUCT_ID1) },
	{ USB_DEVICE(USB_MCEUSB_VENDOR_ID2, USB_MCEUSB_PRODUCT_ID2) },
	{ } /* Terminating entry */
};

/* we can have up to this number of device plugged in at once */
#define MAX_DEVICES		16

/* Structure to hold all of our device specific stuff */
struct usb_skel {
	struct usb_device *	    udev;		/* save off the usb device pointer */
	struct usb_interface *	interface;		/* the interface for this device */
	unsigned char   minor;				/* the starting minor number for this device */
	unsigned char   num_ports;			/* the number of ports this device has */
	char            num_interrupt_in;		/* number of interrupt in endpoints we have */
	char            num_bulk_in;			/* number of bulk in endpoints we have */
	char            num_bulk_out;			/* number of bulk out endpoints we have */

	unsigned char *    bulk_in_buffer;		/* the buffer to receive data */
	int                bulk_in_size;		/* the size of the receive buffer */
	__u8               bulk_in_endpointAddr;	/* the address of the bulk in endpoint */

	unsigned char *    bulk_out_buffer;		/* the buffer to send data */
	int	               bulk_out_size;		/* the size of the send buffer */
	struct urb *       write_urb;			/* the urb used to send data */
	__u8               bulk_out_endpointAddr;	/* the address of the bulk out endpoint */

	atomic_t		write_busy;		/* true iff write urb is busy */
	struct completion	write_finished;		/* wait for the write to finish */
	
	wait_queue_head_t  wait_q;			/* for timeouts */
	int                open_count;			/* number of times this port has been opened */
	struct semaphore   sem;				/* locks this structure */
	
	int			present;		/* if the device is not disconnected */		

	struct lirc_plugin* plugin;
	
	lirc_t lircdata[256];          			/* place to store values until lirc processes them */
	int    lircidx;                			/* current index */
	int    lirccnt;                			/* remaining values */
	
	int    usb_valid_bytes_in_bulk_buffer;		/* leftover data from a previous read */
	int    mce_bytes_left_in_packet;		/* for packets split across multiple reads */
	
	int    header_bytes;	/* Older remote sends 2-byte header */
	int    remote_type;		/* 1 for older remotes, 2 for newer */
	
	/* Value to hold the last received space; 0 if last value
	 * received was a pulse
	 */
	int    last_space;
	
#ifdef KERNEL_2_5
	dma_addr_t dma_in;
	dma_addr_t dma_out;
#endif
};

#define MCE_TIME_UNIT 50

/* driver api */
#ifdef KERNEL_2_5
static int mceusb_probe		(struct usb_interface *interface, const struct usb_device_id *id);
static void mceusb_disconnect	(struct usb_interface *interface);
static void mceusb_write_bulk_callback	(struct urb *urb, struct pt_regs *regs);
#else
static void * mceusb_probe	(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id);
static void mceusb_disconnect	(struct usb_device *dev, void *ptr);
static void mceusb_write_bulk_callback	(struct urb *urb);
#endif

/* read data from the usb bus; convert to mode2 */
static int msir_fetch_more_data( struct usb_skel* dev, int dont_block );

/* helper functions */
static void msir_cleanup( struct usb_skel* dev );
static void set_use_dec( void* data );
static int set_use_inc( void* data );

/* array of pointers to our devices that are currently connected */
static struct usb_skel		*minor_table[MAX_DEVICES];

/* lock to protect the minor_table structure */
static DECLARE_MUTEX (minor_table_mutex);
static void mceusb_setup1( struct usb_device *udev );
static void mceusb_setup2( struct usb_device *udev, struct usb_skel* dev );

/* usb specific object needed to register this driver with the usb subsystem */
static struct usb_driver mceusb_driver = {
	.owner =	THIS_MODULE,
	.name =		DRIVER_NAME,
	.probe =	mceusb_probe,
	.disconnect =	mceusb_disconnect,
	.id_table =	mceusb_table,
};

/**
 *	usb_mceusb_debug_data
 */
static inline void usb_mceusb_debug_data (const char *function, int size,
					  const unsigned char *data)
{
	int i;
	if (!debug)
		return;
	
	printk(KERN_DEBUG __FILE__": %s - length = %d, data = ", 
	       function, size);
	for (i = 0; i < size; ++i) {
		printk(KERN_DEBUG "%.2x ", data[i]);
	}
	printk(KERN_DEBUG "\n");
}

/**
 *mceusb_delete
 */
static inline void mceusb_delete (struct usb_skel *dev)
{
	dprintk("%s", __func__);
	minor_table[dev->minor] = NULL;
#ifdef KERNEL_2_5
	usb_buffer_free(dev->udev, dev->bulk_in_size, dev->bulk_in_buffer, dev->dma_in);
	usb_buffer_free(dev->udev, dev->bulk_out_size, dev->bulk_out_buffer, dev->dma_out);
#else
	if (dev->bulk_in_buffer != NULL)
		kfree (dev->bulk_in_buffer);
	if (dev->bulk_out_buffer != NULL)
		kfree (dev->bulk_out_buffer);
#endif
	if (dev->write_urb != NULL)
		usb_free_urb (dev->write_urb);
	kfree (dev);
}

/* Setup routine for older MS remote */
static void mceusb_setup1( struct usb_device *udev )
{
	char data[8];
	int res;
	
	memset( data, 0, 8 );

	/* Get Status */
	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
			      USB_REQ_GET_STATUS, USB_DIR_IN,
			      0, 0, data, 2, HZ * 3);

	/*    res = usb_get_status( udev, 0, 0, data ); */
	dprintk("%s - res = %d status = 0x%x 0x%x", __func__,
		res, 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 );

	res = usb_control_msg( udev, usb_rcvctrlpipe(udev, 0),
			       5, USB_TYPE_VENDOR, 0, 0,
			       data, 2, HZ * 3 );
	dprintk("%s - res = %d, devnum = %d", __func__, res, udev->devnum);
	dprintk("%s - data[0] = %d, data[1] = %d", __func__,
		data[0], data[1] );

	/* set feature */
	res = usb_control_msg( udev, usb_sndctrlpipe(udev, 0),
			       USB_REQ_SET_FEATURE, USB_TYPE_VENDOR,
			       0xc04e, 0x0000, NULL, 0, HZ * 3 );

	dprintk("%s - res = %d", __func__, res);

	/* These two are sent by the windows driver, but stall for
	 * me. I dont have an analyzer on the linux side so i can't
	 * see what is actually different and why the device takes
	 * issue with them
	 */
#if 0
	/* this is some custom control message they send */
	res = usb_control_msg( udev, usb_sndctrlpipe(udev, 0),
			       0x04, USB_TYPE_VENDOR,
			       0x0808, 0x0000, NULL, 0, HZ * 3 );

	dprintk("%s - res = %d", __func__, res);

	/* this is another custom control message they send */
	res = usb_control_msg( udev, usb_sndctrlpipe(udev, 0),
			       0x02, USB_TYPE_VENDOR,
			       0x0000, 0x0100, NULL, 0, HZ * 3 );

	dprintk("%s - res = %d", __func__, res);
#endif
}

/* Setup routine for newer Phillips remote */
static void mceusb_setup2( struct usb_device *udev, struct usb_skel* dev )
{
	char data[16];
	int res, size;
	
	memset( data, 0, 16 );
	res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
					USB_REQ_GET_STATUS, USB_DIR_IN, 0, 0,
					data, 2, HZ);
	dprintk("%s - Get Status: res = %d, status = 0x%x 0x%x", __func__, res, data[0], data[1]);
	
	memset( data, 0, 16 );
	res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
					USB_REQ_SET_CONFIGURATION, 0, 1, 0,
					data, 0, HZ);
	dprintk("%s - Set Configuration #1: res = %d", __func__, res);
	
	/* Doesn't seem strictly necessary, but the Windows driver does it */
	
	memset( data, 0, 16 );
	data[0] = 0;
	data[1] = 0xFF;
	data[2] = 0xAA;
	data[3] = 0xFF;
	data[4] = 0x0B;
		
	res = usb_bulk_msg(udev, usb_sndbulkpipe(udev, dev->bulk_out_endpointAddr),
		data, 5, &size, HZ);
	dprintk("%s - Send #1: res = %d, data = 0x%x 0x%x, size=%d, sendAddr=%d", __func__, res, data[0],
data[1], size, dev->bulk_out_endpointAddr);
	if (res == -ETIMEDOUT)
	{
		err("%s - Unsuccessful initialization", __func__);
		return;
	}
	
	memset( data, 0, 16 );
	res = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, dev->bulk_in_endpointAddr),
		data, 16, &size, HZ);
	dprintk("%s - Receive #1: res = %d, size=%d, receiveAddr=%d", __func__, res, size, dev->bulk_in_endpointAddr);
	
	memset( data, 0, 16 );
	data[0] = 0xFF;
	data[1] = 0x18;
	
	res = usb_bulk_msg(udev, usb_sndbulkpipe(udev, dev->bulk_out_endpointAddr),
		data, 2, &size, HZ);
	dprintk("%s - Send #2: res = %d, size=%d, sendAddr=%d", __func__, res, size, dev->bulk_out_endpointAddr);
	
	memset( data, 0, 16 );
	res = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, dev->bulk_in_endpointAddr),
		data, 16, &size, HZ * 5);
	dprintk("%s - Receive #2: res = %d, size=%d, receiveAddr=%d", __func__, res, size, dev->bulk_in_endpointAddr);
}

static void msir_cleanup( struct usb_skel* dev )
{
	memset( dev->bulk_in_buffer, 0, dev->bulk_in_size );

	dev->usb_valid_bytes_in_bulk_buffer = 0;

	dev->last_space = PULSE_MASK;

	dev->mce_bytes_left_in_packet = 0;
	dev->lircidx = 0;
	dev->lirccnt = 0;
	memset( dev->lircdata, 0, sizeof(dev->lircdata) );
}

static int set_use_inc(void* data)
{
	MOD_INC_USE_COUNT;
	return 0;
}

static void set_use_dec(void* data)
{
	/* check for unplug here */
	struct usb_skel* dev = (struct usb_skel*) data;
	if( !dev->udev )
	{ 
		lirc_unregister_plugin( dev->minor );
		lirc_buffer_free( dev->plugin->rbuf );
		kfree( dev->plugin->rbuf );
		kfree( dev->plugin );
	}
	
	MOD_DEC_USE_COUNT;
}

/*
 * msir_fetch_more_data
 *
 * The goal here is to read in more remote codes from the remote. In
 * the event that the remote isn't sending us anything, the caller
 * will block until a key is pressed (i.e. this performs phys read,
 * filtering, and queueing of data) unless dont_block is set to 1; in
 * this situation, it will perform a few reads and will exit out if it
 * does not see any appropriate data
 *
 * dev->sem should be locked when this function is called - fine grain
 * locking isn't really important here anyways
 *
 * This routine always returns the number of words available
 *
 */
static int msir_fetch_more_data( struct usb_skel* dev, int dont_block )
{
	int retries = 0;
	int words_to_read = 
		(sizeof(dev->lircdata)/sizeof(lirc_t)) - dev->lirccnt;
	int partial, this_read = 0;
	int bulkidx = 0;
	int bytes_left_in_packet = 0;
	signed char* signedp = (signed char*)dev->bulk_in_buffer;
	
	if( words_to_read == 0 )
		return dev->lirccnt;

	/* this forces all existing data to be read by lirc before we
	 * issue another usb command. this is the only form of
	 * throttling we have
	 */
	if( dev->lirccnt )
	{
		return dev->lirccnt;
	}

	/* reserve room for our leading space */
	if( dev->last_space )
		words_to_read--;
		
	while( words_to_read )
	{
		/* handle signals and USB disconnects */
		if( signal_pending(current) )
		{
			return dev->lirccnt ? dev->lirccnt : -EINTR;
		}
		if( !dev->udev )
		{
			return -ENODEV;
		}

		bulkidx = 0;

		/*
		 * perform data read (phys or from previous buffer)
		 */

		/* use leftovers if present, otherwise perform a read */
		if( dev->usb_valid_bytes_in_bulk_buffer )
		{
			this_read = partial = 
				dev->usb_valid_bytes_in_bulk_buffer;
			dev->usb_valid_bytes_in_bulk_buffer = 0;
		}
		else
		{
			int retval;

			this_read = dev->bulk_in_size;
			partial = 0;
			retval = usb_bulk_msg
				(dev->udev,
				 usb_rcvbulkpipe
				 (dev->udev, dev->bulk_in_endpointAddr),
				 (unsigned char*)dev->bulk_in_buffer,
				 this_read, &partial, HZ/4);
				 
			/* retry a few times on overruns; map all
			   other errors to -EIO */
			if( retval )
			{
				if( retval == -EOVERFLOW && 
				    retries < 5 )
				{
					retries++;
					interruptible_sleep_on_timeout
						( &dev->wait_q, HZ );
					continue;
				}
				else
				{
					return -EIO;
				}
			}

			retries = 0;
			if( partial )
				this_read = partial;

			/* skip the header */
			bulkidx += dev->header_bytes;

			/* check for empty reads (header only) */
			if (dev->header_bytes > 0 && this_read == dev->header_bytes)
			{
				/* assume no data */
				if( dont_block )
				{
					break;
				}

				/* sleep for a bit before performing
				   another read */
				interruptible_sleep_on_timeout
					( &dev->wait_q, 1 );
				continue;
			}
		}

		/*
		 * process data
		 */

		/* at this point this_read is > 0 */
		while( bulkidx < this_read &&
		       (words_to_read > (dev->last_space ? 1 : 0)) )
			//while( bulkidx < this_read && words_to_read )
		{
			int keycode;
			int pulse = 0;

			/* read packet length if needed */
			if( !bytes_left_in_packet )
			{
				
				/* we assume we are on a packet length
				 * value. it is possible, in some
				 * cases, to get a packet that does
				 * not start with a length, apparently
				 * due to some sort of fragmenting,
				 * but occaisonally we do not receive
				 * the second half of a fragment
				 */
				bytes_left_in_packet = 
					128 + signedp[bulkidx++];
					
									
				/* This is a bit of a hack. Basically,
				   timing out the bulk transfer on the
				   new remote delays key presses noticeably,
				   so this tries to quit early when it
				   reaches the end of transmission - Jim */
				if (bytes_left_in_packet == 31 && dev->remote_type == 2)
				{
					dev->mce_bytes_left_in_packet = 0;
					return dev->lirccnt;
				}

				/* unfortunately rather than keep all
				 * the data in the packetized format,
				 * the transceiver sends a trailing 8
				 * bytes that aren't part of the
				 * transmittion from the remote,
				 * aren't packetized, and dont really
				 * have any value. we can basically
				 * tell we have hit them if 1) we have
				 * a loooong space currently stored
				 * up, and 2) the bytes_left value for
				 * this packet is obviously wrong
				 */
				if( bytes_left_in_packet > 4  )
				{
					if( dev->mce_bytes_left_in_packet )
					{
						bytes_left_in_packet = dev->mce_bytes_left_in_packet;
						bulkidx--;
					}
					bytes_left_in_packet = 0;
					bulkidx = this_read;
				}

				/* always clear this if we have a
				   valid packet */
				dev->mce_bytes_left_in_packet = 0;

				/* continue here to verify we haven't
				   hit the end of the bulk_in */
				continue;
				
			}

			/*
			 * generate mode2
			 */

			keycode = signedp[bulkidx++];
			if( keycode < 0 )
			{
				pulse = 1;
				keycode += 128;
			}
			keycode *= MCE_TIME_UNIT;

			bytes_left_in_packet--;

			if( pulse )
			{
				if( dev->last_space )
				{
					dev->lircdata[dev->lirccnt++] =
						dev->last_space;
					dev->last_space = 0;
					words_to_read--;

					/* clear the lirc_t for the pulse */
					dev->lircdata[dev->lirccnt] = 0;
				}
				dev->lircdata[dev->lirccnt] += keycode;
				dev->lircdata[dev->lirccnt] |= PULSE_BIT;
			}
			else
			{
				/* on pulse->space transition, add one
				   for the existing pulse */
				if( dev->lircdata[dev->lirccnt] &&
				    !dev->last_space )
				{
					dev->lirccnt++;
					words_to_read--;
				}

				dev->last_space += keycode;
			}
		}
	}
	
	/* save off some info if we are exiting mid-packet, or with
	   leftovers */
	if( bytes_left_in_packet )
	{
		dev->mce_bytes_left_in_packet = bytes_left_in_packet;
	}
	if( bulkidx < this_read )
	{
		dev->usb_valid_bytes_in_bulk_buffer = (this_read - bulkidx);
		memcpy( dev->bulk_in_buffer, &(dev->bulk_in_buffer[bulkidx]),
			dev->usb_valid_bytes_in_bulk_buffer );
	}
	return dev->lirccnt;
}

/* mceusb_add_to_buf: called by lirc_dev to fetch all available keys
 * this is used as a polling interface for us: since we set
 * plugin->sample_rate we will periodically get the below call to
 * check for new data returns 0 on success, or -ENODATA if nothing is
 * available
 */
static int mceusb_add_to_buf(void* data, struct lirc_buffer* buf )
{
	struct usb_skel* dev = (struct usb_skel*) data;

	down( &dev->sem );

	/* verify device still present */
	if( dev->udev == NULL )
	{
		up( &dev->sem );
		return -ENODEV;
	}

	if( !dev->lirccnt )
	{
		int res;
		dev->lircidx = 0;

		res = msir_fetch_more_data( dev, 1 );
		
		if( res == 0 )
			res = -ENODATA;
		if( res < 0 ) {
			up( &dev->sem );
			return res;
		}
	}

	if( dev->lirccnt )
	{
		int keys_to_copy;

		/* determine available buffer space and available data */
		keys_to_copy = lirc_buffer_available( buf );
		if( keys_to_copy > dev->lirccnt )
		{
			keys_to_copy = dev->lirccnt;
		}

		lirc_buffer_write_n( buf, (unsigned char*) &(dev->lircdata[dev->lircidx]), keys_to_copy );
		dev->lircidx += keys_to_copy;
		dev->lirccnt -= keys_to_copy;

		up( &dev->sem );
		return 0;
	}

	up( &dev->sem );
	return -ENODATA;
}

/**
 *	mceusb_write_bulk_callback
 */
#ifdef KERNEL_2_5 
static void mceusb_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
#else
static void mceusb_write_bulk_callback (struct urb *urb)
#endif
{
	struct usb_skel *dev = (struct usb_skel *)urb->context;

	dprintk("%s - minor %d", __func__, dev->minor);

	if ((urb->status != -ENOENT) && 
	    (urb->status != -ECONNRESET)) {
		dprintk("%s - nonzero write buld status received: %d",
			__func__, urb->status);
		return;
	}

	return;
}

/**
 *	mceusb_probe
 *
 *	Called by the usb core when a new device is connected that it 
 *	thinks this driver might be interested in.
 */
#ifdef KERNEL_2_5 
static int mceusb_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
	struct usb_device *udev = interface_to_usbdev(interface);
	struct usb_host_interface *iface_desc;
#else
static void * mceusb_probe(struct usb_device *udev, unsigned int ifnum,
			   const struct usb_device_id *id)
{
	struct usb_interface *interface = &udev->actconfig->interface[ifnum];
	struct usb_interface_descriptor *iface_desc;	
#endif
	struct usb_skel *dev = NULL;
	struct usb_endpoint_descriptor *endpoint;
	
	struct lirc_plugin* plugin;
	struct lirc_buffer* rbuf;

	int minor;
	size_t buffer_size;
	int i, remote_type;
	int retval = -ENOMEM;
	
	/* See if the device offered us matches what we can accept */
	if (udev->descriptor.idVendor == USB_MCEUSB_VENDOR_ID1 &&
		udev->descriptor.idProduct == USB_MCEUSB_PRODUCT_ID1) {
		remote_type = 1;
	}
	else if (udev->descriptor.idVendor == USB_MCEUSB_VENDOR_ID2 &&
			udev->descriptor.idProduct == USB_MCEUSB_PRODUCT_ID2) {
		remote_type = 2;
	}
	else
	{
		dprintk("Wrong Vendor/Product IDs");
		
#ifdef KERNEL_2_5
		return -ENODEV;
#else
		return NULL;
#endif
	}

	/* select a "subminor" number (part of a minor number) */
	down (&minor_table_mutex);
	for (minor = 0; minor < MAX_DEVICES; ++minor) {
		if (minor_table[minor] == NULL)
			break;
	}
	if (minor >= MAX_DEVICES) {
		info ("Too many devices plugged in, "
		      "can not handle this device.");
		goto error;
	}

	/* allocate memory for our device state and initialize it */
	dev = kmalloc (sizeof(struct usb_skel), GFP_KERNEL);
	if (dev == NULL) {
		err ("Out of memory");
#ifdef KERNEL_2_5
		retval = -ENOMEM;
#endif
		goto error;
	}
	minor_table[minor] = dev;
	
	memset (dev, 0x00, sizeof (*dev));
	init_MUTEX (&dev->sem);
	dev->udev = udev;
	dev->interface = interface;
	dev->minor = minor;

	/* set up the endpoint information */
	/* check out the endpoints */
	/* use only the first bulk-in and bulk-out endpoints */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,4)
	iface_desc = interface->cur_altsetting;
#else
	iface_desc = &interface->altsetting[0];
#endif

#ifdef KERNEL_2_5
	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
		endpoint = &iface_desc->endpoint[i].desc;
#else
	for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
		endpoint = &iface_desc->endpoint[i];
#endif
		if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) &&
		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
		     USB_ENDPOINT_XFER_BULK)) {
			dprintk("we found a bulk in endpoint");
			buffer_size = endpoint->wMaxPacketSize;
			dev->bulk_in_size = buffer_size;
			dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
#ifdef KERNEL_2_5
			dev->bulk_in_buffer = usb_buffer_alloc
				(udev, buffer_size, SLAB_ATOMIC, &dev->dma_in);
#else
			dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
#endif
			if (!dev->bulk_in_buffer) {
				err("Couldn't allocate bulk_in_buffer");
				goto error;
			}
		}
		
		if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == 0x00) &&
		    ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
		     USB_ENDPOINT_XFER_BULK)) {
			dprintk("we found a bulk out endpoint");
#ifdef KERNEL_2_5
			dev->write_urb = usb_alloc_urb(0, GFP_KERNEL);
#else
			dev->write_urb = usb_alloc_urb(0);
#endif
			if (!dev->write_urb) {
				err("No free urbs available");
				goto error;
			}
			buffer_size = endpoint->wMaxPacketSize;
			dev->bulk_out_size = buffer_size;
			dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
#ifdef KERNEL_2_5
			dev->bulk_out_buffer = usb_buffer_alloc(udev, buffer_size, SLAB_ATOMIC, &dev->dma_out);
#else
			dev->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);
#endif
			if (!dev->bulk_out_buffer) {
				err("Couldn't allocate bulk_out_buffer");
				goto error;
			}
#ifdef KERNEL_2_5
			usb_fill_bulk_urb(dev->write_urb, udev, 
				      usb_sndbulkpipe
				      (udev, endpoint->bEndpointAddress),
				      dev->bulk_out_buffer, buffer_size,
				      mceusb_write_bulk_callback, dev);
#else
			FILL_BULK_URB(dev->write_urb, udev,
				      usb_sndbulkpipe
				      (udev, endpoint->bEndpointAddress),
				      dev->bulk_out_buffer, buffer_size,
				      mceusb_write_bulk_callback, dev);
#endif
		}
	}

	if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) {
		err("Couldn't find both bulk-in and bulk-out endpoints");
		goto error;
	}

	/* init the waitq */
	init_waitqueue_head( &dev->wait_q );

	/* Set up our lirc plugin */
	if(!(plugin = kmalloc(sizeof(struct lirc_plugin), GFP_KERNEL))) {
		err("out of memory");
		goto error;
	}
	memset( plugin, 0, sizeof(struct lirc_plugin) );

	if(!(rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL))) {
		err("out of memory");
		kfree( plugin );
		goto error;
	}

	/* the lirc_atiusb module doesn't memset rbuf here ... ? */
	if( lirc_buffer_init( rbuf, sizeof(lirc_t), 128)) {
		err("out of memory");
		kfree( plugin );
		kfree( rbuf );
		goto error;
	}

	strcpy(plugin->name, DRIVER_NAME " ");
	plugin->minor       = minor;
	plugin->code_length = sizeof(lirc_t) * 8;
	plugin->features    = LIRC_CAN_REC_MODE2; // | LIRC_CAN_SEND_MODE2;
	plugin->data        = dev;
	plugin->rbuf        = rbuf;
	plugin->ioctl       = NULL;
	plugin->set_use_inc = &set_use_inc;
	plugin->set_use_dec = &set_use_dec;
	plugin->sample_rate = 80;   // sample at 100hz (10ms)
	plugin->add_to_buf  = &mceusb_add_to_buf;
	//    plugin->fops        = &mceusb_fops;
	if( lirc_register_plugin(plugin) < 0 )
	{
		kfree( plugin );
		lirc_buffer_free( rbuf );
		kfree( rbuf );
		goto error;
	}
	dev->plugin = plugin;
	
	/* 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
	 */
	{
		char junk[64];
		int partial = 0, retval;
		do
		{
			retval = usb_bulk_msg
					(udev, usb_rcvbulkpipe
					 (udev, dev->bulk_in_endpointAddr),
					 junk, 64,
					 &partial, HZ);
		} while (partial > 0);
	}

	msir_cleanup( dev );
	
	dev->remote_type = remote_type;
	if (remote_type == 1)
	{
		dev->header_bytes = 2;
		mceusb_setup1( udev );
	}
	else
	{
		dev->header_bytes = 0;
		mceusb_setup2( udev, dev );
	}
	
#ifdef KERNEL_2_5
	/* we can register the device now, as it is ready */
	usb_set_intfdata (interface, dev);
#endif	
	/* let the user know what node this device is now attached to */
	//info ("USB Microsoft IR Transceiver device now attached to msir%d", dev->minor);
	up (&minor_table_mutex);
#ifdef KERNEL_2_5
	return 0;
#else
	return dev;
#endif
 error:
	mceusb_delete (dev);
	dev = NULL;
	dprintk("%s: retval = %x", __func__, retval);
	up (&minor_table_mutex);
#ifdef KERNEL_2_5
	return retval;
#else
	return NULL;
#endif
}

/**
 *	mceusb_disconnect
 *
 *	Called by the usb core when the device is removed from the system.
 *
 *	This routine guarantees that the driver will not submit any more urbs
 *	by clearing dev->udev.  It is also supposed to terminate any currently
 *	active urbs.  Unfortunately, usb_bulk_msg(), used in skel_read(), does
 *	not provide any way to do this.  But at least we can cancel an active
 *	write.
 */
#ifdef KERNEL_2_5 
static void mceusb_disconnect(struct usb_interface *interface)
#else
static void mceusb_disconnect(struct usb_device *udev, void *ptr)
#endif
{
	struct usb_skel *dev;
	int minor;
#ifdef KERNEL_2_5
	dev = usb_get_intfdata (interface);
	usb_set_intfdata (interface, NULL);
#else
	dev = (struct usb_skel *)ptr;
#endif
	
	down (&minor_table_mutex);
	down (&dev->sem);
	minor = dev->minor;

	/* unhook lirc things */
	lirc_unregister_plugin( minor );
	lirc_buffer_free( dev->plugin->rbuf );
	kfree( dev->plugin->rbuf );
	kfree( dev->plugin );
#ifdef KERNEL_2_5
	/* terminate an ongoing write */
	if (atomic_read (&dev->write_busy)) {
		usb_unlink_urb (dev->write_urb);
		wait_for_completion (&dev->write_finished);
	}

	/* prevent device read, write and ioctl */
	dev->present = 0;
#endif
	
	mceusb_delete (dev);
	
	info("Microsoft IR Transceiver #%d now disconnected", minor);
	up (&dev->sem);
	up (&minor_table_mutex);
}

/**
 *	usb_mceusb_init
 */
static int __init usb_mceusb_init(void)
{
	int result;

	/* register this driver with the USB subsystem */
	result = usb_register(&mceusb_driver);
#ifdef KERNEL_2_5	
	if ( result ) {
#else
	if ( result < 0 ) {
#endif
		err("usb_register failed for the " DRIVER_NAME " driver. error number %d",result);
#ifdef KERNEL_2_5
		return result;
#else
		return -1;
#endif
	}

	info(DRIVER_DESC " " DRIVER_VERSION);
	return 0;
}

/**
 *	usb_mceusb_exit
 */
static void __exit usb_mceusb_exit(void)
{
	/* deregister this driver with the USB subsystem */
	usb_deregister(&mceusb_driver);
}

 module_init (usb_mceusb_init);
module_exit (usb_mceusb_exit);

MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE (usb, mceusb_table);

module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Debug enabled or not");

EXPORT_NO_SYMBOLS;


More information about the mythtv-users mailing list