📄 irda-usb.c
字号:
/***************************************************************************** * * Filename: irda-usb.c * Version: 0.10 * 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> * Copyright (C) 2004, SigmaTel, Inc. <irquality@sigmatel.com> * Copyright (C) 2005, Milan Beno <beno@pobox.sk> * Copyright (C) 2006, Nick Fedchik <nick@fedchik.org.ua> * * 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.5.20, 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-hcd (For Intel/Via USB controllers) * o uhci-hcd (Alternate/JE driver for Intel/Via USB controllers) * o ohci-hcd (For other USB controllers) * * This driver has NOT been tested with the following drivers : * o ehci-hcd (USB 2.0 controllers) * * Note that all HCD drivers do URB_ZERO_PACKET and timeout properly, * so we don't have to worry about that anymore. * One common problem is the failure to set the address on the dongle, * but this happens before the driver gets loaded... * * Jean II *//*------------------------------------------------------------------*/#include <linux/module.h>#include <linux/moduleparam.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 <linux/firmware.h>#include "irda-usb.h"/*------------------------------------------------------------------*/static int qos_mtt_bits = 0;/* 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 }, /* Look like ACTiSYS, Report : IBM Corp., IBM UltraPort IrDA */ { USB_DEVICE(0x4428, 0x012), .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 }, /* SigmaTel STIR4210/4220/4116 USB IrDA (VFIR) Bridge */ { USB_DEVICE(0x66f, 0x4210), .driver_info = IUC_STIR421X | IUC_SPEED_BUG }, { USB_DEVICE(0x66f, 0x4220), .driver_info = IUC_STIR421X | IUC_SPEED_BUG }, { USB_DEVICE(0x66f, 0x4116), .driver_info = IUC_STIR421X | IUC_SPEED_BUG }, { .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 designed * using the "USB-IrDA specification" (yes, there exist such a thing), and * therefore not supported by this driver (don't add them above). * There is a Linux driver, stir4200, that support those USB devices. * Jean II */MODULE_DEVICE_TABLE(usb, dongles);/*------------------------------------------------------------------*/static void irda_usb_init_qos(struct irda_usb_cb *self) ;static struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf);static void irda_usb_disconnect(struct usb_interface *intf);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 void 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 void irda_usb_rx_defer_expired(unsigned long data);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 ugliness... *//*------------------------------------------------------------------*//* * 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){ /* Here we check if we have an STIR421x chip, * and if either speed or xbofs (or both) needs * to be changed. */ if (self->capability & IUC_STIR421X && ((self->new_speed != -1) || (self->new_xbofs != -1))) { /* With STIR421x, speed and xBOFs must be set at the same * time, even if only one of them changes. */ if (self->new_speed == -1) self->new_speed = self->speed ; if (self->new_xbofs == -1) self->new_xbofs = self->xbofs ; } /* Set the 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, "%s(), not changing speed yet\n", __FUNCTION__); *header = 0; return; } IRDA_DEBUG(2, "%s(), changing speed to %d\n", __FUNCTION__, self->new_speed); self->speed = self->new_speed; /* We will do ` self->new_speed = -1; ' in the completion * handler just in case the current URB fail - Jean II */ 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; case 16000000: *header = SPEED_16000000; self->new_xbofs = 0; break; } } else /* No change */ *header = 0; /* Set the negotiated additional XBOFS */ if (self->new_xbofs != -1) { IRDA_DEBUG(2, "%s(), changing xbofs to %d\n", __FUNCTION__, self->new_xbofs); self->xbofs = self->new_xbofs; /* We will do ` self->new_xbofs = -1; ' in the completion * handler just in case the current URB fail - Jean II */ 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; } }}/** calculate turnaround time for SigmaTel header*/static __u8 get_turnaround_time(struct sk_buff *skb){ int turnaround_time = irda_get_mtt(skb); if ( turnaround_time == 0 ) return 0; else if ( turnaround_time <= 10 ) return 1; else if ( turnaround_time <= 50 ) return 2; else if ( turnaround_time <= 100 ) return 3; else if ( turnaround_time <= 500 ) return 4; else if ( turnaround_time <= 1000 ) return 5; else if ( turnaround_time <= 5000 ) return 6; else return 7;}/*------------------------------------------------------------------*//* * 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, "%s(), speed=%d, xbofs=%d\n", __FUNCTION__, self->new_speed, self->new_xbofs); /* Grab the speed URB */ urb = self->speed_urb; if (urb->status != 0) { IRDA_WARNING("%s(), URB still in use!\n", __FUNCTION__); 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); if (self->capability & IUC_STIR421X) { if (frame[0] == 0) return ; // do nothing if no change frame[1] = 0; // other parameters don't change here frame[2] = 0; } /* Submit the 0 length IrDA frame to trigger new speed settings */ usb_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 = self->header_length; urb->transfer_flags = 0; /* Irq disabled -> GFP_ATOMIC */ if ((ret = usb_submit_urb(urb, GFP_ATOMIC))) { IRDA_WARNING("%s(), failed Speed URB\n", __FUNCTION__); }}/*------------------------------------------------------------------*//* * Speed URB callback * Now, we can only get called for the speed URB. */static void speed_bulk_callback(struct urb *urb){ struct irda_usb_cb *self = urb->context; IRDA_DEBUG(2, "%s()\n", __FUNCTION__); /* We should always have a context */ IRDA_ASSERT(self != NULL, return;); /* We should always be called for the speed URB */ IRDA_ASSERT(urb == self->speed_urb, 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, "%s(), URB complete status %d, transfer_flags 0x%04X\n", __FUNCTION__, 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; -> tested above /* New speed and xbof is now commited in hardware */ self->new_speed = -1; self->new_xbofs = -1; /* Allow the stack to send more packets */ 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; int err = 1; /* Failed */ IRDA_DEBUG(4, "%s() on %s\n", __FUNCTION__, netdev->name); 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->present) { IRDA_DEBUG(0, "%s(), Device is gone...\n", __FUNCTION__); goto drop; } /* 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 */ err = 0; /* No error */ goto drop; } } if (urb->status != 0) { IRDA_WARNING("%s(), URB still in use!\n", __FUNCTION__); goto drop; } memcpy(self->tx_buff + self->header_length, skb->data, skb->len); /* Change setting for next frame */ if (self->capability & IUC_STIR421X) { __u8 turnaround_time; __u8* frame = self->tx_buff; turnaround_time = get_turnaround_time( skb ); irda_usb_build_header(self, frame, 0); frame[2] = turnaround_time; if ((skb->len != 0) && ((skb->len % 128) == 0) && ((skb->len % 512) != 0)) { /* add extra byte for special SigmaTel feature */ frame[1] = 1; skb_put(skb, 1); } else { frame[1] = 0; } } else { irda_usb_build_header(self, self->tx_buff, 0); } /* FIXME: Make macro out of this one */ ((struct irda_skb_cb *)skb->cb)->context = self; usb_fill_bulk_urb(urb, self->usbdev, usb_sndbulkpipe(self->usbdev, self->bulk_out_ep), self->tx_buff, skb->len + self->header_length, write_bulk_callback, skb); /* This flag (URB_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 = URB_ZERO_PACKET; /* Generate min turn time. FIXME: can we do better than this? */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -