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

📄 irport.c

📁 底层驱动开发
💻 C
📖 第 1 页 / 共 2 页
字号:
/********************************************************************* *  * Filename:	  irport.c * Version:	  1.0 * Description:   Half duplex serial port SIR driver for IrDA.  * Status:	  Experimental. * Author:	  Dag Brattli <dagb@cs.uit.no> * Created at:	  Sun Aug  3 13:49:59 1997 * Modified at:   Fri Jan 28 20:22:38 2000 * Modified by:   Dag Brattli <dagb@cs.uit.no> * Sources:	  serial.c by Linus Torvalds  *  *     Copyright (c) 1997, 1998, 1999-2000 Dag Brattli, All Rights Reserved. *     Copyright (c) 2000-2003 Jean Tourrilhes, All Rights Reserved. *      *     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., 59 Temple Place, Suite 330, Boston,  *     MA 02111-1307 USA * *     This driver is ment to be a small half duplex serial driver to be *     used for IR-chipsets that has a UART (16550) compatibility mode.  *     Eventually it will replace irtty, because of irtty has some  *     problems that is hard to get around when we don't have control *     over the serial driver. This driver may also be used by FIR  *     drivers to handle SIR mode for them. * ********************************************************************/#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/skbuff.h>#include <linux/serial_reg.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/spinlock.h>#include <linux/delay.h>#include <linux/rtnetlink.h>#include <linux/bitops.h>#include <asm/system.h>#include <asm/io.h>#include <net/irda/irda.h>#include <net/irda/wrapper.h>#include "irport.h"#define IO_EXTENT 8/*  * Currently you'll need to set these values using insmod like this: * insmod irport io=0x3e8 irq=11 */static unsigned int io[]  = { ~0, ~0, ~0, ~0 };static unsigned int irq[] = { 0, 0, 0, 0 };static unsigned int qos_mtt_bits = 0x03;static struct irport_cb *dev_self[] = { NULL, NULL, NULL, NULL};static char *driver_name = "irport";static inline void irport_write_wakeup(struct irport_cb *self);static inline int  irport_write(int iobase, int fifo_size, __u8 *buf, int len);static inline void irport_receive(struct irport_cb *self);static int  irport_net_ioctl(struct net_device *dev, struct ifreq *rq, 			     int cmd);static inline int  irport_is_receiving(struct irport_cb *self);static int  irport_set_dtr_rts(struct net_device *dev, int dtr, int rts);static int  irport_raw_write(struct net_device *dev, __u8 *buf, int len);static struct net_device_stats *irport_net_get_stats(struct net_device *dev);static int irport_change_speed_complete(struct irda_task *task);static void irport_timeout(struct net_device *dev);static irqreturn_t irport_interrupt(int irq, void *dev_id,				    struct pt_regs *regs);static int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev);static void irport_change_speed(void *priv, __u32 speed);static int irport_net_open(struct net_device *dev);static int irport_net_close(struct net_device *dev);static struct irport_cb *irport_open(int i, unsigned int iobase, unsigned int irq){	struct net_device *dev;	struct irport_cb *self;	IRDA_DEBUG(1, "%s()\n", __FUNCTION__);	/* Lock the port that we need */	if (!request_region(iobase, IO_EXTENT, driver_name)) {		IRDA_DEBUG(0, "%s(), can't get iobase of 0x%03x\n",			   __FUNCTION__, iobase);		goto err_out1;	}	/*	 *  Allocate new instance of the driver	 */	dev = alloc_irdadev(sizeof(struct irport_cb));	if (!dev) {		IRDA_ERROR("%s(), can't allocate memory for "			   "irda device!\n", __FUNCTION__);		goto err_out2;	}	self = dev->priv;	spin_lock_init(&self->lock);	/* Need to store self somewhere */	dev_self[i] = self;	self->priv = self;	self->index = i;	/* Initialize IO */	self->io.sir_base  = iobase;        self->io.sir_ext   = IO_EXTENT;        self->io.irq       = irq;        self->io.fifo_size = 16;		/* 16550A and compatible */	/* Initialize QoS for this device */	irda_init_max_qos_capabilies(&self->qos);		self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|		IR_115200;	self->qos.min_turn_time.bits = qos_mtt_bits;	irda_qos_bits_to_value(&self->qos);		/* Bootstrap ZeroCopy Rx */	self->rx_buff.truesize = IRDA_SKB_MAX_MTU;	self->rx_buff.skb = __dev_alloc_skb(self->rx_buff.truesize,					    GFP_KERNEL);	if (self->rx_buff.skb == NULL) {		IRDA_ERROR("%s(), can't allocate memory for "			   "receive buffer!\n", __FUNCTION__);		goto err_out3;	}	skb_reserve(self->rx_buff.skb, 1);	self->rx_buff.head = self->rx_buff.skb->data;	/* No need to memset the buffer, unless you are really pedantic */	/* Finish setup the Rx buffer descriptor */	self->rx_buff.in_frame = FALSE;	self->rx_buff.state = OUTSIDE_FRAME;	self->rx_buff.data = self->rx_buff.head;	/* Specify how much memory we want */	self->tx_buff.truesize = 4000;		/* Allocate memory if needed */	if (self->tx_buff.truesize > 0) {		self->tx_buff.head = (__u8 *) kmalloc(self->tx_buff.truesize, 						      GFP_KERNEL);		if (self->tx_buff.head == NULL) {			IRDA_ERROR("%s(), can't allocate memory for "				   "transmit buffer!\n", __FUNCTION__);			goto err_out4;		}		memset(self->tx_buff.head, 0, self->tx_buff.truesize);	}		self->tx_buff.data = self->tx_buff.head;	self->netdev = dev;	/* Keep track of module usage */	SET_MODULE_OWNER(dev);	/* May be overridden by piggyback drivers */	self->interrupt    = irport_interrupt;	self->change_speed = irport_change_speed;	/* Override the network functions we need to use */	dev->hard_start_xmit = irport_hard_xmit;	dev->tx_timeout	     = irport_timeout;	dev->watchdog_timeo  = HZ;  /* Allow time enough for speed change */	dev->open            = irport_net_open;	dev->stop            = irport_net_close;	dev->get_stats	     = irport_net_get_stats;	dev->do_ioctl        = irport_net_ioctl;	/* Make ifconfig display some details */	dev->base_addr = iobase;	dev->irq = irq;	if (register_netdev(dev)) {		IRDA_ERROR("%s(), register_netdev() failed!\n", __FUNCTION__);		goto err_out5;	}	IRDA_MESSAGE("IrDA: Registered device %s (irport io=0x%X irq=%d)\n",		dev->name, iobase, irq);	return self; err_out5:	kfree(self->tx_buff.head); err_out4:	kfree_skb(self->rx_buff.skb); err_out3:	free_netdev(dev);	dev_self[i] = NULL; err_out2:	release_region(iobase, IO_EXTENT); err_out1:	return NULL;}static int irport_close(struct irport_cb *self){	IRDA_ASSERT(self != NULL, return -1;);	/* We are not using any dongle anymore! */	if (self->dongle)		irda_device_dongle_cleanup(self->dongle);	self->dongle = NULL;		/* Remove netdevice */	unregister_netdev(self->netdev);	/* Release the IO-port that this driver is using */	IRDA_DEBUG(0 , "%s(), Releasing Region %03x\n", 		   __FUNCTION__, self->io.sir_base);	release_region(self->io.sir_base, self->io.sir_ext);	if (self->tx_buff.head)		kfree(self->tx_buff.head);		if (self->rx_buff.skb)		kfree_skb(self->rx_buff.skb);	self->rx_buff.skb = NULL;		/* Remove ourselves */	dev_self[self->index] = NULL;	free_netdev(self->netdev);		return 0;}static void irport_stop(struct irport_cb *self){	int iobase;	iobase = self->io.sir_base;	/* We can't lock, we may be called from a FIR driver - Jean II */	/* We are not transmitting any more */	self->transmitting = 0;	/* Reset UART */	outb(0, iobase+UART_MCR);		/* Turn off interrupts */	outb(0, iobase+UART_IER);}static void irport_start(struct irport_cb *self){	int iobase;	iobase = self->io.sir_base;	irport_stop(self);		/* We can't lock, we may be called from a FIR driver - Jean II */	/* Initialize UART */	outb(UART_LCR_WLEN8, iobase+UART_LCR);  /* Reset DLAB */	outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase+UART_MCR);		/* Turn on interrups */	outb(UART_IER_RLSI | UART_IER_RDI |UART_IER_THRI, iobase+UART_IER);}/* * Function irport_probe (void) * *    Start IO port  * */int irport_probe(int iobase){	IRDA_DEBUG(4, "%s(), iobase=%#x\n", __FUNCTION__, iobase);	return 0;}/* * Function irport_get_fcr (speed) * *    Compute value of fcr * */static inline unsigned int irport_get_fcr(__u32 speed){	unsigned int fcr;    /* FIFO control reg */	/* Enable fifos */	fcr = UART_FCR_ENABLE_FIFO;	/* 	 * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and	 * almost 1,7 ms at 19200 bps. At speeds above that we can just forget	 * about this timeout since it will always be fast enough. 	 */	if (speed < 38400)		fcr |= UART_FCR_TRIGGER_1;	else 		//fcr |= UART_FCR_TRIGGER_14;		fcr |= UART_FCR_TRIGGER_8;	return(fcr);} /* * Function irport_change_speed (self, speed) * *    Set speed of IrDA port to specified baudrate * * This function should be called with irq off and spin-lock. */static void irport_change_speed(void *priv, __u32 speed){	struct irport_cb *self = (struct irport_cb *) priv;	int iobase; 	unsigned int fcr;    /* FIFO control reg */	unsigned int lcr;    /* Line control reg */	int divisor;	IRDA_ASSERT(self != NULL, return;);	IRDA_ASSERT(speed != 0, return;);	IRDA_DEBUG(1, "%s(), Setting speed to: %d - iobase=%#x\n",		    __FUNCTION__, speed, self->io.sir_base);	/* We can't lock, we may be called from a FIR driver - Jean II */	iobase = self->io.sir_base;		/* Update accounting for new speed */	self->io.speed = speed;	/* Turn off interrupts */	outb(0, iobase+UART_IER); 	divisor = SPEED_MAX/speed;		/* Get proper fifo configuration */	fcr = irport_get_fcr(speed);	/* IrDA ports use 8N1 */	lcr = UART_LCR_WLEN8;		outb(UART_LCR_DLAB | lcr, iobase+UART_LCR); /* Set DLAB */	outb(divisor & 0xff,      iobase+UART_DLL); /* Set speed */	outb(divisor >> 8,	  iobase+UART_DLM);	outb(lcr,		  iobase+UART_LCR); /* Set 8N1	*/	outb(fcr,		  iobase+UART_FCR); /* Enable FIFO's */	/* Turn on interrups */	/* This will generate a fatal interrupt storm.	 * People calling us will do that properly - Jean II */	//outb(/*UART_IER_RLSI|*/UART_IER_RDI/*|UART_IER_THRI*/, iobase+UART_IER);}/* * Function __irport_change_speed (instance, state, param) * *    State machine for changing speed of the device. We do it this way since *    we cannot use schedule_timeout() when we are in interrupt context * */int __irport_change_speed(struct irda_task *task){	struct irport_cb *self;	__u32 speed = (__u32) task->param;	unsigned long flags = 0;	int wasunlocked = 0;	int ret = 0;	IRDA_DEBUG(2, "%s(), <%ld>\n", __FUNCTION__, jiffies); 	self = (struct irport_cb *) task->instance;	IRDA_ASSERT(self != NULL, return -1;);	/* Locking notes : this function may be called from irq context with	 * spinlock, via irport_write_wakeup(), or from non-interrupt without	 * spinlock (from the task timer). Yuck !	 * This is ugly, and unsafe is the spinlock is not already aquired.	 * This will be fixed when irda-task get rewritten.	 * Jean II */	if (!spin_is_locked(&self->lock)) {		spin_lock_irqsave(&self->lock, flags);		wasunlocked = 1;	}	switch (task->state) {	case IRDA_TASK_INIT:	case IRDA_TASK_WAIT:		/* Are we ready to change speed yet? */		if (self->tx_buff.len > 0) {			task->state = IRDA_TASK_WAIT;			/* Try again later */			ret = msecs_to_jiffies(20);			break;		}		if (self->dongle)			irda_task_next_state(task, IRDA_TASK_CHILD_INIT);		else			irda_task_next_state(task, IRDA_TASK_CHILD_DONE);		break;	case IRDA_TASK_CHILD_INIT:		/* Go to default speed */		self->change_speed(self->priv, 9600);		/* Change speed of dongle */		if (irda_task_execute(self->dongle,				      self->dongle->issue->change_speed, 				      NULL, task, (void *) speed))		{			/* Dongle need more time to change its speed */			irda_task_next_state(task, IRDA_TASK_CHILD_WAIT);			/* Give dongle 1 sec to finish */			ret = msecs_to_jiffies(1000);		} else			/* Child finished immediately */			irda_task_next_state(task, IRDA_TASK_CHILD_DONE);		break;	case IRDA_TASK_CHILD_WAIT:		IRDA_WARNING("%s(), changing speed of dongle timed out!\n", __FUNCTION__);		ret = -1;				break;	case IRDA_TASK_CHILD_DONE:		/* Finally we are ready to change the speed */		self->change_speed(self->priv, speed);				irda_task_next_state(task, IRDA_TASK_DONE);		break;	default:		IRDA_ERROR("%s(), unknown state %d\n",			   __FUNCTION__, task->state);		irda_task_next_state(task, IRDA_TASK_DONE);		ret = -1;		break;	}	/* Put stuff in the state we found them - Jean II */	if(wasunlocked) {		spin_unlock_irqrestore(&self->lock, flags);	}	return ret;}/* * Function irport_change_speed_complete (task) * *    Called when the change speed operation completes * */static int irport_change_speed_complete(struct irda_task *task){	struct irport_cb *self;	IRDA_DEBUG(1, "%s()\n", __FUNCTION__);	self = (struct irport_cb *) task->instance;	IRDA_ASSERT(self != NULL, return -1;);	IRDA_ASSERT(self->netdev != NULL, return -1;);	/* Finished changing speed, so we are not busy any longer */	/* Signal network layer so it can try to send the frame */	netif_wake_queue(self->netdev);		return 0;}/* * Function irport_timeout (struct net_device *dev) * *    The networking layer thinks we timed out. * */static void irport_timeout(struct net_device *dev){	struct irport_cb *self;	int iobase;	int iir, lsr;	unsigned long flags;	self = (struct irport_cb *) dev->priv;	IRDA_ASSERT(self != NULL, return;);	iobase = self->io.sir_base;		IRDA_WARNING("%s: transmit timed out, jiffies = %ld, trans_start = %ld\n",		dev->name, jiffies, dev->trans_start);	spin_lock_irqsave(&self->lock, flags);	/* Debug what's happening... */	/* Get interrupt status */	lsr = inb(iobase+UART_LSR);	/* Read interrupt register */	iir = inb(iobase+UART_IIR);	IRDA_DEBUG(0, "%s(), iir=%02x, lsr=%02x, iobase=%#x\n", 		   __FUNCTION__, iir, lsr, iobase);	IRDA_DEBUG(0, "%s(), transmitting=%d, remain=%d, done=%d\n", 		   __FUNCTION__, self->transmitting, self->tx_buff.len,		   self->tx_buff.data - self->tx_buff.head);	/* Now, restart the port */	irport_start(self);	self->change_speed(self->priv, self->io.speed);	/* This will re-enable irqs */	outb(/*UART_IER_RLSI|*/UART_IER_RDI/*|UART_IER_THRI*/, iobase+UART_IER);	dev->trans_start = jiffies;	spin_unlock_irqrestore(&self->lock, flags);	netif_wake_queue(dev);} /* * Function irport_wait_hw_transmitter_finish () * *    Wait for the real end of HW transmission * * The UART is a strict FIFO, and we get called only when we have finished * pushing data to the FIFO, so the maximum amount of time we must wait * is only for the FIFO to drain out. * * We use a simple calibrated loop. We may need to adjust the loop * delay (udelay) to balance I/O traffic and latency. And we also need to * adjust the maximum timeout. * It would probably be better to wait for the proper interrupt, * but it doesn't seem to be available. * * We can't use jiffies or kernel timers because : * 1) We are called from the interrupt handler, which disable softirqs, * so jiffies won't be increased * 2) Jiffies granularity is usually very coarse (10ms), and we don't * want to wait that long to detect stuck hardware. * Jean II */static void irport_wait_hw_transmitter_finish(struct irport_cb *self){	int iobase;	int count = 1000;	/* 1 ms */		iobase = self->io.sir_base;	/* Calibrated busy loop */	while((count-- > 0) && !(inb(iobase+UART_LSR) & UART_LSR_TEMT))		udelay(1);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -