patch-2.3.6 linux/drivers/net/irda/irport.c
Next file: linux/drivers/net/irda/litelink.c
Previous file: linux/drivers/net/irda/girbil.c
Back to the patch index
Back to the overall index
- Lines: 510
- Date:
Mon Jun 7 16:18:58 1999
- Orig file:
v2.3.5/linux/drivers/net/irda/irport.c
- Orig date:
Mon May 31 22:28:05 1999
diff -u --recursive --new-file v2.3.5/linux/drivers/net/irda/irport.c linux/drivers/net/irda/irport.c
@@ -1,43 +1,38 @@
/*********************************************************************
- *
+ *
* Filename: irport.c
- * Version: 0.9
- * Description: Serial driver for IrDA.
+ * 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: Sat May 23 23:15:20 1998
+ * Modified at: Tue Jun 1 10:02:42 1999
* Modified by: Dag Brattli <dagb@cs.uit.no>
* Sources: serial.c by Linus Torvalds
*
- * Copyright (c) 1997,1998 Dag Brattli <dagb@cs.uit.no>
- * All Rights Reserved.
+ * Copyright (c) 1997, 1998, 1999 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.
- *
- * Neither Dag Brattli nor University of Tromsų admit liability nor
- * provide warranty for any of this software. This material is
- * provided "AS-IS" and at no charge.
- *
- * NOTICE:
+ *
+ * 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. If
- * your chipset is is UART only, you should probably use IrTTY instead
- * since the Linux serial driver is probably more robust and optimized.
- *
- * The functions in this file may be used by FIR drivers, but this
- * driver knows nothing about FIR drivers so don't ever insert such
- * code into this file. Instead you should code your FIR driver in a
- * separate file, and then call the functions in this file if
- * necessary. This is becase it is difficult to use the Linux serial
- * driver with a FIR driver becase they must share interrupts etc. Most
- * FIR chipsets can function in advanced SIR mode, and you should
- * probably use that mode instead of the UART compatibility mode (and
- * then just forget about this file)
+ * 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.
*
********************************************************************/
@@ -48,14 +43,15 @@
#include <linux/ioport.h>
#include <linux/malloc.h>
#include <linux/string.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <asm/io.h>
+#include <linux/skbuff.h>
+#include <linux/serial_reg.h>
#include <linux/errno.h>
#include <linux/init.h>
-#include <linux/skbuff.h>
-#include <linux/serial_reg.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/spinlock.h>
#include <net/irda/irda.h>
#include <net/irda/irmod.h>
@@ -86,7 +82,6 @@
static int irport_net_init(struct device *dev);
static int irport_net_open(struct device *dev);
static int irport_net_close(struct device *dev);
-static void irport_wait_until_sent(struct irda_device *idev);
static int irport_is_receiving(struct irda_device *idev);
static void irport_set_dtr_rts(struct irda_device *idev, int dtr, int rts);
static int irport_raw_write(struct irda_device *idev, __u8 *buf, int len);
@@ -158,12 +153,15 @@
idev->io.io_ext = IO_EXTENT;
idev->io.fifo_size = 16;
+ idev->netdev.base_addr = iobase;
+ idev->netdev.irq = irq;
+
/* Lock the port that we need */
ret = check_region(idev->io.iobase2, idev->io.io_ext);
if (ret < 0) {
- DEBUG( 0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
- idev->io.iobase2);
- /* w83977af_cleanup( self->idev); */
+ DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
+ idev->io.iobase2);
+ /* irport_cleanup(self->idev); */
return -ENODEV;
}
request_region(idev->io.iobase2, idev->io.io_ext, idev->name);
@@ -207,12 +205,10 @@
static int irport_close(struct irda_device *idev)
{
- DEBUG(0, __FUNCTION__ "()\n");
-
ASSERT(idev != NULL, return -1;);
ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return -1;);
- /* Release the PORT that this driver is using */
+ /* Release the IO-port that this driver is using */
DEBUG(0 , __FUNCTION__ "(), Releasing Region %03x\n",
idev->io.iobase2);
release_region(idev->io.iobase2, idev->io.io_ext);
@@ -224,24 +220,37 @@
return 0;
}
-void irport_start(int iobase)
+void irport_start(struct irda_device *idev, int iobase)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&idev->lock, flags);
+
+ irport_stop(idev, iobase);
+
/* 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), iobase+UART_IER);
+ outb(UART_IER_RLSI | UART_IER_RDI |UART_IER_THRI, iobase+UART_IER);
+ spin_unlock_irqrestore(&idev->lock, flags);
}
-void irport_stop(int iobase)
+void irport_stop(struct irda_device *idev, int iobase)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&idev->lock, flags);
+
/* Reset UART */
outb(0, iobase+UART_MCR);
/* Turn off interrupts */
outb(0, iobase+UART_IER);
+
+ spin_unlock_irqrestore(&idev->lock, flags);
}
/*
@@ -254,24 +263,24 @@
{
DEBUG(4, __FUNCTION__ "(), iobase=%#x\n", iobase);
-
return 0;
}
/*
* Function irport_change_speed (idev, speed)
*
- * Set speed of port to specified baudrate
+ * Set speed of IrDA port to specified baudrate
*
*/
void irport_change_speed(struct irda_device *idev, int speed)
{
+ unsigned long flags;
int iobase;
int fcr; /* FIFO control reg */
int lcr; /* Line control reg */
int divisor;
- DEBUG( 0, __FUNCTION__ "(), Setting speed to: %d\n", speed);
+ DEBUG(0, __FUNCTION__ "(), Setting speed to: %d\n", speed);
ASSERT(idev != NULL, return;);
ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
@@ -281,12 +290,24 @@
/* Update accounting for new speed */
idev->io.baudrate = speed;
+ spin_lock_irqsave(&idev->lock, flags);
+
/* Turn off interrupts */
outb(0, iobase+UART_IER);
divisor = SPEED_MAX/speed;
- fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14;
+ 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 (idev->io.baudrate < 38400)
+ fcr |= UART_FCR_TRIGGER_1;
+ else
+ fcr |= UART_FCR_TRIGGER_14;
/* IrDA ports use 8N1 */
lcr = UART_LCR_WLEN8;
@@ -297,8 +318,10 @@
outb(lcr, iobase+UART_LCR); /* Set 8N1 */
outb(fcr, iobase+UART_FCR); /* Enable FIFO's */
- /* Turn on receive interrups */
- outb(UART_IER_RLSI|UART_IER_RDI, iobase+UART_IER);
+ /* Turn on interrups */
+ outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, iobase+UART_IER);
+
+ spin_unlock_irqrestore(&self->lock, flags);
}
/*
@@ -312,10 +335,13 @@
{
int actual = 0;
int iobase;
+ int fcr;
ASSERT(idev != NULL, return;);
ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
+ DEBUG(4, __FUNCTION__ "()\n");
+
/* Finished with frame? */
if (idev->tx_buff.len > 0) {
/* Write data left in transmit buffer */
@@ -335,9 +361,18 @@
/* Schedule network layer, so we can get some more frames */
mark_bh(NET_BH);
- outb(UART_FCR_ENABLE_FIFO |
- UART_FCR_TRIGGER_14 |
- UART_FCR_CLEAR_RCVR, iobase+UART_FCR); /* Enable FIFO's */
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR;
+
+ if (idev->io.baudrate < 38400)
+ fcr |= UART_FCR_TRIGGER_1;
+ else
+ fcr |= UART_FCR_TRIGGER_14;
+
+ /*
+ * Reset Rx FIFO to make sure that all reflected transmit data
+ * will be discarded
+ */
+ outb(fcr, iobase+UART_FCR);
/* Turn on receive interrupts */
outb(UART_IER_RLSI|UART_IER_RDI, iobase+UART_IER);
@@ -347,7 +382,7 @@
/*
* Function irport_write (driver)
*
- *
+ * Fill Tx FIFO with transmit data
*
*/
static int irport_write(int iobase, int fifo_size, __u8 *buf, int len)
@@ -356,21 +391,18 @@
/* Tx FIFO should be empty! */
if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) {
- DEBUG( 0, __FUNCTION__ "(), failed, fifo not empty!\n");
+ DEBUG(0, __FUNCTION__ "(), failed, fifo not empty!\n");
return -1;
}
/* Fill FIFO with current frame */
- while (( fifo_size-- > 0) && (actual < len)) {
+ while ((fifo_size-- > 0) && (actual < len)) {
/* Transmit next byte */
- outb( buf[actual], iobase+UART_TX);
+ outb(buf[actual], iobase+UART_TX);
actual++;
}
- DEBUG(4, __FUNCTION__ "(), fifo_size %d ; %d sent of %d\n",
- fifo_size, actual, len);
-
return actual;
}
@@ -384,11 +416,10 @@
int irport_hard_xmit(struct sk_buff *skb, struct device *dev)
{
struct irda_device *idev;
+ unsigned long flags;
int actual = 0;
int iobase;
- DEBUG(5, __FUNCTION__ "(), dev=%p\n", dev);
-
ASSERT(dev != NULL, return 0;);
idev = (struct irda_device *) dev->priv;
@@ -399,8 +430,19 @@
iobase = idev->io.iobase2;
/* Lock transmit buffer */
- if (irda_lock((void *) &dev->tbusy) == FALSE)
- return -EBUSY;
+ if (irda_lock((void *) &dev->tbusy) == FALSE) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return -EBUSY;
+
+ WARNING("%s: transmit timed out\n", dev->name);
+ irport_start(idev, iobase);
+ irport_change_speed(idev, idev->io.baudrate);
+
+ dev->trans_start = jiffies;
+ }
+
+ spin_lock_irqsave(&idev->lock, flags);
/* Init tx buffer */
idev->tx_buff.data = idev->tx_buff.head;
@@ -415,6 +457,8 @@
/* Turn on transmit finished interrupt. Will fire immediately! */
outb(UART_IER_THRI, iobase+UART_IER);
+ spin_unlock_irqrestore(&idev->lock, flags);
+
dev_kfree_skb(skb);
return 0;
@@ -431,10 +475,7 @@
int iobase;
int boguscount = 0;
- if (!idev)
- return;
-
- DEBUG(4, __FUNCTION__ "()\n");
+ ASSERT(idev != NULL, return;);
iobase = idev->io.iobase2;
@@ -466,27 +507,42 @@
int boguscount = 0;
if (!idev) {
- printk(KERN_WARNING __FUNCTION__
- "() irq %d for unknown device.\n", irq);
+ WARNING(__FUNCTION__ "() irq %d for unknown device.\n", irq);
return;
}
+ spin_lock(&idev->lock);
+
idev->netdev.interrupt = 1;
iobase = idev->io.iobase2;
- iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+ iir = inb(iobase+UART_IIR) & UART_IIR_ID;
while (iir) {
/* Clear interrupt */
lsr = inb(iobase+UART_LSR);
- if ((iir & UART_IIR_THRI) && (lsr & UART_LSR_THRE)) {
- /* Transmitter ready for data */
- irport_write_wakeup(idev);
- } else if ((iir & UART_IIR_RDI) && (lsr & UART_LSR_DR)) {
- /* Receive interrupt */
- irport_receive(idev);
- }
+ DEBUG(4, __FUNCTION__ "(), iir=%02x, lsr=%02x, iobase=%#x\n",
+ iir, lsr, iobase);
+
+ switch (iir) {
+ case UART_IIR_RLSI:
+ DEBUG(0, __FUNCTION__ "(), RLSI\n");
+ break;
+ case UART_IIR_RDI:
+ if (lsr & UART_LSR_DR)
+ /* Receive interrupt */
+ irport_receive(idev);
+ break;
+ case UART_IIR_THRI:
+ if (lsr & UART_LSR_THRE)
+ /* Transmitter ready for data */
+ irport_write_wakeup(idev);
+ break;
+ default:
+ DEBUG(0, __FUNCTION__ "(), unhandled IIR=%#x\n", iir);
+ break;
+ }
/* Make sure we don't stay here to long */
if (boguscount++ > 32)
@@ -495,6 +551,8 @@
iir = inb(iobase + UART_IIR) & UART_IIR_ID;
}
idev->netdev.interrupt = 0;
+
+ spin_unlock(&idev->lock);
}
static int irport_net_init(struct device *dev)
@@ -524,18 +582,21 @@
iobase = idev->io.iobase2;
if (request_irq(idev->io.irq2, irport_interrupt, 0, idev->name,
- (void *) idev)) {
+ (void *) idev))
return -EAGAIN;
- }
+
+ irport_start(idev, iobase);
+
+ MOD_INC_USE_COUNT;
/* Ready to play! */
dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
- MOD_INC_USE_COUNT;
-
- irport_start(iobase);
+ /* Change speed to make sure dongles follow us again */
+ if (idev->change_speed)
+ idev->change_speed(idev, 9600);
return 0;
}
@@ -558,12 +619,12 @@
iobase = idev->io.iobase2;
- irport_stop(iobase);
-
/* Stop device */
dev->tbusy = 1;
dev->start = 0;
+ irport_stop(idev, iobase);
+
free_irq(idev->io.irq2, idev);
MOD_DEC_USE_COUNT;
@@ -571,12 +632,32 @@
return 0;
}
-static void irport_wait_until_sent(struct irda_device *idev)
+/*
+ * Function irport_wait_until_sent (idev)
+ *
+ * Delay exectution until finished transmitting
+ *
+ */
+void irport_wait_until_sent(struct irda_device *idev)
{
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(60*HZ/1000);
+ int iobase;
+
+ iobase = idev->io.iobase2;
+
+ /* Wait until Tx FIFO is empty */
+ while (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) {
+ DEBUG(2, __FUNCTION__ "(), waiting!\n");
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(MSECS_TO_JIFFIES(60));
+ }
}
+/*
+ * Function irport_is_receiving (idev)
+ *
+ * Returns true is we are currently receiving data
+ *
+ */
static int irport_is_receiving(struct irda_device *idev)
{
return (idev->rx_buff.state != OUTSIDE_FRAME);
@@ -635,6 +716,9 @@
MODULE_PARM(io, "1-4i");
MODULE_PARM(irq, "1-4i");
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Half duplex serial driver for IrDA SIR mode");
/*
* Function cleanup_module (void)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)