Help with debugging no-callback problem with IrDA USB dongle

[email protected]
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
After a big delay, I am now trying to write a driver for my USB-to-Irda
dongle.
This device reports the following in lsusb:

Bus 002 Device 002: ID 07c0:4200 Code Mercenaries Hard- und Software GmbH
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0         8
  idVendor           0x07c0 Code Mercenaries Hard- und Software GmbH
  idProduct          0x4200
  bcdDevice            0.00
  iManufacturer           1 KingSun Co,Ltd
  iProduct                2 USB to Irda
  iSerial                 0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           41
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          4 Usb to Irda
    bmAttributes         0x80
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass     85
      bInterfaceProtocol      0
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval               1

My current attempt to write a driver for this dongle is attached to this
message. This is my very first attempt to write a Linux kernel driver, and
also
the first time I try to write an USB driver for any device whatsoever, so I
might be doing something stupid in the code. The problem I have is that
when I
submit the interrupt urbs for either endpoint in the driver code, my callback
code never gets called at all - not even to signal an error while
submitting the
urb. However, when the irda0 network interface is brought down manually
(ifconfig irda0 down), I do get calls to my callbacks, with errors
corresponding
to the device being shut down. I have a suspicion that this is caused by the
required urb teardown in kingsun_net_close.

Here is a dump of usbmon when my driver takes control of the dongle:

Plugin of the dongle:

ddf58140 410742248 C Ii:001:01 0 1 = 02
ddf58140 410742276 S Ii:001:01 -115 2 <
cb8ebb40 410793172 S Ci:001:00 s a3 00 0000 0001 0004 4 <
cb8ebb40 410793182 C Ci:001:00 0 4 = 01030100
cb8ebb40 410793184 S Co:001:00 s 23 01 0010 0001 0000 0
cb8ebb40 410793186 C Co:001:00 0 0
cb8ebb40 410793188 S Ci:001:00 s a3 00 0000 0001 0004 4 <
cb8ebb40 410793192 C Ci:001:00 0 4 = 01030000
cb8ebb40 410819126 S Ci:001:00 s a3 00 0000 0001 0004 4 <
cb8ebb40 410819137 C Ci:001:00 0 4 = 01030000
cb8ebb40 410845111 S Ci:001:00 s a3 00 0000 0001 0004 4 <
cb8ebb40 410845115 C Ci:001:00 0 4 = 01030000
cb8ebb40 410871106 S Ci:001:00 s a3 00 0000 0001 0004 4 <
cb8ebb40 410871115 C Ci:001:00 0 4 = 01030000
cb8ebb40 410897092 S Ci:001:00 s a3 00 0000 0001 0004 4 <
cb8ebb40 410897097 C Ci:001:00 0 4 = 01030000
cb8ebb40 410897111 S Co:001:00 s 23 03 0004 0001 0000 0
cb8ebb40 410897115 C Co:001:00 0 0
cb8ebb40 410948087 S Ci:001:00 s a3 00 0000 0001 0004 4 <
cb8ebb40 410948118 C Ci:001:00 0 4 = 03030000
cb8ebb40 410999075 S Co:001:00 s 23 01 0014 0001 0000 0
cb8ebb40 410999092 C Co:001:00 0 0
cb8ebb40 410999129 S Ci:000:00 s 80 06 0100 0000 0040 64 <
cb8ebb40 411005209 C Ci:000:00 0 18 = 12011001 00000008 c0070042 00000102
0001
cb8ebb40 411005244 S Co:001:00 s 23 03 0004 0001 0000 0
cb8ebb40 411005252 C Co:001:00 0 0
d98cd440 411056057 S Ci:001:00 s a3 00 0000 0001 0004 4 <
d98cd440 411056089 C Ci:001:00 0 4 = 03030000
d98cd440 411112032 S Co:001:00 s 23 01 0014 0001 0000 0
d98cd440 411112047 C Co:001:00 0 0
d98cd440 411112059 S Co:000:00 s 00 05 0007 0000 0000 0
d98cd440 411114193 C Co:000:00 0 0
d98cd440 411127008 S Ci:007:00 s 80 06 0100 0000 0012 18 <
d98cd440 411132176 C Ci:007:00 0 18 = 12011001 00000008 c0070042 00000102
0001
d98cd440 411132197 S Ci:007:00 s 80 06 0200 0000 0009 9 <
d98cd440 411137173 C Ci:007:00 0 9 = 09022900 01010480 32
d98cd440 411137182 S Ci:007:00 s 80 06 0200 0000 0029 41 <
d98cd440 411146172 C Ci:007:00 0 41 = 09022900 01010480 32090400 0002ff55
00000921 00010001 22250007 05810308
da870b40 411146189 S Ci:007:00 s 80 06 0300 0000 00ff 255 <
da870b40 411151170 C Ci:007:00 0 4 = 04030904
da870b40 411151178 S Ci:007:00 s 80 06 0302 0409 00ff 255 <
da870b40 416154270 C Ci:007:00 -2 24 = 18035500 53004200 20007400 6f002000
49007200 64006100
da870b40 416154298 S Ci:007:00 s 80 06 0302 0409 0002 2 <
da870b40 416158250 C Ci:007:00 0 2 = 1803
da870b40 416158265 S Ci:007:00 s 80 06 0302 0409 0018 24 <
da870b40 416164245 C Ci:007:00 0 24 = 18035500 53004200 20007400 6f002000
49007200 64006100
da870b40 416164259 S Ci:007:00 s 80 06 0301 0409 00ff 255 <
da870b40 416172242 C Ci:007:00 0 30 = 1e034b00 69006e00 67005300 75006e00
20004300 6f002c00 4c007400 6400
da870b40 416172552 S Co:007:00 s 00 09 0001 0000 0000 0
da870b40 416175265 C Co:007:00 0 0
da870b40 416175321 S Ci:007:00 s 80 06 0304 0409 00ff 255 <
da870b40 421178343 C Ci:007:00 -2 24 = 18035500 73006200 20007400 6f002000
49007200 64006100
da870b40 421178372 S Ci:007:00 s 80 06 0304 0409 0002 2 <
da870b40 421182327 C Ci:007:00 0 2 = 1803
da870b40 421182350 S Ci:007:00 s 80 06 0304 0409 0018 24 <
da870b40 421188325 C Ci:007:00 0 24 = 18035500 73006200 20007400 6f002000
49007200 64006100
da87f140 421243731 S Co:007:00 s 00 09 0001 0000 0000 0
da87f140 421246338 C Co:007:00 0 0

ifconfig irda0 up

da87fec0 474705925 S Co:007:00 s 02 01 0000 0081 0000 0
da87fec0 474708443 C Co:007:00 0 0
da87fec0 474708548 S Co:007:00 s 02 01 0000 0002 0000 0
da87fec0 474711422 C Co:007:00 0 0
da87fec0 474711537 S Ii:007:01 -115 8 <
da870b40 475671204 S Io:007:02 -115 30 = ffffffff ffffffff ffffffff
c0ff3f01 c5257160 ffffffff 01000064 d2c1

ifconfig irda0 down

da870b40 533710546 C Io:007:02 -2 0
da87fec0 533711539 C Ii:007:01 -2 0

Please notice that usbmon does log the IrDA discovery packet (the one that
starts with ffffffff), only my code does not get called for any response
on the
send.

I welcome any critique on the driver code, whether or not it help with the
problem of no callbacks.

Alex Villacís Lasso

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
kingsun.c (text/x-csrc, 18.5 KB)
/*****************************************************************************
*
* Filename:      kingsun.c
* Version:       0.1
* Description:   Irda KingSun USB Dongle
* Status:        Experimental
* Author:        Alex Villacís Lasso <[email protected]>
*
*  	Based on stir4200 and mcs7780 drivers, with (strange?) differences
*
*	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.
*
*	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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*****************************************************************************/

/*
 * This is my current (2007-04-25) understanding of how this dongle is supposed
 * to work. This is based on reverse-engineering and examination of the packet
 * data sent and received by the WinXP driver using USBSnoopy. Feel free to 
 * update here as more of this dongle is known:
 *
 * General: Unlike the other USB IrDA dongles, this particular dongle exposes, 
 * not two bulk (in and out) endpoints, but two *interrupt* ones. This dongle, 
 * like the bulk based ones (stir4200.c and mcs7780.c), requires polling in 
 * order to receive data. 
 * Transmission: Just like stir4200, this dongle uses a raw stream of data, 
 * which needs to be wrapped and escaped in a similar way as in stir4200.c. 
 * Reception: Poll-based, as in stir4200. Each read returns the contents of a
 * 8-byte buffer, of which the first byte (LSB) indicates the number of bytes
 * (1-7) of valid data contained within the remaining 7 bytes. For example, if
 * the buffer had the following contents:
 *  06 ff ff ff c0 01 04 aa
 * This means that (06) there are 6 bytes of valid data. The byte 0xaa at the 
 * end is garbage (left over from a previous reception) and is discarded. 
 * If a read returns an "impossible" value as the length of valid data (such as
 * 0x36) in the first byte, then the buffer is uninitialized (as is the case of
 * first plug-in) and its contents should be discarded. There is currently no
 * evidence that the top 5 bits of the first byte of the buffer can have values
 * other than 0 once reception begins. 
 * Once valid bytes are collected, the assembled stream is a sequence of wrapped
 * IrDA frames that is unwrapped and unescaped as in stir4200.c .
 * BIG FAT WARNING: the dongle does *not* reset the RX buffer in any way after a
 * successful read from the host, which means that in absence of further 
 * reception, repeated reads from the dongle will return the exact same contents
 * repeatedly. This driver attempts to deal with this quirk by remembering the
 * content of the last bufferful read, and parsing the next read only if is
 * different from the previous buffer.
 * Speed change: no commands observed so far to change speed, assumed fixed
 * 9600bps (SIR).
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kref.h>
#include <linux/usb.h>
#include <linux/device.h>
#include <linux/crc32.h>

#include <asm/unaligned.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>

#include <net/irda/irda.h>
#include <net/irda/wrapper.h>
#include <net/irda/crc.h>

/* According to lsusb, 0x07c0 is assigned to "Code Mercenaries Hard- und Software GmbH" */
#define KING_VENDOR_ID 0x07c0
#define KING_PRODUCT_ID 0x4200

/* These are the currently known USB ids */
static struct usb_device_id dongles[] = {
    /* KingSun Co,Ltd  IrDA/USB Bridge */
    { USB_DEVICE(KING_VENDOR_ID, KING_PRODUCT_ID) },
    { }
};

MODULE_DEVICE_TABLE(usb, dongles);

static int qos_mtt_bits = 0x07 /* > 1ms */ ;
module_param(qos_mtt_bits, int, 0);
MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time");

#define KINGSUN_FIFO_SIZE		4096
#define KINGSUN_EP_IN			0
#define KINGSUN_EP_OUT			1

struct kingsun_cb {
	struct usb_device *usbdev;      /* init: probe_irda */
	struct net_device *netdev;      /* network layer */
	struct irlap_cb   *irlap;       /* The link layer we are binded to */
	struct net_device_stats stats;	/* network statistics */
	struct qos_info   qos;

	__u8		  *in_buf;	/* receive buffer */
	__u8		  *out_buf;	/* transmit buffer */
	__u8		  *prev_rx; /* previous bytes received (copied from in_buf) */
	__u8		  max_rx;	/* max. atomic read from dongle (usually 8), also 
								size of both in_buf and prev_rx */
	__u8		  max_tx;	/* max. atomic write to dongle (usually 8) */

	iobuff_t  	  rx_buff;	/* receive unwrap state machine */
	struct timeval	  rx_time;
	spinlock_t lock;

	__u8 ep_in;
	__u8 ep_out;

	struct urb	 *tx_urb;
	struct urb	 *rx_urb;
};

/* Callback transmission routine */
static void kingsun_send_irq(struct urb *urb)
{
	struct kingsun_cb *kingsun = urb->context;
	struct net_device *netdev = kingsun->netdev;

	/* in process of stopping, just drop data */
	if (!netif_running(kingsun->netdev)) {
		err("kingsun_send_irq: Network not running!");
		return;
	}

	/* unlink, shutdown, unplug, other nasties */
	if (urb->status != 0) {
		err("kingsun_send_irq: urb asynchronously failed - %d", urb->status);
	}
	netif_wake_queue(netdev);
}

/*
 * Called from net/core when new frame is available.
 */
static int kingsun_hard_xmit(struct sk_buff *skb, struct net_device *netdev)
{
	struct kingsun_cb *kingsun;
	int wraplen;
	int ret = 0;

	if (skb == NULL || netdev == NULL)
		return -EINVAL;

	netif_stop_queue(netdev);
	/* the IRDA wrapping routines don't deal with non linear skb */
	SKB_LINEAR_ASSERT(skb);

	kingsun = netdev_priv(netdev);

	spin_lock(&kingsun->lock);

	/* Append data to the end of whatever data remains to be transmitted */
	wraplen = async_wrap_skb(skb, 
		kingsun->out_buf,
		KINGSUN_FIFO_SIZE);

	/* Calculate how much data can be transmitted in this urb */
	usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev,
		usb_sndintpipe(kingsun->usbdev, kingsun->ep_out),
		kingsun->out_buf, wraplen, kingsun_send_irq,
		kingsun, 1);

	if ((ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC))) {
		IRDA_ERROR("kingsun_hard_xmit: failed tx_urb submit: %d", ret);
		switch (ret) {
		case -ENODEV:
		case -EPIPE:
			break;
		default:
			kingsun->stats.tx_errors++;
			netif_start_queue(netdev);
		}
	} else {
		kingsun->stats.tx_packets++;
		kingsun->stats.tx_bytes += skb->len;
	}

	dev_kfree_skb(skb);
	spin_unlock(&kingsun->lock);
	return ret;
}

/* Receive callback function */
static void kingsun_rcv_irq(struct urb *urb)
{
	struct kingsun_cb *kingsun = urb->context;
	int ret;

	/* in process of stopping, just drop data */
	if (!netif_running(kingsun->netdev)) {
		err("kingsun_rcv_irq: Network not running!");
		return;
	}

	/* unlink, shutdown, unplug, other nasties */
	if (urb->status != 0) {
		err("kingsun_rcv_irq: urb asynchronously failed - %d", urb->status);
		return;
	}

	if (urb->actual_length == kingsun->max_rx) {
		__u8 *bytes = urb->transfer_buffer;
		int i;
		/* Check whether this buffer is byte-by-byte identical to the previous
		   buffer received. If so, this is a repeat read and should be 
		   discarded */
		if (0 != memcmp(kingsun->prev_rx, bytes, kingsun->max_rx)) {
			memcpy(kingsun->prev_rx, bytes, kingsun->max_rx);

			/* The very first byte in the buffer indicates the length of valid
			   data in the read. This byte must be in the range 
			   1..kingsun->max_rx -1 . Values outside this range indicate an
			   out-of-reach remote side (FIXME: any way to indicate this 
			   condition to the IrLAP layer?) */
			if (bytes[0] >= 1 && bytes[0] < kingsun->max_rx) {
            			for (i = 1; i <= bytes[0]; i++) {
					async_unwrap_char(kingsun->netdev, &kingsun->stats,
						&kingsun->rx_buff, bytes[i]);
				}
				kingsun->netdev->last_rx = jiffies;
				do_gettimeofday(&kingsun->rx_time);
			}
		}
	} else if (urb->actual_length > 0) {
	    err("kingsun_rcv_irq: Unexpected response length, expected %d got %d", kingsun->max_rx, urb->actual_length);
	}
	/* This urb has already been filled in kingsun_net_open */
	ret = usb_submit_urb(urb, GFP_ATOMIC);
}

/*
 * Function kingsun_net_open (dev)
 *
 *    Network device is taken up. Usually this is done by "ifconfig irda0 up"
 */
static int kingsun_net_open(struct net_device *netdev)
{
	struct kingsun_cb *kingsun = netdev_priv(netdev);
	int err;
	char hwname[16];

	err = usb_clear_halt(kingsun->usbdev, usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in));
	if (err)
		goto err_out1;
	err = usb_clear_halt(kingsun->usbdev, usb_sndintpipe(kingsun->usbdev, kingsun->ep_out));
	if (err)
		goto err_out1;

	err = -ENOMEM;

	/* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */

	/* Initialize for SIR to copy data directly into skb.  */
	kingsun->rx_buff.in_frame = FALSE;
	kingsun->rx_buff.state = OUTSIDE_FRAME;
	kingsun->rx_buff.truesize = IRDA_SKB_MAX_MTU;
	kingsun->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
	if (!kingsun->rx_buff.skb) 
		goto free_mem;

	skb_reserve(kingsun->rx_buff.skb, 1);
	kingsun->rx_buff.head = kingsun->rx_buff.skb->data;
	do_gettimeofday(&kingsun->rx_time);

	kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
	if (!kingsun->rx_urb) 
		goto free_mem;

	kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
	if (!kingsun->tx_urb) 
		goto free_mem;

	/*
	 * Now that everything should be initialized properly,
	 * Open new IrLAP layer instance to take care of us...
	 */
	sprintf(hwname, "usb#%d", kingsun->usbdev->devnum);
	kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname);
	if (!kingsun->irlap) {
		err("kingsun: irlap_open failed");
		goto free_mem;
	}

	/* Start first reception */
	usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev,
			  usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in),
			  kingsun->in_buf, kingsun->max_rx,
			  kingsun_rcv_irq, kingsun, 1);
	kingsun->rx_urb->status = 0;
	err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL);
	if (err) {
		err("kingsun: first urb-submit failed: %d", err);
		goto close_irlap;
	}

	netif_start_queue(netdev);

	/* Situation at this point:
	   - all work buffers allocated
	   - urbs allocated and ready to fill
	   - max rx packet known (in max_rx)
	   - unwrap state machine initialized, in state outside of any frame
	   - receive request in progress
	   - IrLAP layer started, about to hand over packets to send
	 */

	return 0;

 close_irlap:
	irlap_close(kingsun->irlap);
 free_mem:
	if (kingsun->tx_urb) {
		usb_free_urb(kingsun->tx_urb);
		kingsun->tx_urb = NULL;
	}
	if (kingsun->rx_urb) {
		usb_free_urb(kingsun->rx_urb);
		kingsun->rx_urb = NULL;
	}
	if (kingsun->rx_buff.skb) {
		kfree_skb(kingsun->rx_buff.skb);
		kingsun->rx_buff.skb = NULL;
		kingsun->rx_buff.head = NULL;
	}
 err_out1:
	return err;
}

/*
 * Function kingsun_net_close (kingsun)
 *
 *    Network device is taken down. Usually this is done by
 *    "ifconfig irda0 down"
 */
static int kingsun_net_close(struct net_device *netdev)
{
	struct kingsun_cb *kingsun = netdev_priv(netdev);

	/* Stop transmit processing */
	netif_stop_queue(netdev);

	/* Mop up receive && transmit urb's */
	usb_kill_urb(kingsun->tx_urb);
	usb_kill_urb(kingsun->rx_urb);
	
	usb_free_urb(kingsun->tx_urb);
	usb_free_urb(kingsun->rx_urb);
	
	kingsun->tx_urb = NULL;
	kingsun->rx_urb = NULL;
	
	kfree_skb(kingsun->rx_buff.skb);
	kingsun->rx_buff.skb = NULL;
	kingsun->rx_buff.head = NULL;
	kingsun->rx_buff.in_frame = FALSE;
	kingsun->rx_buff.state = OUTSIDE_FRAME;

	/* Stop and remove instance of IrLAP */
	if (kingsun->irlap)
		irlap_close(kingsun->irlap);

	kingsun->irlap = NULL;

	return 0;
}

/*
 * IOCTLs : Extra out-of-band network commands...
 */
static int kingsun_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
{
	struct kingsun_cb *kingsun = netdev_priv(netdev);
	int ret = 0;

	switch (cmd) {
	case SIOCSBANDWIDTH: /* Set bandwidth */
		if (!capable(CAP_NET_ADMIN))
			return -EPERM;

		/* Check if the device is still there */
		if (netif_device_present(kingsun->netdev))
			ret = -EOPNOTSUPP;	/* No observed commands for speed change */
		break;

	case SIOCSMEDIABUSY: /* Set media busy */
		if (!capable(CAP_NET_ADMIN))
			return -EPERM;

		/* Check if the IrDA stack is still there */
		if (netif_running(kingsun->netdev))
			irda_device_set_media_busy(kingsun->netdev, TRUE);
		break;

	case SIOCGRECEIVING:
		/* Only approximately true */
		ret = -EOPNOTSUPP;
		break;

	default:
		ret = -EOPNOTSUPP;
	}

	return ret;
}

/*
 * Get device stats (for /proc/net/dev and ifconfig)
 */
static struct net_device_stats *kingsun_net_get_stats(struct net_device *netdev)
{
	struct kingsun_cb *kingsun = netdev_priv(netdev);
	return &kingsun->stats;
}

/*
 * This routine is called by the USB subsystem for each new device
 * in the system. We need to check if the device is ours, and in
 * this case start handling it.
 */
static int kingsun_probe(struct usb_interface *intf,
		      const struct usb_device_id *id)
{
	struct usb_host_interface *interface;
	struct usb_endpoint_descriptor *endpoint;

	struct usb_device *dev = interface_to_usbdev(intf);
	struct kingsun_cb *kingsun = NULL;
	struct net_device *net = NULL;
	int ret = -ENOMEM;
	int pipe, maxp_in, maxp_out;
	__u8 ep_in;
	__u8 ep_out;


	ret = usb_reset_configuration(dev);
	if (ret != 0) {
	        err("kingsun: usb reset configuration failed");
	        goto err_out1;
	} 

	/* Check that there really are two interrupt endpoints. Check based on the
	   one in drivers/usb/input/usbmouse.c 
	 */
	interface = intf->cur_altsetting;
	if (interface->desc.bNumEndpoints != 2) {
		err("kingsun: expected 2 endpoints, found %d", interface->desc.bNumEndpoints);
		return -ENODEV;
	}
	endpoint = &interface->endpoint[KINGSUN_EP_IN].desc;
	if (!usb_endpoint_is_int_in(endpoint)) {
		err("kingsun: endpoint 0 is not interrupt IN");
		return -ENODEV;
	}

	ep_in = endpoint->bEndpointAddress;
	pipe = usb_rcvintpipe(dev, ep_in);
	maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
	if (maxp_in > 255 || maxp_in <= 1) {
		err("kingsun: endpoint 0 has max packet size %d not in range [2..255]", maxp_in);
		return -ENODEV;
	}

	endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc;
	if (!usb_endpoint_is_int_out(endpoint)) {
		err("kingsun: endpoint 1 is not interrupt OUT");
		return -ENODEV;
	}

	ep_out = endpoint->bEndpointAddress;
	pipe = usb_sndintpipe(dev, ep_out);
	maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe));

	/* Allocate network device container. */
	net = alloc_irdadev(sizeof(*kingsun));
	if(!net)
		goto err_out1;

	SET_MODULE_OWNER(net);
	SET_NETDEV_DEV(net, &intf->dev);
	kingsun = netdev_priv(net);
	kingsun->irlap = NULL;
	kingsun->tx_urb = NULL;
	kingsun->rx_urb = NULL;
	kingsun->ep_in = ep_in;
	kingsun->ep_out = ep_out;
	kingsun->in_buf = NULL;
	kingsun->out_buf = NULL;
	kingsun->prev_rx = NULL;
	kingsun->max_rx = (__u8)maxp_in;
	kingsun->max_tx = (__u8)maxp_out;
	kingsun->netdev = net;
	kingsun->usbdev = dev;
	kingsun->rx_buff.in_frame = FALSE;
	kingsun->rx_buff.state = OUTSIDE_FRAME;
	kingsun->rx_buff.skb = NULL;
	spin_lock_init(&kingsun->lock);

	/* Allocate verification RX buffer */
	kingsun->prev_rx = (__u8 *)kzalloc(kingsun->max_rx, GFP_KERNEL);
	if (!kingsun->prev_rx) 
		goto free_mem;
	kingsun->prev_rx[0] = 0; /* That is, 0 bytes valid (see RX protocol expl.) */

	/* Allocate input buffer */
	kingsun->in_buf = (__u8 *)kmalloc(kingsun->max_rx, GFP_KERNEL);
	if (!kingsun->in_buf) 
		goto free_mem;
	
	/* Allocate output buffer */
	kingsun->out_buf = (__u8 *)kmalloc(KINGSUN_FIFO_SIZE, GFP_KERNEL);
	if (!kingsun->out_buf) 
		goto free_mem;

	printk(KERN_INFO "KingSun IRDA/USB found at address %d, "
		"Vendor: %x, Product: %x\n",
	       dev->devnum, le16_to_cpu(dev->descriptor.idVendor),
	       le16_to_cpu(dev->descriptor.idProduct));

	/* Initialize QoS for this device */
	irda_init_max_qos_capabilies(&kingsun->qos);

	/* That's the Rx capability. */
	kingsun->qos.baud_rate.bits       &= IR_9600;
	kingsun->qos.min_turn_time.bits   &= qos_mtt_bits;
	irda_qos_bits_to_value(&kingsun->qos);

	/* Override the network functions we need to use */
	net->hard_start_xmit = kingsun_hard_xmit;
	net->open            = kingsun_net_open;
	net->stop            = kingsun_net_close;
	net->get_stats	     = kingsun_net_get_stats;
	net->do_ioctl        = kingsun_net_ioctl;

	ret = register_netdev(net);
	if (ret != 0)
		goto free_mem;

	info("IrDA: Registered KingSun device %s", net->name);

	usb_set_intfdata(intf, kingsun);

	/* Situation at this point:
	   - all work buffers allocated
	   - urbs not allocated, set to NULL
	   - max rx packet known (in max_rx)
	   - unwrap state machine (partially) initialized, but skb == NULL
	 */

	return 0;

free_mem:
	if (kingsun->out_buf) kfree(kingsun->out_buf);
	if (kingsun->in_buf) kfree(kingsun->in_buf);
	if (kingsun->prev_rx) kfree(kingsun->prev_rx);
	free_netdev(net);
err_out1:
	return ret;
}

/*
 * The current device is removed, the USB layer tell us to shut it down...
 */
static void kingsun_disconnect(struct usb_interface *intf)
{
	struct kingsun_cb *kingsun = usb_get_intfdata(intf);

	if (!kingsun)
		return;

	unregister_netdev(kingsun->netdev);
	kfree(kingsun->out_buf);
	kfree(kingsun->in_buf);
	kfree(kingsun->prev_rx);
	free_netdev(kingsun->netdev);

	usb_set_intfdata(intf, NULL);
}

#ifdef CONFIG_PM
/* USB suspend, so power off the transmitter/receiver */
static int kingsun_suspend(struct usb_interface *intf, pm_message_t message)
{
	struct kingsun_cb *kingsun = usb_get_intfdata(intf);

	netif_device_detach(kingsun->netdev);
	return 0;
}

/* Coming out of suspend, so reset hardware */
static int kingsun_resume(struct usb_interface *intf)
{
	struct kingsun_cb *kingsun = usb_get_intfdata(intf);

	netif_device_attach(kingsun->netdev);

	/* receiver restarted when send thread wakes up */
	return 0;
}
#endif

/*
 * USB device callbacks
 */
static struct usb_driver irda_driver = {
	.name		= "kingsun",
	.probe		= kingsun_probe,
	.disconnect	= kingsun_disconnect,
	.id_table	= dongles,
#ifdef CONFIG_PM
	.suspend	= kingsun_suspend,
	.resume		= kingsun_resume,
#endif
};

/*
 * Module insertion
 */
static int __init kingsun_init(void)
{
	return usb_register(&irda_driver);
}
module_init(kingsun_init);

/*
 * Module removal
 */
static void __exit kingsun_cleanup(void)
{
	/* Deregister the driver and remove all pending instances */
	usb_deregister(&irda_driver);
}
module_exit(kingsun_cleanup);

MODULE_AUTHOR("Alex Villacís Lasso <[email protected]>");
MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun");
MODULE_LICENSE("GPL");
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.