📄 irda-usb.c
字号:
/***************************************************************************** * * Filename: irda-usb.c * Version: 0.9b * Description: IrDA-USB Driver * Status: Experimental * Author: Dag Brattli <dag@brattli.net> * * Copyright (C) 2000, Roman Weissgaerber <weissg@vienna.at> * Copyright (C) 2001, Dag Brattli <dag@brattli.net> * Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.com> * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * *****************************************************************************//* * IMPORTANT NOTE * -------------- * * As of kernel 2.4.10, this is the state of compliance and testing of * this driver (irda-usb) with regards to the USB low level drivers... * * This driver has been tested SUCCESSFULLY with the following drivers : * o usb-uhci (For Intel/Via USB controllers) * o usb-ohci (For other USB controllers) * * This driver has NOT been tested with the following drivers : * o usb-ehci (USB 2.0 controllers) * * This driver WON'T WORK with the following drivers : * o uhci (Alternate/JE driver for Intel/Via USB controllers) * Amongst the reasons : * o uhci doesn't implement USB_ZERO_PACKET * o uhci non-compliant use of urb->timeout * The final fix for USB_ZERO_PACKET in uhci is likely to be in 2.4.19 and * 2.5.8. With this fix, the driver will work properly. More on that later. * * Jean II *//*------------------------------------------------------------------*/#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/init.h>#include <linux/skbuff.h>#include <linux/netdevice.h>#include <linux/slab.h>#include <linux/rtnetlink.h>#include <linux/usb.h>#include <net/irda/irda.h>#include <net/irda/irlap.h>#include <net/irda/irda_device.h>#include <net/irda/wrapper.h>#include <net/irda/irda-usb.h>/*------------------------------------------------------------------*/static int qos_mtt_bits = 0;/* Master instance for each hardware found */#define NIRUSB 4 /* Max number of USB-IrDA dongles */static struct irda_usb_cb irda_instance[NIRUSB];/* These are the currently known IrDA USB dongles. Add new dongles here */static struct usb_device_id dongles[] = { /* ACTiSYS Corp, ACT-IR2000U FIR-USB Adapter */ { USB_DEVICE(0x9c4, 0x011), driver_info: IUC_SPEED_BUG | IUC_NO_WINDOW }, /* KC Technology Inc., KC-180 USB IrDA Device */ { USB_DEVICE(0x50f, 0x180), driver_info: IUC_SPEED_BUG | IUC_NO_WINDOW }, /* Extended Systems, Inc., XTNDAccess IrDA USB (ESI-9685) */ { USB_DEVICE(0x8e9, 0x100), driver_info: IUC_SPEED_BUG | IUC_NO_WINDOW }, { match_flags: USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS, bInterfaceClass: USB_CLASS_APP_SPEC, bInterfaceSubClass: USB_CLASS_IRDA, driver_info: IUC_DEFAULT, }, { }, /* The end */};/* * Important note : * Devices based on the SigmaTel chipset (0x66f, 0x4200) are not compliant * with the USB-IrDA specification (and actually very very different), and * there is no way this driver can support those devices, apart from * a complete rewrite... * Jean II */MODULE_DEVICE_TABLE(usb, dongles);/*------------------------------------------------------------------*/static struct irda_class_desc *irda_usb_find_class_desc(struct usb_device *dev, unsigned int ifnum);static void irda_usb_disconnect(struct usb_device *dev, void *ptr);static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self);static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *dev);static int irda_usb_open(struct irda_usb_cb *self);static int irda_usb_close(struct irda_usb_cb *self);static void speed_bulk_callback(struct urb *urb);static void write_bulk_callback(struct urb *urb);static void irda_usb_receive(struct urb *urb);static int irda_usb_net_init(struct net_device *dev);static int irda_usb_net_open(struct net_device *dev);static int irda_usb_net_close(struct net_device *dev);static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);static void irda_usb_net_timeout(struct net_device *dev);static struct net_device_stats *irda_usb_net_get_stats(struct net_device *dev);/************************ TRANSMIT ROUTINES ************************//* * Receive packets from the IrDA stack and send them on the USB pipe. * Handle speed change, timeout and lot's of uglyness... *//*------------------------------------------------------------------*//* * Function irda_usb_build_header(self, skb, header) * * Builds USB-IrDA outbound header * * When we send an IrDA frame over an USB pipe, we add to it a 1 byte * header. This function create this header with the proper values. * * Important note : the USB-IrDA spec 1.0 say very clearly in chapter 5.4.2.2 * that the setting of the link speed and xbof number in this outbound header * should be applied *AFTER* the frame has been sent. * Unfortunately, some devices are not compliant with that... It seems that * reading the spec is far too difficult... * Jean II */static void irda_usb_build_header(struct irda_usb_cb *self, __u8 *header, int force){ /* Set the negotiated link speed */ if (self->new_speed != -1) { /* Hum... Ugly hack :-( * Some device are not compliant with the spec and change * parameters *before* sending the frame. - Jean II */ if ((self->capability & IUC_SPEED_BUG) && (!force) && (self->speed != -1)) { /* No speed and xbofs change here * (we'll do it later in the write callback) */ IRDA_DEBUG(2, __FUNCTION__ "(), not changing speed yet\n"); *header = 0; return; } IRDA_DEBUG(2, __FUNCTION__ "(), changing speed to %d\n", self->new_speed); self->speed = self->new_speed; self->new_speed = -1; switch (self->speed) { case 2400: *header = SPEED_2400; break; default: case 9600: *header = SPEED_9600; break; case 19200: *header = SPEED_19200; break; case 38400: *header = SPEED_38400; break; case 57600: *header = SPEED_57600; break; case 115200: *header = SPEED_115200; break; case 576000: *header = SPEED_576000; break; case 1152000: *header = SPEED_1152000; break; case 4000000: *header = SPEED_4000000; self->new_xbofs = 0; break; } } else /* No change */ *header = 0; /* Set the negotiated additional XBOFS */ if (self->new_xbofs != -1) { IRDA_DEBUG(2, __FUNCTION__ "(), changing xbofs to %d\n", self->new_xbofs); self->xbofs = self->new_xbofs; self->new_xbofs = -1; switch (self->xbofs) { case 48: *header |= 0x10; break; case 28: case 24: /* USB spec 1.0 says 24 */ *header |= 0x20; break; default: case 12: *header |= 0x30; break; case 5: /* Bug in IrLAP spec? (should be 6) */ case 6: *header |= 0x40; break; case 3: *header |= 0x50; break; case 2: *header |= 0x60; break; case 1: *header |= 0x70; break; case 0: *header |= 0x80; break; } }}/*------------------------------------------------------------------*//* * Send a command to change the speed of the dongle * Need to be called with spinlock on. */static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self){ __u8 *frame; struct urb *urb; int ret; IRDA_DEBUG(2, __FUNCTION__ "(), speed=%d, xbofs=%d\n", self->new_speed, self->new_xbofs); /* Grab the speed URB */ urb = self->speed_urb; if (urb->status != 0) { WARNING(__FUNCTION__ "(), URB still in use!\n"); return; } /* Allocate the fake frame */ frame = self->speed_buff; /* Set the new speed and xbofs in this fake frame */ irda_usb_build_header(self, frame, 1); /* Submit the 0 length IrDA frame to trigger new speed settings */ FILL_BULK_URB(urb, self->usbdev, usb_sndbulkpipe(self->usbdev, self->bulk_out_ep), frame, IRDA_USB_SPEED_MTU, speed_bulk_callback, self); urb->transfer_buffer_length = USB_IRDA_HEADER; urb->transfer_flags = USB_QUEUE_BULK | USB_ASYNC_UNLINK; urb->timeout = MSECS_TO_JIFFIES(100); /* Irq disabled -> GFP_ATOMIC */ if ((ret = usb_submit_urb(urb, GFP_ATOMIC))) { WARNING(__FUNCTION__ "(), failed Speed URB\n"); }}/*------------------------------------------------------------------*//* * Note : this function will be called with both speed_urb and empty_urb... */static void speed_bulk_callback(struct urb *urb){ struct irda_usb_cb *self = urb->context; IRDA_DEBUG(2, __FUNCTION__ "()\n"); /* We should always have a context */ if (self == NULL) { WARNING(__FUNCTION__ "(), Bug : self == NULL\n"); return; } /* Check for timeout and other USB nasties */ if (urb->status != 0) { /* I get a lot of -ECONNABORTED = -103 here - Jean II */ IRDA_DEBUG(0, __FUNCTION__ "(), URB complete status %d, transfer_flags 0x%04X\n", urb->status, urb->transfer_flags); /* Don't do anything here, that might confuse the USB layer. * Instead, we will wait for irda_usb_net_timeout(), the * network layer watchdog, to fix the situation. * Jean II */ /* A reset of the dongle might be welcomed here - Jean II */ return; } /* urb is now available */ urb->status = 0; /* If it was the speed URB, allow the stack to send more packets */ if(urb == self->speed_urb) { netif_wake_queue(self->netdev); }}/*------------------------------------------------------------------*//* * Send an IrDA frame to the USB dongle (for transmission) */static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *netdev){ struct irda_usb_cb *self = netdev->priv; struct urb *urb = self->tx_urb; unsigned long flags; s32 speed; s16 xbofs; int res, mtt; netif_stop_queue(netdev); /* Protect us from USB callbacks, net watchdog and else. */ spin_lock_irqsave(&self->lock, flags); /* Check if the device is still there. * We need to check self->present under the spinlock because * of irda_usb_disconnect() is synchronous - Jean II */ if ((!self) || (!self->present)) { IRDA_DEBUG(0, __FUNCTION__ "(), Device is gone...\n"); spin_unlock_irqrestore(&self->lock, flags); return 1; /* Failed */ } /* Check if we need to change the number of xbofs */ xbofs = irda_get_next_xbofs(skb); if ((xbofs != self->xbofs) && (xbofs != -1)) { self->new_xbofs = xbofs; } /* Check if we need to change the speed */ speed = irda_get_next_speed(skb); if ((speed != self->speed) && (speed != -1)) { /* Set the desired speed */ self->new_speed = speed; /* Check for empty frame */ if (!skb->len) { /* IrLAP send us an empty frame to make us change the * speed. Changing speed with the USB adapter is in * fact sending an empty frame to the adapter, so we * could just let the present function do its job. * However, we would wait for min turn time, * do an extra memcpy and increment packet counters... * Jean II */ irda_usb_change_speed_xbofs(self); netdev->trans_start = jiffies; /* Will netif_wake_queue() in callback */ goto drop; } } if (urb->status != 0) { WARNING(__FUNCTION__ "(), URB still in use!\n"); goto drop; } /* Make sure there is room for IrDA-USB header. The actual * allocation will be done lower in skb_push(). * Also, we don't use directly skb_cow(), because it require * headroom >= 16, which force unnecessary copies - Jean II */ if (skb_headroom(skb) < USB_IRDA_HEADER) { IRDA_DEBUG(0, __FUNCTION__ "(), Insuficient skb headroom.\n"); if (skb_cow(skb, USB_IRDA_HEADER)) { WARNING(__FUNCTION__ "(), failed skb_cow() !!!\n"); goto drop; } } /* Change setting for next frame */ irda_usb_build_header(self, skb_push(skb, USB_IRDA_HEADER), 0); /* FIXME: Make macro out of this one */ ((struct irda_skb_cb *)skb->cb)->context = self; FILL_BULK_URB(urb, self->usbdev, usb_sndbulkpipe(self->usbdev, self->bulk_out_ep), skb->data, IRDA_USB_MAX_MTU, write_bulk_callback, skb); urb->transfer_buffer_length = skb->len; /* Note : unlink *must* be Asynchronous because of the code in * irda_usb_net_timeout() -> call in irq - Jean II */ urb->transfer_flags = USB_QUEUE_BULK | USB_ASYNC_UNLINK; /* This flag (USB_ZERO_PACKET) indicates that what we send is not * a continuous stream of data but separate packets. * In this case, the USB layer will insert an empty USB frame (TD) * after each of our packets that is exact multiple of the frame size. * This is how the dongle will detect the end of packet - Jean II */ urb->transfer_flags |= USB_ZERO_PACKET; /* Timeout need to be shorter than NET watchdog timer */ urb->timeout = MSECS_TO_JIFFIES(200);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -