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

📄 irport.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 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. *      *     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/malloc.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/rtnetlink.h>#include <asm/system.h>#include <asm/bitops.h>#include <asm/io.h>#include <net/irda/irda.h>#include <net/irda/irmod.h>#include <net/irda/wrapper.h>#include <net/irda/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 void irport_write_wakeup(struct irport_cb *self);static int  irport_write(int iobase, int fifo_size, __u8 *buf, int len);static void irport_receive(struct irport_cb *self);static int  irport_net_init(struct net_device *dev);static int  irport_net_ioctl(struct net_device *dev, struct ifreq *rq, 			     int cmd);static 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);EXPORT_SYMBOL(irport_open);EXPORT_SYMBOL(irport_close);EXPORT_SYMBOL(irport_start);EXPORT_SYMBOL(irport_stop);EXPORT_SYMBOL(irport_interrupt);EXPORT_SYMBOL(irport_hard_xmit);EXPORT_SYMBOL(irport_timeout);EXPORT_SYMBOL(irport_change_speed);EXPORT_SYMBOL(irport_net_open);EXPORT_SYMBOL(irport_net_close);int __init irport_init(void){ 	int i; 	for (i=0; (io[i] < 2000) && (i < 4); i++) { 		int ioaddr = io[i]; 		if (check_region(ioaddr, IO_EXTENT)) 			continue; 		if (irport_open(i, io[i], irq[i]) != NULL) 			return 0; 	}	/* 	 * Maybe something failed, but we can still be usable for FIR drivers 	 */ 	return 0;}/* * Function irport_cleanup () * *    Close all configured ports * */#ifdef MODULEstatic void irport_cleanup(void){ 	int i;        IRDA_DEBUG( 4, __FUNCTION__ "()\n");	for (i=0; i < 4; i++) { 		if (dev_self[i]) 			irport_close(dev_self[i]); 	}}#endif /* MODULE */struct irport_cb *irport_open(int i, unsigned int iobase, unsigned int irq){	struct net_device *dev;	struct irport_cb *self;	int ret;	int err;	IRDA_DEBUG(0, __FUNCTION__ "()\n");	/*	 *  Allocate new instance of the driver	 */	self = kmalloc(sizeof(struct irport_cb), GFP_KERNEL);	if (!self) {		ERROR(__FUNCTION__ "(), can't allocate memory for "		      "control block!\n");		return NULL;	}	memset(self, 0, sizeof(struct irport_cb));	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;	/* Lock the port that we need */	ret = check_region(self->io.sir_base, self->io.sir_ext);	if (ret < 0) { 		IRDA_DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",			   self->io.sir_base);		return NULL;	}	request_region(self->io.sir_base, self->io.sir_ext, driver_name);	/* 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);		self->flags = IFF_SIR|IFF_PIO;	/* Specify how much memory we want */	self->rx_buff.truesize = 4000; 	self->tx_buff.truesize = 4000;		/* Allocate memory if needed */	if (self->rx_buff.truesize > 0) {		self->rx_buff.head = (__u8 *) kmalloc(self->rx_buff.truesize,						      GFP_KERNEL);		if (self->rx_buff.head == NULL)			return NULL;		memset(self->rx_buff.head, 0, self->rx_buff.truesize);	}	if (self->tx_buff.truesize > 0) {		self->tx_buff.head = (__u8 *) kmalloc(self->tx_buff.truesize, 						      GFP_KERNEL);		if (self->tx_buff.head == NULL) {			kfree(self->rx_buff.head);			return NULL;		}		memset(self->tx_buff.head, 0, self->tx_buff.truesize);	}		self->rx_buff.in_frame = FALSE;	self->rx_buff.state = OUTSIDE_FRAME;	self->tx_buff.data = self->tx_buff.head;	self->rx_buff.data = self->rx_buff.head;	self->mode = IRDA_IRLAP;	if (!(dev = dev_alloc("irda%d", &err))) {		ERROR(__FUNCTION__ "(), dev_alloc() failed!\n");		return NULL;	}	self->netdev = dev;	/* May be overridden by piggyback drivers */ 	dev->priv = (void *) self;	self->interrupt    = irport_interrupt;	self->change_speed = irport_change_speed;	/* Override the network functions we need to use */	dev->init            = irport_net_init;	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;	rtnl_lock();	err = register_netdevice(dev);	rtnl_unlock();	if (err) {		ERROR(__FUNCTION__ "(), register_netdev() failed!\n");		return NULL;	}	MESSAGE("IrDA: Registered device %s\n", dev->name);	return self;}int irport_close(struct irport_cb *self){	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 */	if (self->netdev) {		rtnl_lock();		unregister_netdevice(self->netdev);		rtnl_unlock();	}	/* Release the IO-port that this driver is using */	IRDA_DEBUG(0 , __FUNCTION__ "(), Releasing Region %03x\n", 		   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.head)		kfree(self->rx_buff.head);		/* Remove ourselves */	dev_self[self->index] = NULL;	kfree(self);		return 0;}void irport_start(struct irport_cb *self){	unsigned long flags;	int iobase;	iobase = self->io.sir_base;	spin_lock_irqsave(&self->lock, flags);	irport_stop(self);	/* 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);	spin_unlock_irqrestore(&self->lock, flags);}void irport_stop(struct irport_cb *self){	unsigned long flags;	int iobase;	iobase = self->io.sir_base;	spin_lock_irqsave(&self->lock, flags);	/* Reset UART */	outb(0, iobase+UART_MCR);		/* Turn off interrupts */	outb(0, iobase+UART_IER);	spin_unlock_irqrestore(&self->lock, flags);}/* * Function irport_probe (void) * *    Start IO port  * */int irport_probe(int iobase){	IRDA_DEBUG(4, __FUNCTION__ "(), iobase=%#x\n", iobase);	return 0;}/* * Function irport_change_speed (self, speed) * *    Set speed of IrDA port to specified baudrate * */void irport_change_speed(void *priv, __u32 speed){	struct irport_cb *self = (struct irport_cb *) priv;	unsigned long flags;	int iobase; 	int fcr;    /* FIFO control reg */	int lcr;    /* Line control reg */	int divisor;	IRDA_DEBUG(2, __FUNCTION__ "(), Setting speed to: %d\n", speed);	ASSERT(self != NULL, return;);	iobase = self->io.sir_base;		/* Update accounting for new speed */	self->io.speed = speed;	spin_lock_irqsave(&self->lock, flags);	/* Turn off interrupts */	outb(0, iobase+UART_IER); 	divisor = SPEED_MAX/speed;		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 (self->io.speed < 38400)		fcr |= UART_FCR_TRIGGER_1;	else 		fcr |= UART_FCR_TRIGGER_14;        	/* 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 */	outb(/*UART_IER_RLSI|*/UART_IER_RDI/*|UART_IER_THRI*/, iobase+UART_IER);	spin_unlock_irqrestore(&self->lock, flags);}/* * 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;	int ret = 0;	IRDA_DEBUG(2, __FUNCTION__ "(), <%ld>\n", jiffies); 	self = (struct irport_cb *) task->instance;	ASSERT(self != NULL, return -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:		WARNING(__FUNCTION__ 			"(), changing speed of dongle timed out!\n");		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:		ERROR(__FUNCTION__ "(), unknown state %d\n", task->state);		irda_task_next_state(task, IRDA_TASK_DONE);		ret = -1;		break;	}		return ret;}/* * Function irport_write_wakeup (tty) * *    Called by the driver when there's room for more data.  If we have *    more packets to send, we send them here. * */static void irport_write_wakeup(struct irport_cb *self){	int actual = 0;	int iobase;	int fcr;	ASSERT(self != NULL, return;);	IRDA_DEBUG(4, __FUNCTION__ "()\n");	iobase = self->io.sir_base;	/* Finished with frame?  */	if (self->tx_buff.len > 0)  {		/* Write data left in transmit buffer */		actual = irport_write(iobase, self->io.fifo_size, 				      self->tx_buff.data, self->tx_buff.len);		self->tx_buff.data += actual;		self->tx_buff.len  -= actual;	} else {		/* 		 *  Now serial buffer is almost free & we can start 		 *  transmission of another packet. But first we must check		 *  if we need to change the speed of the hardware		 */		if (self->new_speed) {			IRDA_DEBUG(5, __FUNCTION__ "(), Changing speed!\n");			irda_task_execute(self, __irport_change_speed, 					  irport_change_speed_complete, 					  NULL, (void *) self->new_speed);			self->new_speed = 0;		} else {			/* Tell network layer that we want more frames */			netif_wake_queue(self->netdev);		}		self->stats.tx_packets++;

⌨️ 快捷键说明

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