📄 irport.c
字号:
/********************************************************************* * * 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 + -