⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 irda-usb.c

📁 《linux驱动程序设计从入门到精通》一书中所有的程序代码含驱动和相应的应用程序
💻 C
📖 第 1 页 / 共 4 页
字号:
/***************************************************************************** * * 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 + -