summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@home.transmeta.com>2003-05-25 03:28:43 -0700
committerLinus Torvalds <torvalds@home.transmeta.com>2003-05-25 03:28:43 -0700
commit01166d963aabab97d0d0d757b6a6e115c8a2cfcf (patch)
treec2c69645bc280a1bf23c4362a5826da0efbb1666
parente71eb380233a26e4665f45c3434062606daf4df6 (diff)
parentdf3a5c9ff0eed97cfb4c1594ffcab755b76f430e (diff)
Merge bk://kernel.bkbits.net/jgarzik/irda-2.5
into home.transmeta.com:/home/torvalds/v2.5/linux
-rw-r--r--drivers/net/irda/Kconfig21
-rw-r--r--drivers/net/irda/Makefile9
-rw-r--r--drivers/net/irda/ali-ircc.c4
-rw-r--r--drivers/net/irda/donauboe.c4
-rw-r--r--drivers/net/irda/irport.c480
-rw-r--r--drivers/net/irda/irtty-sir.c9
-rw-r--r--drivers/net/irda/nsc-ircc.c4
-rw-r--r--drivers/net/irda/sir_kthread.c7
-rw-r--r--drivers/net/irda/smc-ircc.c12
-rw-r--r--drivers/net/irda/smsc-ircc2.c2441
-rw-r--r--drivers/net/irda/smsc-ircc2.h194
-rw-r--r--drivers/net/irda/smsc-sio.h100
-rw-r--r--drivers/net/irda/w83977af_ir.c2
-rw-r--r--include/net/irda/iriap.h2
-rw-r--r--include/net/irda/irlan_common.h2
-rw-r--r--include/net/irda/irlmp_event.h20
-rw-r--r--include/net/irda/irport.h1
-rw-r--r--net/irda/af_irda.c13
-rw-r--r--net/irda/ircomm/ircomm_core.c36
-rw-r--r--net/irda/ircomm/ircomm_event.c10
-rw-r--r--net/irda/ircomm/ircomm_lmp.c56
-rw-r--r--net/irda/ircomm/ircomm_ttp.c53
-rw-r--r--net/irda/ircomm/ircomm_tty.c21
-rw-r--r--net/irda/ircomm/ircomm_tty_attach.c22
-rw-r--r--net/irda/iriap.c136
-rw-r--r--net/irda/iriap_event.c35
-rw-r--r--net/irda/irlan/irlan_common.c14
-rw-r--r--net/irda/irlan/irlan_eth.c8
-rw-r--r--net/irda/irlap.c16
-rw-r--r--net/irda/irlap_event.c7
-rw-r--r--net/irda/irlap_frame.c185
-rw-r--r--net/irda/irlmp.c171
-rw-r--r--net/irda/irlmp_event.c41
-rw-r--r--net/irda/irlmp_frame.c23
-rw-r--r--net/irda/irnet/irnet.h6
-rw-r--r--net/irda/irnet/irnet_irda.c30
-rw-r--r--net/irda/irnet/irnet_ppp.c2
-rw-r--r--net/irda/irttp.c119
38 files changed, 3654 insertions, 662 deletions
diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig
index 379a7f6b3b6a..dfcb2e5dab44 100644
--- a/drivers/net/irda/Kconfig
+++ b/drivers/net/irda/Kconfig
@@ -289,16 +289,29 @@ config TOSHIBA_FIR
<file:Documentation/modules.txt>.
The module will be called donauboe.
-config SMC_IRCC_FIR
- tristate "SMC IrCC (EXPERIMENTAL)"
+config SMC_IRCC_OLD
+ tristate "SMC IrCC (old driver) (EXPERIMENTAL)"
depends on EXPERIMENTAL && IRDA
help
Say Y here if you want to build support for the SMC Infrared
Communications Controller. It is used in the Fujitsu Lifebook 635t
- and Sony PCG-505TX. If you want to compile it as a module, say M
- here and read <file:Documentation/modules.txt>. The module will be
+ and Sony PCG-505TX. This driver is obsolete, will no more be
+ maintained and will be removed in favor of the new driver.
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. The module will be
called smc-ircc.
+config SMC_IRCC_FIR
+ tristate "SMSC IrCC (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && IRDA
+ help
+ Say Y here if you want to build support for the SMC Infrared
+ Communications Controller. It is used in a wide variety of
+ laptops (Fujitsu, Sony, Compaq and some Toshiba).
+ If you want to compile it as a module, say M here and read
+ <file:Documentation/modules.txt>. The module will be
+ called smsc-ircc2.o.
+
config ALI_FIR
tristate "ALi M5123 FIR (EXPERIMENTAL)"
depends on EXPERIMENTAL && IRDA
diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile
index 4d31d2b3c95c..fa89bee5c77b 100644
--- a/drivers/net/irda/Makefile
+++ b/drivers/net/irda/Makefile
@@ -15,13 +15,14 @@ obj-$(CONFIG_WINBOND_FIR) += w83977af_ir.o
obj-$(CONFIG_SA1100_FIR) += sa1100_ir.o
obj-$(CONFIG_TOSHIBA_OLD) += toshoboe.o
obj-$(CONFIG_TOSHIBA_FIR) += donauboe.o
-obj-$(CONFIG_SMC_IRCC_FIR) += smc-ircc.o irport.o
+obj-$(CONFIG_SMC_IRCC_OLD) += smc-ircc.o irport.o
+obj-$(CONFIG_SMC_IRCC_FIR) += smsc-ircc2.o
obj-$(CONFIG_ALI_FIR) += ali-ircc.o
obj-$(CONFIG_VLSI_FIR) += vlsi_ir.o
# Old dongle drivers for old SIR drivers
-obj-$(CONFIG_ESI_OLD) += esi.o
-obj-$(CONFIG_TEKRAM_OLD) += tekram.o
-obj-$(CONFIG_ACTISYS_OLD) += actisys.o
+obj-$(CONFIG_ESI_DONGLE_OLD) += esi.o
+obj-$(CONFIG_TEKRAM_DONGLE_OLD) += tekram.o
+obj-$(CONFIG_ACTISYS_DONGLE_OLD) += actisys.o
obj-$(CONFIG_GIRBIL_DONGLE) += girbil.o
obj-$(CONFIG_LITELINK_DONGLE) += litelink.o
obj-$(CONFIG_OLD_BELKIN_DONGLE) += old_belkin.o
diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c
index 46223bbbb7c3..15a81f7b3775 100644
--- a/drivers/net/irda/ali-ircc.c
+++ b/drivers/net/irda/ali-ircc.c
@@ -1451,6 +1451,7 @@ static int ali_ircc_fir_hard_xmit(struct sk_buff *skb, struct net_device *dev)
/* Check for empty frame */
if (!skb->len) {
ali_ircc_change_speed(self, speed);
+ dev->trans_start = jiffies;
spin_unlock_irqrestore(&self->lock, flags);
dev_kfree_skb(skb);
return 0;
@@ -1560,6 +1561,7 @@ static int ali_ircc_fir_hard_xmit(struct sk_buff *skb, struct net_device *dev)
/* Restore bank register */
switch_bank(iobase, BANK0);
+ dev->trans_start = jiffies;
spin_unlock_irqrestore(&self->lock, flags);
dev_kfree_skb(skb);
@@ -1974,6 +1976,7 @@ static int ali_ircc_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev)
/* Check for empty frame */
if (!skb->len) {
ali_ircc_change_speed(self, speed);
+ dev->trans_start = jiffies;
spin_unlock_irqrestore(&self->lock, flags);
dev_kfree_skb(skb);
return 0;
@@ -1993,6 +1996,7 @@ static int ali_ircc_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev)
/* Turn on transmit finished interrupt. Will fire immediately! */
outb(UART_IER_THRI, iobase+UART_IER);
+ dev->trans_start = jiffies;
spin_unlock_irqrestore(&self->lock, flags);
dev_kfree_skb(skb);
diff --git a/drivers/net/irda/donauboe.c b/drivers/net/irda/donauboe.c
index 185035d28b17..25d510e5deb3 100644
--- a/drivers/net/irda/donauboe.c
+++ b/drivers/net/irda/donauboe.c
@@ -1051,7 +1051,9 @@ toshoboe_hard_xmit (struct sk_buff *skb, struct net_device *dev)
toshoboe_checkstuck (self);
- /* Check if we need to change the speed */
+ dev->trans_start = jiffies;
+
+ /* Check if we need to change the speed */
/* But not now. Wait after transmission if mtt not required */
speed=irda_get_next_speed(skb);
if ((speed != self->io.speed) && (speed != -1))
diff --git a/drivers/net/irda/irport.c b/drivers/net/irda/irport.c
index 310e7ead2f4a..e0d26d1b5cd5 100644
--- a/drivers/net/irda/irport.c
+++ b/drivers/net/irda/irport.c
@@ -11,6 +11,7 @@
* 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
@@ -48,6 +49,7 @@
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/spinlock.h>
+#include <linux/delay.h>
#include <linux/rtnetlink.h>
#include <asm/system.h>
@@ -72,14 +74,14 @@ 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 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_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 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);
@@ -169,7 +171,7 @@ irport_open(int i, unsigned int iobase, unsigned int irq)
self->io.sir_base = iobase;
self->io.sir_ext = IO_EXTENT;
self->io.irq = irq;
- self->io.fifo_size = 16;
+ self->io.fifo_size = 16; /* 16550A and compatible */
/* Initialize QoS for this device */
irda_init_max_qos_capabilies(&self->qos);
@@ -181,39 +183,47 @@ irport_open(int i, unsigned int iobase, unsigned int irq)
irda_qos_bits_to_value(&self->qos);
self->flags = IFF_SIR|IFF_PIO;
+ self->mode = IRDA_IRLAP;
+
+ /* 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)
+ return NULL;
+ 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->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);
+ kfree_skb(self->rx_buff.skb);
+ self->rx_buff.skb = NULL;
+ self->rx_buff.head = NULL;
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("%s(), dev_alloc() failed!\n", __FUNCTION__);
return NULL;
}
self->netdev = dev;
+ /* Keep track of module usage */
+ SET_MODULE_OWNER(dev);
/* May be overridden by piggyback drivers */
dev->priv = (void *) self;
@@ -241,7 +251,8 @@ irport_open(int i, unsigned int iobase, unsigned int irq)
ERROR("%s(), register_netdev() failed!\n", __FUNCTION__);
return NULL;
}
- MESSAGE("IrDA: Registered device %s\n", dev->name);
+ MESSAGE("IrDA: Registered device %s (irport io=0x%X irq=%d)\n",
+ dev->name, iobase, irq);
return self;
}
@@ -270,8 +281,9 @@ int irport_close(struct irport_cb *self)
if (self->tx_buff.head)
kfree(self->tx_buff.head);
- if (self->rx_buff.head)
- kfree(self->rx_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;
@@ -306,6 +318,9 @@ void irport_stop(struct irport_cb *self)
/* 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);
@@ -327,6 +342,33 @@ int irport_probe(int iobase)
}
/*
+ * 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
@@ -337,11 +379,12 @@ void irport_change_speed(void *priv, __u32 speed)
{
struct irport_cb *self = (struct irport_cb *) priv;
int iobase;
- int fcr; /* FIFO control reg */
- int lcr; /* Line control reg */
+ unsigned int fcr; /* FIFO control reg */
+ unsigned int lcr; /* Line control reg */
int divisor;
ASSERT(self != NULL, return;);
+ ASSERT(speed != 0, return;);
IRDA_DEBUG(1, "%s(), Setting speed to: %d - iobase=%#x\n",
__FUNCTION__, speed, self->io.sir_base);
@@ -358,18 +401,9 @@ void irport_change_speed(void *priv, __u32 speed)
divisor = SPEED_MAX/speed;
- fcr = UART_FCR_ENABLE_FIFO;
+ /* Get proper fifo configuration */
+ fcr = irport_get_fcr(speed);
- /*
- * 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;
@@ -380,7 +414,7 @@ void irport_change_speed(void *priv, __u32 speed)
outb(fcr, iobase+UART_FCR); /* Enable FIFO's */
/* Turn on interrups */
- /* This will generate a fata interrupt storm.
+ /* 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);
}
@@ -467,8 +501,8 @@ int __irport_change_speed(struct irda_task *task)
irda_task_next_state(task, IRDA_TASK_DONE);
ret = -1;
break;
- }
- /* Put stuff in the sate we found them - Jean II */
+ }
+ /* Put stuff in the state we found them - Jean II */
if(wasunlocked) {
spin_unlock_irqrestore(&self->lock, flags);
}
@@ -477,98 +511,6 @@ int __irport_change_speed(struct irda_task *task)
}
/*
- * 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, "%s()\n", __FUNCTION__);
-
- 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;
-
- /* Turn on transmit finished interrupt. */
- outb(UART_IER_THRI, iobase+UART_IER);
- } 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, "%s(), Changing speed!\n", __FUNCTION__);
- irda_task_execute(self, __irport_change_speed,
- irport_change_speed_complete,
- NULL, (void *) self->new_speed);
- self->new_speed = 0;
- IRDA_DEBUG(5, "%s(), Speed changed!\n", __FUNCTION__ );
- } else {
- /* Tell network layer that we want more frames */
- netif_wake_queue(self->netdev);
- }
- self->stats.tx_packets++;
-
- /*
- * Reset Rx FIFO to make sure that all reflected transmit data
- * is discarded. This is needed for half duplex operation
- */
- fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR;
- if (self->io.speed < 38400)
- fcr |= UART_FCR_TRIGGER_1;
- else
- fcr |= UART_FCR_TRIGGER_14;
-
- outb(fcr, iobase+UART_FCR);
-
- /* Turn on receive interrupts */
- outb(UART_IER_RDI, iobase+UART_IER);
- }
-}
-
-/*
- * Function irport_write (driver)
- *
- * Fill Tx FIFO with transmit data
- *
- */
-static int irport_write(int iobase, int fifo_size, __u8 *buf, int len)
-{
- int actual = 0;
-
- /* Tx FIFO should be empty! */
- if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) {
- IRDA_DEBUG(0, "%s(), failed, fifo not empty!\n", __FUNCTION__);
- return 0;
- }
-
- /* Fill FIFO with current frame */
- while ((fifo_size-- > 0) && (actual < len)) {
- /* Transmit next byte */
- outb(buf[actual], iobase+UART_TX);
-
- actual++;
- }
-
- return actual;
-}
-
-/*
* Function irport_change_speed_complete (task)
*
* Called when the change speed operation completes
@@ -604,24 +546,80 @@ 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;
+ ASSERT(self != NULL, return;);
iobase = self->io.sir_base;
- WARNING("%s: transmit timed out\n", dev->name);
+ 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);
- dev->trans_start = jiffies;
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);
+
+ if(count == 0)
+ IRDA_DEBUG(0, "%s(): stuck transmitter\n", __FUNCTION__);
+}
+
+/*
* Function irport_hard_start_xmit (struct sk_buff *skb, struct net_device *dev)
*
* Transmits the current frame until FIFO is full, then
@@ -645,8 +643,8 @@ int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev)
iobase = self->io.sir_base;
netif_stop_queue(dev);
-
- /* Make sure tests *& speed change are atomic */
+
+ /* Make sure tests & speed change are atomic */
spin_lock_irqsave(&self->lock, flags);
/* Check if we need to change the speed */
@@ -654,10 +652,21 @@ int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev)
if ((speed != self->io.speed) && (speed != -1)) {
/* Check for empty frame */
if (!skb->len) {
+ /*
+ * We send frames one by one in SIR mode (no
+ * pipelining), so at this point, if we were sending
+ * a previous frame, we just received the interrupt
+ * telling us it is finished (UART_IIR_THRI).
+ * Therefore, waiting for the transmitter to really
+ * finish draining the fifo won't take too long.
+ * And the interrupt handler is not expected to run.
+ * - Jean II */
+ irport_wait_hw_transmitter_finish(self);
/* Better go there already locked - Jean II */
irda_task_execute(self, __irport_change_speed,
irport_change_speed_complete,
NULL, (void *) speed);
+ dev->trans_start = jiffies;
spin_unlock_irqrestore(&self->lock, flags);
dev_kfree_skb(skb);
return 0;
@@ -674,9 +683,13 @@ int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev)
self->stats.tx_bytes += self->tx_buff.len;
+ /* We are transmitting */
+ self->transmitting = 1;
+
/* Turn on transmit finished interrupt. Will fire immediately! */
outb(UART_IER_THRI, iobase+UART_IER);
+ dev->trans_start = jiffies;
spin_unlock_irqrestore(&self->lock, flags);
dev_kfree_skb(skb);
@@ -685,12 +698,100 @@ int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev)
}
/*
+ * Function irport_write (driver)
+ *
+ * Fill Tx FIFO with transmit data
+ *
+ * Called only from irport_write_wakeup()
+ */
+static inline int irport_write(int iobase, int fifo_size, __u8 *buf, int len)
+{
+ int actual = 0;
+
+ /* Fill FIFO with current frame */
+ while ((actual < fifo_size) && (actual < len)) {
+ /* Transmit next byte */
+ outb(buf[actual], iobase+UART_TX);
+
+ actual++;
+ }
+
+ return actual;
+}
+
+/*
+ * 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.
+ *
+ * Called only from irport_interrupt()
+ * Make sure this function is *not* called while we are receiving,
+ * otherwise we will reset fifo and loose data :-(
+ */
+static inline void irport_write_wakeup(struct irport_cb *self)
+{
+ int actual = 0;
+ int iobase;
+ unsigned int fcr;
+
+ ASSERT(self != NULL, return;);
+
+ IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
+
+ 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) {
+ irport_wait_hw_transmitter_finish(self);
+ 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++;
+
+ /*
+ * Reset Rx FIFO to make sure that all reflected transmit data
+ * is discarded. This is needed for half duplex operation
+ */
+ fcr = irport_get_fcr(self->io.speed);
+ fcr |= UART_FCR_CLEAR_RCVR;
+ outb(fcr, iobase+UART_FCR);
+
+ /* Finished transmitting */
+ self->transmitting = 0;
+
+ /* Turn on receive interrupts */
+ outb(UART_IER_RDI, iobase+UART_IER);
+
+ IRDA_DEBUG(1, "%s() : finished Tx\n", __FUNCTION__);
+ }
+}
+
+/*
* Function irport_receive (self)
*
* Receive one frame from the infrared port
*
+ * Called only from irport_interrupt()
*/
-static void irport_receive(struct irport_cb *self)
+static inline void irport_receive(struct irport_cb *self)
{
int boguscount = 0;
int iobase;
@@ -739,40 +840,51 @@ irqreturn_t irport_interrupt(int irq, void *dev_id, struct pt_regs *regs)
iobase = self->io.sir_base;
- iir = inb(iobase+UART_IIR) & UART_IIR_ID;
- while (iir) {
- handled = 1;
+ /* Cut'n'paste interrupt routine from serial.c
+ * This version try to minimise latency and I/O operations.
+ * Simplified and modified to enforce half duplex operation.
+ * - Jean II */
- /* Clear interrupt */
+ /* Check status even is iir reg is cleared, more robust and
+ * eliminate a read on the I/O bus - Jean II */
+ do {
+ /* Get interrupt status ; Clear interrupt */
lsr = inb(iobase+UART_LSR);
+
+ /* Are we receiving or transmitting ? */
+ if(!self->transmitting) {
+ /* Received something ? */
+ if (lsr & UART_LSR_DR)
+ irport_receive(self);
+ } else {
+ /* Room in Tx fifo ? */
+ if (lsr & (UART_LSR_THRE | UART_LSR_TEMT))
+ irport_write_wakeup(self);
+ }
- IRDA_DEBUG(4, "%s(), iir=%02x, lsr=%02x, iobase=%#x\n",
- __FUNCTION__, iir, lsr, iobase);
+ /* A bit hackish, but working as expected... Jean II */
+ if(lsr & (UART_LSR_THRE | UART_LSR_TEMT | UART_LSR_DR))
+ handled = 1;
- switch (iir) {
- case UART_IIR_RLSI:
- IRDA_DEBUG(2, "%s(), RLSI\n", __FUNCTION__);
- break;
- case UART_IIR_RDI:
- /* Receive interrupt */
- irport_receive(self);
- break;
- case UART_IIR_THRI:
- if (lsr & UART_LSR_THRE)
- /* Transmitter ready for data */
- irport_write_wakeup(self);
- break;
- default:
- IRDA_DEBUG(0, "%s(), unhandled IIR=%#x\n", __FUNCTION__, iir);
- break;
- }
-
- /* Make sure we don't stay here too long */
- if (boguscount++ > 100)
+ /* Make sure we don't stay here to long */
+ if (boguscount++ > 10) {
+ WARNING("%s() irq handler looping : lsr=%02x\n",
+ __FUNCTION__, lsr);
break;
+ }
+
+ /* Read interrupt register */
+ iir = inb(iobase+UART_IIR);
+
+ /* Enable this debug only when no other options and at low
+ * bit rates, otherwise it may cause Rx overruns (lsr=63).
+ * - Jean II */
+ IRDA_DEBUG(6, "%s(), iir=%02x, lsr=%02x, iobase=%#x\n",
+ __FUNCTION__, iir, lsr, iobase);
+
+ /* As long as interrupt pending... */
+ } while ((iir & UART_IIR_NO_INT) == 0);
- iir = inb(iobase + UART_IIR) & UART_IIR_ID;
- }
spin_unlock(&self->lock);
return IRQ_RETVAL(handled);
}
@@ -800,8 +912,8 @@ int irport_net_open(struct net_device *dev)
char hwname[16];
unsigned long flags;
- IRDA_DEBUG(1, "%s()\n", __FUNCTION__);
-
+ IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
+
ASSERT(dev != NULL, return -1;);
self = (struct irport_cb *) dev->priv;
@@ -815,7 +927,12 @@ int irport_net_open(struct net_device *dev)
}
spin_lock_irqsave(&self->lock, flags);
+ /* Init uart */
irport_start(self);
+ /* Set 9600 bauds per default, including at the dongle */
+ irda_task_execute(self, __irport_change_speed,
+ irport_change_speed_complete,
+ NULL, (void *) 9600);
spin_unlock_irqrestore(&self->lock, flags);
@@ -828,12 +945,9 @@ int irport_net_open(struct net_device *dev)
*/
self->irlap = irlap_open(dev, &self->qos, hwname);
- /* FIXME: change speed of dongle */
/* Ready to play! */
netif_start_queue(dev);
-
- MOD_INC_USE_COUNT;
return 0;
}
@@ -873,40 +987,16 @@ int irport_net_close(struct net_device *dev)
free_irq(self->io.irq, dev);
- MOD_DEC_USE_COUNT;
-
return 0;
}
/*
- * Function irport_wait_until_sent (self)
- *
- * Delay exectution until finished transmitting
- *
- */
-#if 0
-void irport_wait_until_sent(struct irport_cb *self)
-{
- int iobase;
-
- iobase = self->io.sir_base;
-
- /* Wait until Tx FIFO is empty */
- while (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) {
- IRDA_DEBUG(2, "%s(), waiting!\n", __FUNCTION__);
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(MSECS_TO_JIFFIES(60));
- }
-}
-#endif
-
-/*
* Function irport_is_receiving (self)
*
* Returns true is we are currently receiving data
*
*/
-static int irport_is_receiving(struct irport_cb *self)
+static inline int irport_is_receiving(struct irport_cb *self)
{
return (self->rx_buff.state != OUTSIDE_FRAME);
}
@@ -998,6 +1088,12 @@ static int irport_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
break;
}
+ /* Locking :
+ * irda_device_dongle_init() can't be locked.
+ * irda_task_execute() doesn't need to be locked.
+ * Jean II
+ */
+
/* Initialize dongle */
dongle = irda_device_dongle_init(dev, irq->ifr_dongle);
if (!dongle)
diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c
index 87deb312b94c..a2d387ced051 100644
--- a/drivers/net/irda/irtty-sir.c
+++ b/drivers/net/irda/irtty-sir.c
@@ -504,10 +504,7 @@ static int irtty_open(struct tty_struct *tty)
struct sirtty_cb *priv;
int ret = 0;
- /* unfortunately, there's no tty_ldisc->owner field
- * so there is some window for SMP race with rmmod
- */
- MOD_INC_USE_COUNT;
+ /* Module stuff handled via irda_ldisc.owner - Jean II */
/* First make sure we're not already connected. */
if (tty->disc_data != NULL) {
@@ -569,7 +566,6 @@ static int irtty_open(struct tty_struct *tty)
out_put:
sirdev_put_instance(dev);
out:
- MOD_DEC_USE_COUNT;
return ret;
}
@@ -614,8 +610,6 @@ static void irtty_close(struct tty_struct *tty)
tty->driver->stop(tty);
kfree(priv);
-
- MOD_DEC_USE_COUNT;
}
/* ------------------------------------------------------- */
@@ -633,6 +627,7 @@ static struct tty_ldisc irda_ldisc = {
.receive_buf = irtty_receive_buf,
.receive_room = irtty_receive_room,
.write_wakeup = irtty_write_wakeup,
+ .owner = THIS_MODULE,
};
/* ------------------------------------------------------- */
diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c
index ada9a3feda69..6f847c07d710 100644
--- a/drivers/net/irda/nsc-ircc.c
+++ b/drivers/net/irda/nsc-ircc.c
@@ -1096,6 +1096,7 @@ static int nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev)
* to make sure packets gets through the
* proper xmit handler - Jean II */
}
+ dev->trans_start = jiffies;
spin_unlock_irqrestore(&self->lock, flags);
dev_kfree_skb(skb);
return 0;
@@ -1120,6 +1121,7 @@ static int nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev)
/* Restore bank register */
outb(bank, iobase+BSR);
+ dev->trans_start = jiffies;
spin_unlock_irqrestore(&self->lock, flags);
dev_kfree_skb(skb);
@@ -1164,6 +1166,7 @@ static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev)
* the speed change has been done.
* Jean II */
}
+ dev->trans_start = jiffies;
spin_unlock_irqrestore(&self->lock, flags);
dev_kfree_skb(skb);
return 0;
@@ -1250,6 +1253,7 @@ static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev)
/* Restore bank register */
outb(bank, iobase+BSR);
+ dev->trans_start = jiffies;
spin_unlock_irqrestore(&self->lock, flags);
dev_kfree_skb(skb);
diff --git a/drivers/net/irda/sir_kthread.c b/drivers/net/irda/sir_kthread.c
index 293c05c27cd0..cc38b8809b14 100644
--- a/drivers/net/irda/sir_kthread.c
+++ b/drivers/net/irda/sir_kthread.c
@@ -151,6 +151,13 @@ static int irda_thread(void *startup)
while (irda_rq_queue.thread != NULL) {
+ /* We use TASK_INTERRUPTIBLE, rather than
+ * TASK_UNINTERRUPTIBLE. Andrew Morton made this
+ * change ; he told me that it is safe, because "signal
+ * blocking is now handled in daemonize()", he added
+ * that the problem is that "uninterruptible sleep
+ * contributes to load average", making user worry.
+ * Jean II */
set_task_state(current, TASK_INTERRUPTIBLE);
add_wait_queue(&irda_rq_queue.kick, &wait);
if (list_empty(&irda_rq_queue.request_list))
diff --git a/drivers/net/irda/smc-ircc.c b/drivers/net/irda/smc-ircc.c
index f802455b8ada..91f476547a83 100644
--- a/drivers/net/irda/smc-ircc.c
+++ b/drivers/net/irda/smc-ircc.c
@@ -529,6 +529,9 @@ static int __init ircc_open(unsigned int fir_base, unsigned int sir_base)
irport->priv = self;
+ /* Keep track of module usage */
+ SET_MODULE_OWNER(self->netdev);
+
/* Initialize IO */
self->io = &irport->io;
self->io->fir_base = fir_base;
@@ -747,6 +750,7 @@ static int ircc_hard_xmit(struct sk_buff *skb, struct net_device *dev)
/* Check for empty frame */
if (!skb->len) {
ircc_change_speed(self, speed);
+ dev->trans_start = jiffies;
spin_unlock_irqrestore(&self->irport->lock, flags);
dev_kfree_skb(skb);
return 0;
@@ -776,6 +780,7 @@ static int ircc_hard_xmit(struct sk_buff *skb, struct net_device *dev)
/* Transmit frame */
ircc_dma_xmit(self, iobase, 0);
}
+ dev->trans_start = jiffies;
spin_unlock_irqrestore(&self->irport->lock, flags);
dev_kfree_skb(skb);
@@ -1090,8 +1095,6 @@ static int ircc_net_open(struct net_device *dev)
WARNING("%s(), unable to allocate DMA=%d\n", __FUNCTION__, self->io->dma);
return -EAGAIN;
}
-
- MOD_INC_USE_COUNT;
return 0;
}
@@ -1124,8 +1127,6 @@ static int ircc_net_close(struct net_device *dev)
free_dma(self->io->dma);
- MOD_DEC_USE_COUNT;
-
return 0;
}
@@ -1187,6 +1188,9 @@ static int __exit ircc_close(struct ircc_cb *self)
iobase = self->irport->io.fir_base;
+ if (self->pmdev)
+ pm_unregister(self->pmdev);
+
/* This will destroy irport */
irport_close(self->irport);
diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c
new file mode 100644
index 000000000000..447c6aa4d1c8
--- /dev/null
+++ b/drivers/net/irda/smsc-ircc2.c
@@ -0,0 +1,2441 @@
+/*********************************************************************
+ * $Id: smsc-ircc2.c,v 1.19.2.5 2002/10/27 11:34:26 dip Exp $
+ *
+ * Description: Driver for the SMC Infrared Communications Controller
+ * Status: Experimental.
+ * Author: Daniele Peri (peri@csai.unipa.it)
+ * Created at:
+ * Modified at:
+ * Modified by:
+ *
+ * Copyright (c) 2002 Daniele Peri
+ * All Rights Reserved.
+ * Copyright (c) 2002 Jean Tourrilhes
+ *
+ *
+ * Based on smc-ircc.c:
+ *
+ * Copyright (c) 2001 Stefani Seibold
+ * Copyright (c) 1999-2001 Dag Brattli
+ * Copyright (c) 1998-1999 Thomas Davis,
+ *
+ * and irport.c:
+ *
+ * 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
+ *
+ ********************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/rtnetlink.h>
+#include <linux/serial_reg.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/byteorder.h>
+
+#include <linux/spinlock.h>
+#include <linux/pm.h>
+
+#include <net/irda/wrapper.h>
+#include <net/irda/irda.h>
+#include <net/irda/irda_device.h>
+
+#include "smsc-ircc2.h"
+#include "smsc-sio.h"
+
+/* Types */
+
+struct smsc_transceiver {
+ char *name;
+ void (*set_for_speed)(int fir_base, u32 speed);
+ int (*probe)(int fir_base);
+};
+typedef struct smsc_transceiver smsc_transceiver_t;
+
+#if 0
+struct smc_chip {
+ char *name;
+ u16 flags;
+ u8 devid;
+ u8 rev;
+};
+typedef struct smc_chip smc_chip_t;
+#endif
+
+struct smsc_chip {
+ char *name;
+ #if 0
+ u8 type;
+ #endif
+ u16 flags;
+ u8 devid;
+ u8 rev;
+};
+typedef struct smsc_chip smsc_chip_t;
+
+struct smsc_chip_address {
+ unsigned int cfg_base;
+ unsigned int type;
+};
+typedef struct smsc_chip_address smsc_chip_address_t;
+
+/* Private data for each instance */
+struct smsc_ircc_cb {
+ struct net_device *netdev; /* Yes! we are some kind of netdevice */
+ struct net_device_stats stats;
+ struct irlap_cb *irlap; /* The link layer we are binded to */
+
+ chipio_t io; /* IrDA controller information */
+ iobuff_t tx_buff; /* Transmit buffer */
+ iobuff_t rx_buff; /* Receive buffer */
+
+ struct qos_info qos; /* QoS capabilities for this device */
+
+ spinlock_t lock; /* For serializing operations */
+
+ __u32 new_speed;
+ __u32 flags; /* Interface flags */
+
+ int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */
+ int tx_len; /* Number of frames in tx_buff */
+
+ int transceiver;
+ struct pm_dev *pmdev;
+};
+
+/* Constants */
+
+static const char *driver_name = "smsc-ircc2";
+#define DIM(x) (sizeof(x)/(sizeof(*(x))))
+#define SMSC_IRCC2_C_IRDA_FALLBACK_SPEED 9600
+#define SMSC_IRCC2_C_DEFAULT_TRANSCEIVER 1
+#define SMSC_IRCC2_C_NET_TIMEOUT 0
+#define SMSC_IRCC2_C_SIR_STOP 0
+
+/* Prototypes */
+
+static int smsc_ircc_open(unsigned int firbase, unsigned int sirbase, u8 dma, u8 irq);
+static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base);
+static int smsc_ircc_setup_io(struct smsc_ircc_cb *self, unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq);
+static int smsc_ircc_setup_buffers(struct smsc_ircc_cb *self);
+static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self);
+static int smsc_ircc_setup_netdev(struct smsc_ircc_cb *self);
+static void smsc_ircc_init_chip(struct smsc_ircc_cb *self);
+static int __exit smsc_ircc_close(struct smsc_ircc_cb *self);
+static int smsc_ircc_dma_receive(struct smsc_ircc_cb *self, int iobase);
+static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self, int iobase);
+static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self);
+static int smsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev);
+static int smsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev);
+static void smsc_ircc_dma_xmit(struct smsc_ircc_cb *self, int iobase, int bofs);
+static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self, int iobase);
+static void smsc_ircc_change_speed(void *priv, u32 speed);
+static void smsc_ircc_set_sir_speed(void *priv, u32 speed);
+static irqreturn_t smsc_ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void smsc_ircc_interrupt_sir(int irq, void *dev_id, struct pt_regs *regs);
+static void smsc_ircc_sir_start(struct smsc_ircc_cb *self);
+#if SMSC_IRCC2_C_SIR_STOP
+static void smsc_ircc_sir_stop(struct smsc_ircc_cb *self);
+#endif
+static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self);
+static int smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len);
+static int smsc_ircc_net_init(struct net_device *dev);
+static int smsc_ircc_net_open(struct net_device *dev);
+static int smsc_ircc_net_close(struct net_device *dev);
+static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+#if SMSC_IRCC2_C_NET_TIMEOUT
+static void smsc_ircc_timeout(struct net_device *dev);
+#endif
+static struct net_device_stats *smsc_ircc_net_get_stats(struct net_device *dev);
+static int smsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data);
+static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self);
+static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self);
+static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed);
+static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self);
+
+/* Probing */
+static int __init smsc_ircc_look_for_chips(void);
+static const smsc_chip_t * __init smsc_ircc_probe(unsigned short cfg_base,u8 reg,const smsc_chip_t *chip,char *type);
+static int __init smsc_superio_flat(const smsc_chip_t *chips, unsigned short cfg_base, char *type);
+static int __init smsc_superio_paged(const smsc_chip_t *chips, unsigned short cfg_base, char *type);
+static int __init smsc_superio_fdc(unsigned short cfg_base);
+static int __init smsc_superio_lpc(unsigned short cfg_base);
+
+/* Transceivers specific functions */
+
+static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed);
+static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base);
+static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed);
+static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base);
+static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed);
+static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base);
+
+/* Power Management */
+
+static void smsc_ircc_suspend(struct smsc_ircc_cb *self);
+static void smsc_ircc_wakeup(struct smsc_ircc_cb *self);
+static int smsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data);
+
+
+/* Transceivers for SMSC-ircc */
+
+smsc_transceiver_t smsc_transceivers[]=
+{
+ { "Toshiba Satellite 1800 (GP data pin select)", smsc_ircc_set_transceiver_toshiba_sat1800, smsc_ircc_probe_transceiver_toshiba_sat1800},
+ { "Fast pin select", smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select, smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select},
+ { "ATC IRMode", smsc_ircc_set_transceiver_smsc_ircc_atc, smsc_ircc_probe_transceiver_smsc_ircc_atc},
+ { NULL, NULL}
+};
+#define SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS (DIM(smsc_transceivers)-1)
+
+/* SMC SuperIO chipsets definitions */
+
+#define KEY55_1 0 /* SuperIO Configuration mode with Key <0x55> */
+#define KEY55_2 1 /* SuperIO Configuration mode with Key <0x55,0x55> */
+#define NoIRDA 2 /* SuperIO Chip has no IRDA Port */
+#define SIR 0 /* SuperIO Chip has only slow IRDA */
+#define FIR 4 /* SuperIO Chip has fast IRDA */
+#define SERx4 8 /* SuperIO Chip supports 115,2 KBaud * 4=460,8 KBaud */
+
+static smsc_chip_t __initdata fdc_chips_flat[]=
+{
+ /* Base address 0x3f0 or 0x370 */
+ { "37C44", KEY55_1|NoIRDA, 0x00, 0x00 }, /* This chip cannot be detected */
+ { "37C665GT", KEY55_2|NoIRDA, 0x65, 0x01 },
+ { "37C665GT", KEY55_2|NoIRDA, 0x66, 0x01 },
+ { "37C669", KEY55_2|SIR|SERx4, 0x03, 0x02 },
+ { "37C669", KEY55_2|SIR|SERx4, 0x04, 0x02 }, /* ID? */
+ { "37C78", KEY55_2|NoIRDA, 0x78, 0x00 },
+ { "37N769", KEY55_1|FIR|SERx4, 0x28, 0x00 },
+ { "37N869", KEY55_1|FIR|SERx4, 0x29, 0x00 },
+ { NULL }
+};
+
+static smsc_chip_t __initdata fdc_chips_paged[]=
+{
+ /* Base address 0x3f0 or 0x370 */
+ { "37B72X", KEY55_1|SIR|SERx4, 0x4c, 0x00 },
+ { "37B77X", KEY55_1|SIR|SERx4, 0x43, 0x00 },
+ { "37B78X", KEY55_1|SIR|SERx4, 0x44, 0x00 },
+ { "37B80X", KEY55_1|SIR|SERx4, 0x42, 0x00 },
+ { "37C67X", KEY55_1|FIR|SERx4, 0x40, 0x00 },
+ { "37C93X", KEY55_2|SIR|SERx4, 0x02, 0x01 },
+ { "37C93XAPM", KEY55_1|SIR|SERx4, 0x30, 0x01 },
+ { "37C93XFR", KEY55_2|FIR|SERx4, 0x03, 0x01 },
+ { "37M707", KEY55_1|SIR|SERx4, 0x42, 0x00 },
+ { "37M81X", KEY55_1|SIR|SERx4, 0x4d, 0x00 },
+ { "37N958FR", KEY55_1|FIR|SERx4, 0x09, 0x04 },
+ { "37N971", KEY55_1|FIR|SERx4, 0x0a, 0x00 },
+ { "37N972", KEY55_1|FIR|SERx4, 0x0b, 0x00 },
+ { NULL }
+};
+
+static smsc_chip_t __initdata lpc_chips_flat[]=
+{
+ /* Base address 0x2E or 0x4E */
+ { "47N227", KEY55_1|FIR|SERx4, 0x5a, 0x00 },
+ { "47N267", KEY55_1|FIR|SERx4, 0x5e, 0x00 },
+ { NULL }
+};
+
+static smsc_chip_t __initdata lpc_chips_paged[]=
+{
+ /* Base address 0x2E or 0x4E */
+ { "47B27X", KEY55_1|SIR|SERx4, 0x51, 0x00 },
+ { "47B37X", KEY55_1|SIR|SERx4, 0x52, 0x00 },
+ { "47M10X", KEY55_1|SIR|SERx4, 0x59, 0x00 },
+ { "47M120", KEY55_1|NoIRDA|SERx4, 0x5c, 0x00 },
+ { "47M13X", KEY55_1|SIR|SERx4, 0x59, 0x00 },
+ { "47M14X", KEY55_1|SIR|SERx4, 0x5f, 0x00 },
+ { "47N252", KEY55_1|FIR|SERx4, 0x0e, 0x00 },
+ { "47S42X", KEY55_1|SIR|SERx4, 0x57, 0x00 },
+ { NULL }
+};
+
+#define SMSCSIO_TYPE_FDC 1
+#define SMSCSIO_TYPE_LPC 2
+#define SMSCSIO_TYPE_FLAT 4
+#define SMSCSIO_TYPE_PAGED 8
+
+static smsc_chip_address_t __initdata possible_addresses[]=
+{
+ {0x3f0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED},
+ {0x370, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED},
+ {0xe0, SMSCSIO_TYPE_FDC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED},
+ {0x2e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED},
+ {0x4e, SMSCSIO_TYPE_LPC|SMSCSIO_TYPE_FLAT|SMSCSIO_TYPE_PAGED},
+ {0,0}
+};
+
+/* Globals */
+
+static struct smsc_ircc_cb *dev_self[] = { NULL, NULL};
+
+static int ircc_irq=255;
+static int ircc_dma=255;
+static int ircc_fir=0;
+static int ircc_sir=0;
+static int ircc_cfg=0;
+static int ircc_transceiver=0;
+
+static unsigned short dev_count=0;
+
+static inline void register_bank(int iobase, int bank)
+{
+ outb(((inb(iobase+IRCC_MASTER) & 0xf0) | (bank & 0x07)),
+ iobase+IRCC_MASTER);
+}
+
+
+/*******************************************************************************
+ *
+ *
+ * SMSC-ircc stuff
+ *
+ *
+ *******************************************************************************/
+
+/*
+ * Function smsc_ircc_init ()
+ *
+ * Initialize chip. Just try to find out how many chips we are dealing with
+ * and where they are
+ */
+int __init smsc_ircc_init(void)
+{
+ int ret=-ENODEV;
+
+ IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+ dev_count=0;
+
+ if ((ircc_fir>0)&&(ircc_sir>0)) {
+ MESSAGE(" Overriding FIR address 0x%04x\n", ircc_fir);
+ MESSAGE(" Overriding SIR address 0x%04x\n", ircc_sir);
+
+ if (smsc_ircc_open(ircc_fir, ircc_sir, ircc_dma, ircc_irq) == 0)
+ return 0;
+
+ return -ENODEV;
+ }
+
+ /* try user provided configuration register base address */
+ if (ircc_cfg>0) {
+ MESSAGE(" Overriding configuration address 0x%04x\n", ircc_cfg);
+ if (!smsc_superio_fdc(ircc_cfg))
+ ret = 0;
+ if (!smsc_superio_lpc(ircc_cfg))
+ ret = 0;
+ }
+
+ if(smsc_ircc_look_for_chips()>0) ret = 0;
+
+ return ret;
+}
+
+/*
+ * Function smsc_ircc_open (firbase, sirbase, dma, irq)
+ *
+ * Try to open driver instance
+ *
+ */
+static int __init smsc_ircc_open(unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq)
+{
+ struct smsc_ircc_cb *self;
+ int err;
+
+ IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+ err= smsc_ircc_present(fir_base, sir_base);
+ if(err) return -ENODEV;
+
+ if (dev_count>DIM(dev_self)) {
+ WARNING("%s(), too many devices!\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ /*
+ * Allocate new instance of the driver
+ */
+ self = kmalloc(sizeof(struct smsc_ircc_cb), GFP_KERNEL);
+ if (self == NULL) {
+ ERROR("%s, Can't allocate memory for control block!\n",
+ driver_name);
+ return -ENOMEM;
+ }
+ memset(self, 0, sizeof(struct smsc_ircc_cb));
+
+ /* Need to store self somewhere */
+ dev_self[dev_count++] = self;
+ spin_lock_init(&self->lock);
+
+ err = smsc_ircc_setup_buffers(self);
+ if(err) return err;
+
+ err= smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq);
+ if(err) return err;
+
+ smsc_ircc_setup_qos(self);
+
+ self->flags = IFF_FIR|IFF_MIR|IFF_SIR|IFF_DMA|IFF_PIO;
+
+ smsc_ircc_init_chip(self);
+
+ if(ircc_transceiver > 0 && ircc_transceiver < SMSC_IRCC2_C_NUMBER_OF_TRANSCEIVERS) self->transceiver = ircc_transceiver;
+ else smsc_ircc_probe_transceiver(self);
+
+ err = smsc_ircc_setup_netdev(self);
+ if(err) return err;
+
+ self->pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, smsc_ircc_pmproc);
+ if (self->pmdev)
+ self->pmdev->data = self;
+
+ return 0;
+}
+
+/*
+ * Function smsc_ircc_present(fir_base, sir_base)
+ *
+ * Check the smsc-ircc chip presence
+ *
+ */
+static int smsc_ircc_present(unsigned int fir_base, unsigned int sir_base)
+{
+ unsigned char low, high, chip, config, dma, irq, version;
+
+ if (check_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT) < 0) {
+ WARNING("%s: can't get fir_base of 0x%03x\n",
+ __FUNCTION__, fir_base);
+ return -ENODEV;
+ }
+#if POSSIBLE_USED_BY_SERIAL_DRIVER
+ if (check_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT) < 0) {
+ WARNING("%s: can't get sir_base of 0x%03x\n",
+ __FUNCTION__, sir_base);
+ return -ENODEV;
+ }
+#endif
+
+ register_bank(fir_base, 3);
+
+ high = inb(fir_base+IRCC_ID_HIGH);
+ low = inb(fir_base+IRCC_ID_LOW);
+ chip = inb(fir_base+IRCC_CHIP_ID);
+ version = inb(fir_base+IRCC_VERSION);
+ config = inb(fir_base+IRCC_INTERFACE);
+ dma = config & IRCC_INTERFACE_DMA_MASK;
+ irq = (config & IRCC_INTERFACE_IRQ_MASK) >> 4;
+
+ if (high != 0x10 || low != 0xb8 || (chip != 0xf1 && chip != 0xf2)) {
+ WARNING("%s(), addr 0x%04x - no device found!\n",
+ __FUNCTION__, fir_base);
+ return -ENODEV;
+ }
+ MESSAGE("SMsC IrDA Controller found\n IrCC version %d.%d, "
+ "firport 0x%03x, sirport 0x%03x dma=%d, irq=%d\n",
+ chip & 0x0f, version, fir_base, sir_base, dma, irq);
+
+ return 0;
+}
+
+/*
+ * Function smsc_ircc_setup_buffers(self)
+ *
+ * Setup RX/TX buffers
+ *
+ */
+static int smsc_ircc_setup_buffers(struct smsc_ircc_cb *self)
+{
+ self->rx_buff.truesize = SMSC_IRCC2_RX_BUFF_TRUESIZE;
+ self->tx_buff.truesize = SMSC_IRCC2_TX_BUFF_TRUESIZE;
+
+ self->rx_buff.head = (u8 *) kmalloc(self->rx_buff.truesize,
+ GFP_KERNEL|GFP_DMA);
+ if (self->rx_buff.head == NULL) {
+ ERROR("%s, Can't allocate memory for receive buffer!\n",
+ driver_name);
+ kfree(self);
+ return -ENOMEM;
+ }
+
+ self->tx_buff.head = (u8 *) kmalloc(self->tx_buff.truesize,
+ GFP_KERNEL|GFP_DMA);
+ if (self->tx_buff.head == NULL) {
+ ERROR("%s, Can't allocate memory for transmit buffer!\n",
+ driver_name);
+ kfree(self->rx_buff.head);
+ kfree(self);
+ return -ENOMEM;
+ }
+
+ memset(self->rx_buff.head, 0, self->rx_buff.truesize);
+ 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;
+
+ return 0;
+}
+
+/*
+ * Function smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq)
+ *
+ * Setup I/O
+ *
+ */
+static int smsc_ircc_setup_io(struct smsc_ircc_cb *self, unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq)
+{
+ unsigned char config, chip_dma, chip_irq;
+ void *ret;
+
+ register_bank(fir_base, 3);
+ config = inb(fir_base+IRCC_INTERFACE);
+ chip_dma = config & IRCC_INTERFACE_DMA_MASK;
+ chip_irq = (config & IRCC_INTERFACE_IRQ_MASK) >> 4;
+
+ self->io.fir_base = fir_base;
+ self->io.sir_base = sir_base;
+ self->io.fir_ext = SMSC_IRCC2_FIR_CHIP_IO_EXTENT;
+ self->io.sir_ext = SMSC_IRCC2_SIR_CHIP_IO_EXTENT;
+ self->io.fifo_size = SMSC_IRCC2_FIFO_SIZE;
+ self->io.speed = SMSC_IRCC2_C_IRDA_FALLBACK_SPEED;
+
+ if (irq < 255) {
+ if (irq != chip_irq)
+ MESSAGE("%s, Overriding IRQ - chip says %d, using %d\n",
+ driver_name, chip_irq, irq);
+ self->io.irq = irq;
+ }
+ else
+ self->io.irq = chip_irq;
+
+ if (dma < 255) {
+ if (dma != chip_dma)
+ MESSAGE("%s, Overriding DMA - chip says %d, using %d\n",
+ driver_name, chip_dma, dma);
+ self->io.dma = dma;
+ }
+ else
+ self->io.dma = chip_dma;
+
+ ret = request_region(self->io.fir_base, self->io.fir_ext, driver_name);
+ if (!ret) {
+ WARNING("%s(), can't get iobase of 0x%03x\n",
+ __FUNCTION__, self->io.fir_base);
+ kfree(self->tx_buff.head);
+ kfree(self->rx_buff.head);
+ kfree(self);
+ return -ENODEV;
+ }
+ ret = request_region(self->io.sir_base, self->io.sir_ext, driver_name);
+ if (!ret) {
+ WARNING("%s(), can't get iobase of 0x%03x\n",
+ __FUNCTION__, self->io.sir_base);
+ release_region(self->io.fir_base, self->io.fir_ext);
+ kfree(self->tx_buff.head);
+ kfree(self->rx_buff.head);
+ kfree(self);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * Function smsc_ircc_setup_qos(self)
+ *
+ * Setup qos
+ *
+ */
+static void smsc_ircc_setup_qos(struct smsc_ircc_cb *self)
+{
+ /* 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|IR_576000|IR_1152000|(IR_4000000 << 8);
+
+ self->qos.min_turn_time.bits = SMSC_IRCC2_MIN_TURN_TIME;
+ self->qos.window_size.bits = SMSC_IRCC2_WINDOW_SIZE;
+ irda_qos_bits_to_value(&self->qos);
+}
+
+/*
+ * Function smsc_ircc_init_chip(self)
+ *
+ * Init chip
+ *
+ */
+static void smsc_ircc_init_chip(struct smsc_ircc_cb *self)
+{
+ int iobase, ir_mode, ctrl, fast;
+
+ ASSERT( self != NULL, return; );
+ iobase = self->io.fir_base;
+
+ ir_mode = IRCC_CFGA_IRDA_SIR_A;
+ ctrl = 0;
+ fast = 0;
+
+ register_bank(iobase, 0);
+ outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER);
+ outb(0x00, iobase+IRCC_MASTER);
+
+ register_bank(iobase, 1);
+ outb(((inb(iobase+IRCC_SCE_CFGA) & 0x87) | ir_mode),
+ iobase+IRCC_SCE_CFGA);
+
+#ifdef smsc_669 /* Uses pin 88/89 for Rx/Tx */
+ outb(((inb(iobase+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM),
+ iobase+IRCC_SCE_CFGB);
+#else
+ outb(((inb(iobase+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR),
+ iobase+IRCC_SCE_CFGB);
+#endif
+ (void) inb(iobase+IRCC_FIFO_THRESHOLD);
+ outb(SMSC_IRCC2_FIFO_THRESHOLD, iobase+IRCC_FIFO_THRESHOLD);
+
+ register_bank(iobase, 4);
+ outb((inb(iobase+IRCC_CONTROL) & 0x30) | ctrl, iobase+IRCC_CONTROL);
+
+ register_bank(iobase, 0);
+ outb(fast, iobase+IRCC_LCR_A);
+
+ smsc_ircc_set_sir_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED);
+
+ /* Power on device */
+ outb(0x00, iobase+IRCC_MASTER);
+}
+
+/*
+ * Function smsc_ircc_setup_netdev(self)
+ *
+ * Alloc and setup network device
+ *
+ */
+static int smsc_ircc_setup_netdev(struct smsc_ircc_cb *self)
+{
+ struct net_device *dev;
+ int err;
+ /* Alloc netdev */
+
+ if (!(dev = dev_alloc("irda%d", &err))) {
+ ERROR("%s(), dev_alloc() failed!\n", __FUNCTION__);
+ kfree(self->tx_buff.head);
+ kfree(self->rx_buff.head);
+ kfree(self);
+ return -ENOMEM;
+ }
+
+ dev->priv = (void *) self;
+ self->netdev = dev;
+
+ dev->init = smsc_ircc_net_init;
+ dev->hard_start_xmit = smsc_ircc_hard_xmit_sir;
+ #if SMSC_IRCC2_C_NET_TIMEOUT
+ dev->tx_timeout = smsc_ircc_timeout;
+ dev->watchdog_timeo = HZ*2; /* Allow enough time for speed change */
+ #endif
+ dev->open = smsc_ircc_net_open;
+ dev->stop = smsc_ircc_net_close;
+ dev->do_ioctl = smsc_ircc_net_ioctl;
+ dev->get_stats = smsc_ircc_net_get_stats;
+
+ /* Make ifconfig display some details */
+ dev->base_addr = self->io.fir_base;
+ dev->irq = self->io.irq;
+
+ rtnl_lock();
+ err = register_netdevice(dev);
+ rtnl_unlock();
+ if (err) {
+ ERROR("%s(), register_netdev() failed!\n", __FUNCTION__);
+ kfree(self->tx_buff.head);
+ kfree(self->rx_buff.head);
+ kfree(self);
+ return -ENODEV;
+ }
+ MESSAGE("IrDA: Registered device %s\n", dev->name);
+
+ return 0;
+}
+
+/*
+ * Function smsc_ircc_net_ioctl (dev, rq, cmd)
+ *
+ * Process IOCTL commands for this device
+ *
+ */
+static int smsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct if_irda_req *irq = (struct if_irda_req *) rq;
+ struct smsc_ircc_cb *self;
+ unsigned long flags;
+ int ret = 0;
+
+ ASSERT(dev != NULL, return -1;);
+
+ self = dev->priv;
+
+ ASSERT(self != NULL, return -1;);
+
+ IRDA_DEBUG(2, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, cmd);
+
+ switch (cmd) {
+ case SIOCSBANDWIDTH: /* Set bandwidth */
+ if (!capable(CAP_NET_ADMIN))
+ ret = -EPERM;
+ else {
+ /* Make sure we are the only one touching
+ * self->io.speed and the hardware - Jean II */
+ spin_lock_irqsave(&self->lock, flags);
+ smsc_ircc_change_speed(self, irq->ifr_baudrate);
+ spin_unlock_irqrestore(&self->lock, flags);
+ }
+ break;
+ case SIOCSMEDIABUSY: /* Set media busy */
+ if (!capable(CAP_NET_ADMIN)) {
+ ret = -EPERM;
+ break;
+ }
+
+ irda_device_set_media_busy(self->netdev, TRUE);
+ break;
+ case SIOCGRECEIVING: /* Check if we are receiving right now */
+ irq->ifr_receiving = smsc_ircc_is_receiving(self);
+ break;
+ #if 0
+ case SIOCSDTRRTS:
+ if (!capable(CAP_NET_ADMIN)) {
+ ret = -EPERM;
+ break;
+ }
+ smsc_ircc_sir_set_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts);
+ break;
+ #endif
+ default:
+ ret = -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+static struct net_device_stats *smsc_ircc_net_get_stats(struct net_device *dev)
+{
+ struct smsc_ircc_cb *self = (struct smsc_ircc_cb *) dev->priv;
+
+ return &self->stats;
+}
+
+#if SMSC_IRCC2_C_NET_TIMEOUT
+/*
+ * Function smsc_ircc_timeout (struct net_device *dev)
+ *
+ * The networking timeout management.
+ *
+ */
+
+static void smsc_ircc_timeout(struct net_device *dev)
+{
+ struct smsc_ircc_cb *self;
+ unsigned long flags;
+
+ self = (struct smsc_ircc_cb *) dev->priv;
+
+ WARNING("%s: transmit timed out, changing speed to: %d\n", dev->name, self->io.speed);
+ spin_lock_irqsave(&self->lock, flags);
+ smsc_ircc_sir_start(self);
+ smsc_ircc_change_speed(self, self->io.speed);
+ dev->trans_start = jiffies;
+ netif_wake_queue(dev);
+ spin_unlock_irqrestore(&self->lock, flags);
+}
+#endif
+
+/*
+ * Function smsc_ircc_hard_xmit_sir (struct sk_buff *skb, struct net_device *dev)
+ *
+ * Transmits the current frame until FIFO is full, then
+ * waits until the next transmit interrupt, and continues until the
+ * frame is transmitted.
+ */
+int smsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev)
+{
+ struct smsc_ircc_cb *self;
+ unsigned long flags;
+ int iobase;
+ s32 speed;
+
+ IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+ ASSERT(dev != NULL, return 0;);
+
+ self = (struct smsc_ircc_cb *) dev->priv;
+ ASSERT(self != NULL, return 0;);
+
+ iobase = self->io.sir_base;
+
+ netif_stop_queue(dev);
+
+ /* Make sure test of self->io.speed & speed change are atomic */
+ spin_lock_irqsave(&self->lock, flags);
+
+ /* Check if we need to change the speed */
+ speed = irda_get_next_speed(skb);
+ if ((speed != self->io.speed) && (speed != -1)) {
+ /* Check for empty frame */
+ if (!skb->len) {
+ /*
+ * We send frames one by one in SIR mode (no
+ * pipelining), so at this point, if we were sending
+ * a previous frame, we just received the interrupt
+ * telling us it is finished (UART_IIR_THRI).
+ * Therefore, waiting for the transmitter to really
+ * finish draining the fifo won't take too long.
+ * And the interrupt handler is not expected to run.
+ * - Jean II */
+ smsc_ircc_sir_wait_hw_transmitter_finish(self);
+ smsc_ircc_change_speed(self, speed);
+ spin_unlock_irqrestore(&self->lock, flags);
+ dev_kfree_skb(skb);
+ return 0;
+ } else {
+ self->new_speed = speed;
+ }
+ }
+
+ /* Init tx buffer */
+ self->tx_buff.data = self->tx_buff.head;
+
+ /* Copy skb to tx_buff while wrapping, stuffing and making CRC */
+ self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data,
+ self->tx_buff.truesize);
+
+ self->stats.tx_bytes += self->tx_buff.len;
+
+ /* Turn on transmit finished interrupt. Will fire immediately! */
+ outb(UART_IER_THRI, iobase+UART_IER);
+
+ spin_unlock_irqrestore(&self->lock, flags);
+
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+/*
+ * Function smsc_ircc_set_fir_speed (self, baud)
+ *
+ * Change the speed of the device
+ *
+ */
+static void smsc_ircc_set_fir_speed(struct smsc_ircc_cb *self, u32 speed)
+{
+ int fir_base, ir_mode, ctrl, fast;
+
+ ASSERT(self != NULL, return;);
+ fir_base = self->io.fir_base;
+
+ self->io.speed = speed;
+
+ switch(speed) {
+ default:
+ case 576000:
+ ir_mode = IRCC_CFGA_IRDA_HDLC;
+ ctrl = IRCC_CRC;
+ fast = 0;
+ IRDA_DEBUG(0, "%s(), handling baud of 576000\n", __FUNCTION__);
+ break;
+ case 1152000:
+ ir_mode = IRCC_CFGA_IRDA_HDLC;
+ ctrl = IRCC_1152 | IRCC_CRC;
+ fast = IRCC_LCR_A_FAST | IRCC_LCR_A_GP_DATA;
+ IRDA_DEBUG(0, "%s(), handling baud of 1152000\n",
+ __FUNCTION__);
+ break;
+ case 4000000:
+ ir_mode = IRCC_CFGA_IRDA_4PPM;
+ ctrl = IRCC_CRC;
+ fast = IRCC_LCR_A_FAST;
+ IRDA_DEBUG(0, "%s(), handling baud of 4000000\n",
+ __FUNCTION__);
+ break;
+ }
+ #if 0
+ Now in tranceiver!
+ /* This causes an interrupt */
+ register_bank(fir_base, 0);
+ outb((inb(fir_base+IRCC_LCR_A) & 0xbf) | fast, fir_base+IRCC_LCR_A);
+ #endif
+
+ register_bank(fir_base, 1);
+ outb(((inb(fir_base+IRCC_SCE_CFGA) & IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK) | ir_mode), fir_base+IRCC_SCE_CFGA);
+
+ register_bank(fir_base, 4);
+ outb((inb(fir_base+IRCC_CONTROL) & 0x30) | ctrl, fir_base+IRCC_CONTROL);
+}
+
+/*
+ * Function smsc_ircc_fir_start(self)
+ *
+ * Change the speed of the device
+ *
+ */
+static void smsc_ircc_fir_start(struct smsc_ircc_cb *self)
+{
+ struct net_device *dev;
+ int fir_base;
+
+ IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+ ASSERT(self != NULL, return;);
+ dev = self->netdev;
+ ASSERT(dev != NULL, return;);
+
+ fir_base = self->io.fir_base;
+
+ /* Reset everything */
+
+ /* Install FIR transmit handler */
+ dev->hard_start_xmit = smsc_ircc_hard_xmit_fir;
+
+ /* Clear FIFO */
+ outb(inb(fir_base+IRCC_LCR_A)|IRCC_LCR_A_FIFO_RESET, fir_base+IRCC_LCR_A);
+
+ /* Enable interrupt */
+ /*outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, fir_base+IRCC_IER);*/
+
+ register_bank(fir_base, 1);
+
+ /* Select the TX/RX interface */
+#ifdef SMSC_669 /* Uses pin 88/89 for Rx/Tx */
+ outb(((inb(fir_base+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM),
+ fir_base+IRCC_SCE_CFGB);
+#else
+ outb(((inb(fir_base+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR),
+ fir_base+IRCC_SCE_CFGB);
+#endif
+ (void) inb(fir_base+IRCC_FIFO_THRESHOLD);
+
+ /* Enable SCE interrupts */
+ outb(0, fir_base+IRCC_MASTER);
+ register_bank(fir_base, 0);
+ outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, fir_base+IRCC_IER);
+ outb(IRCC_MASTER_INT_EN, fir_base+IRCC_MASTER);
+}
+
+/*
+ * Function smsc_ircc_fir_stop(self, baud)
+ *
+ * Change the speed of the device
+ *
+ */
+static void smsc_ircc_fir_stop(struct smsc_ircc_cb *self)
+{
+ int fir_base;
+
+ IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+ ASSERT(self != NULL, return;);
+
+ fir_base = self->io.fir_base;
+ register_bank(fir_base, 0);
+ /*outb(IRCC_MASTER_RESET, fir_base+IRCC_MASTER);*/
+ outb(inb(fir_base+IRCC_LCR_B) & IRCC_LCR_B_SIP_ENABLE, fir_base+IRCC_LCR_B);
+}
+
+
+/*
+ * Function smsc_ircc_change_speed(self, baud)
+ *
+ * Change the speed of the device
+ *
+ * This function *must* be called with spinlock held, because it may
+ * be called from the irq handler. - Jean II
+ */
+static void smsc_ircc_change_speed(void *priv, u32 speed)
+{
+ struct smsc_ircc_cb *self = (struct smsc_ircc_cb *) priv;
+ struct net_device *dev;
+ int iobase;
+ int last_speed_was_sir;
+
+ IRDA_DEBUG(0, "%s() changing speed to: %d\n", __FUNCTION__, speed);
+
+ ASSERT(self != NULL, return;);
+ dev = self->netdev;
+ iobase = self->io.fir_base;
+
+ last_speed_was_sir = self->io.speed <= SMSC_IRCC2_MAX_SIR_SPEED;
+
+ #if 0
+ /* Temp Hack */
+ speed= 1152000;
+ self->io.speed = speed;
+ last_speed_was_sir = 0;
+ smsc_ircc_fir_start(self);
+ #endif
+
+ if(self->io.speed == 0)
+ smsc_ircc_sir_start(self);
+
+ #if 0
+ if(!last_speed_was_sir) speed = self->io.speed;
+ #endif
+
+ if(self->io.speed != speed) smsc_ircc_set_transceiver_for_speed(self, speed);
+
+ self->io.speed = speed;
+
+ if(speed <= SMSC_IRCC2_MAX_SIR_SPEED) {
+ if(!last_speed_was_sir) {
+ smsc_ircc_fir_stop(self);
+ smsc_ircc_sir_start(self);
+ }
+ smsc_ircc_set_sir_speed(self, speed);
+ }
+ else {
+ if(last_speed_was_sir) {
+ #if SMSC_IRCC2_C_SIR_STOP
+ smsc_ircc_sir_stop(self);
+ #endif
+ smsc_ircc_fir_start(self);
+ }
+ smsc_ircc_set_fir_speed(self, speed);
+
+ #if 0
+ self->tx_buff.len = 10;
+ self->tx_buff.data = self->tx_buff.head;
+
+ smsc_ircc_dma_xmit(self, iobase, 4000);
+ #endif
+ /* Be ready for incoming frames */
+ smsc_ircc_dma_receive(self, iobase);
+ }
+
+ netif_wake_queue(dev);
+}
+
+/*
+ * Function smsc_ircc_set_sir_speed (self, speed)
+ *
+ * Set speed of IrDA port to specified baudrate
+ *
+ */
+void smsc_ircc_set_sir_speed(void *priv, __u32 speed)
+{
+ struct smsc_ircc_cb *self = (struct smsc_ircc_cb *) priv;
+ int iobase;
+ int fcr; /* FIFO control reg */
+ int lcr; /* Line control reg */
+ int divisor;
+
+ IRDA_DEBUG(0, "%s(), Setting speed to: %d\n", __FUNCTION__, speed);
+
+ ASSERT(self != NULL, return;);
+ iobase = self->io.sir_base;
+
+ /* Update accounting for new speed */
+ self->io.speed = speed;
+
+ /* Turn off interrupts */
+ outb(0, iobase+UART_IER);
+
+ divisor = SMSC_IRCC2_MAX_SIR_SPEED/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);
+
+ IRDA_DEBUG(2, "%s() speed changed to: %d\n", __FUNCTION__, speed);
+}
+
+
+/*
+ * Function smsc_ircc_hard_xmit_fir (skb, dev)
+ *
+ * Transmit the frame!
+ *
+ */
+static int smsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev)
+{
+ struct smsc_ircc_cb *self;
+ unsigned long flags;
+ s32 speed;
+ int iobase;
+ int mtt;
+
+ ASSERT(dev != NULL, return 0;);
+ self = (struct smsc_ircc_cb *) dev->priv;
+ ASSERT(self != NULL, return 0;);
+
+ iobase = self->io.fir_base;
+
+ netif_stop_queue(dev);
+
+ /* Make sure test of self->io.speed & speed change are atomic */
+ spin_lock_irqsave(&self->lock, flags);
+
+ /* Check if we need to change the speed after this frame */
+ speed = irda_get_next_speed(skb);
+ if ((speed != self->io.speed) && (speed != -1)) {
+ /* Check for empty frame */
+ if (!skb->len) {
+ /* Note : you should make sure that speed changes
+ * are not going to corrupt any outgoing frame.
+ * Look at nsc-ircc for the gory details - Jean II */
+ smsc_ircc_change_speed(self, speed);
+ spin_unlock_irqrestore(&self->lock, flags);
+ dev_kfree_skb(skb);
+ return 0;
+ } else
+ self->new_speed = speed;
+ }
+
+ memcpy(self->tx_buff.head, skb->data, skb->len);
+
+ self->tx_buff.len = skb->len;
+ self->tx_buff.data = self->tx_buff.head;
+
+ mtt = irda_get_mtt(skb);
+ if (mtt) {
+ int bofs;
+
+ /*
+ * Compute how many BOFs (STA or PA's) we need to waste the
+ * min turn time given the speed of the link.
+ */
+ bofs = mtt * (self->io.speed / 1000) / 8000;
+ if (bofs > 4095)
+ bofs = 4095;
+
+ smsc_ircc_dma_xmit(self, iobase, bofs);
+ } else {
+ /* Transmit frame */
+ smsc_ircc_dma_xmit(self, iobase, 0);
+ }
+ spin_unlock_irqrestore(&self->lock, flags);
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+/*
+ * Function smsc_ircc_dma_xmit (self, iobase)
+ *
+ * Transmit data using DMA
+ *
+ */
+static void smsc_ircc_dma_xmit(struct smsc_ircc_cb *self, int iobase, int bofs)
+{
+ u8 ctrl;
+
+ IRDA_DEBUG(3, "%s\n", __FUNCTION__);
+#if 1
+ /* Disable Rx */
+ register_bank(iobase, 0);
+ outb(0x00, iobase+IRCC_LCR_B);
+#endif
+ register_bank(iobase, 1);
+ outb(inb(iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE,
+ iobase+IRCC_SCE_CFGB);
+
+ self->io.direction = IO_XMIT;
+
+ /* Set BOF additional count for generating the min turn time */
+ register_bank(iobase, 4);
+ outb(bofs & 0xff, iobase+IRCC_BOF_COUNT_LO);
+ ctrl = inb(iobase+IRCC_CONTROL) & 0xf0;
+ outb(ctrl | ((bofs >> 8) & 0x0f), iobase+IRCC_BOF_COUNT_HI);
+
+ /* Set max Tx frame size */
+ outb(self->tx_buff.len >> 8, iobase+IRCC_TX_SIZE_HI);
+ outb(self->tx_buff.len & 0xff, iobase+IRCC_TX_SIZE_LO);
+
+ /*outb(UART_MCR_OUT2, self->io.sir_base + UART_MCR);*/
+
+ /* Enable burst mode chip Tx DMA */
+ register_bank(iobase, 1);
+ outb(inb(iobase+IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE |
+ IRCC_CFGB_DMA_BURST, iobase+IRCC_SCE_CFGB);
+
+ /* Setup DMA controller (must be done after enabling chip DMA) */
+ setup_dma(self->io.dma, self->tx_buff.data, self->tx_buff.len,
+ DMA_TX_MODE);
+
+ /* Enable interrupt */
+
+ register_bank(iobase, 0);
+ outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase+IRCC_IER);
+ outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER);
+
+ /* Enable transmit */
+ outb(IRCC_LCR_B_SCE_TRANSMIT | IRCC_LCR_B_SIP_ENABLE, iobase+IRCC_LCR_B);
+}
+
+/*
+ * Function smsc_ircc_dma_xmit_complete (self)
+ *
+ * The transfer of a frame in finished. This function will only be called
+ * by the interrupt handler
+ *
+ */
+static void smsc_ircc_dma_xmit_complete(struct smsc_ircc_cb *self, int iobase)
+{
+ IRDA_DEBUG(3, "%s\n", __FUNCTION__);
+#if 0
+ /* Disable Tx */
+ register_bank(iobase, 0);
+ outb(0x00, iobase+IRCC_LCR_B);
+#endif
+ register_bank(self->io.fir_base, 1);
+ outb(inb(self->io.fir_base+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE,
+ self->io.fir_base+IRCC_SCE_CFGB);
+
+ /* Check for underrun! */
+ register_bank(iobase, 0);
+ if (inb(iobase+IRCC_LSR) & IRCC_LSR_UNDERRUN) {
+ self->stats.tx_errors++;
+ self->stats.tx_fifo_errors++;
+
+ /* Reset error condition */
+ register_bank(iobase, 0);
+ outb(IRCC_MASTER_ERROR_RESET, iobase+IRCC_MASTER);
+ outb(0x00, iobase+IRCC_MASTER);
+ } else {
+ self->stats.tx_packets++;
+ self->stats.tx_bytes += self->tx_buff.len;
+ }
+
+ /* Check if it's time to change the speed */
+ if (self->new_speed) {
+ smsc_ircc_change_speed(self, self->new_speed);
+ self->new_speed = 0;
+ }
+
+ netif_wake_queue(self->netdev);
+}
+
+/*
+ * Function smsc_ircc_dma_receive(self)
+ *
+ * Get ready for receiving a frame. The device will initiate a DMA
+ * if it starts to receive a frame.
+ *
+ */
+static int smsc_ircc_dma_receive(struct smsc_ircc_cb *self, int iobase)
+{
+#if 0
+ /* Turn off chip DMA */
+ register_bank(iobase, 1);
+ outb(inb(iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE,
+ iobase+IRCC_SCE_CFGB);
+#endif
+
+ /* Disable Tx */
+ register_bank(iobase, 0);
+ outb(0x00, iobase+IRCC_LCR_B);
+
+ /* Turn off chip DMA */
+ register_bank(iobase, 1);
+ outb(inb(iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE,
+ iobase+IRCC_SCE_CFGB);
+
+ self->io.direction = IO_RECV;
+ self->rx_buff.data = self->rx_buff.head;
+
+ /* Set max Rx frame size */
+ register_bank(iobase, 4);
+ outb((2050 >> 8) & 0x0f, iobase+IRCC_RX_SIZE_HI);
+ outb(2050 & 0xff, iobase+IRCC_RX_SIZE_LO);
+
+ /* Setup DMA controller */
+ setup_dma(self->io.dma, self->rx_buff.data, self->rx_buff.truesize,
+ DMA_RX_MODE);
+
+ /* Enable burst mode chip Rx DMA */
+ register_bank(iobase, 1);
+ outb(inb(iobase+IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE |
+ IRCC_CFGB_DMA_BURST, iobase+IRCC_SCE_CFGB);
+
+ /* Enable interrupt */
+ register_bank(iobase, 0);
+ outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase+IRCC_IER);
+ outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER);
+
+
+ /* Enable receiver */
+ register_bank(iobase, 0);
+ outb(IRCC_LCR_B_SCE_RECEIVE | IRCC_LCR_B_SIP_ENABLE,
+ iobase+IRCC_LCR_B);
+
+ return 0;
+}
+
+/*
+ * Function smsc_ircc_dma_receive_complete(self, iobase)
+ *
+ * Finished with receiving frames
+ *
+ */
+static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self, int iobase)
+{
+ struct sk_buff *skb;
+ int len, msgcnt, lsr;
+
+ register_bank(iobase, 0);
+
+ IRDA_DEBUG(3, "%s\n", __FUNCTION__);
+#if 0
+ /* Disable Rx */
+ register_bank(iobase, 0);
+ outb(0x00, iobase+IRCC_LCR_B);
+#endif
+ register_bank(iobase, 0);
+ outb(inb(iobase+IRCC_LSAR) & ~IRCC_LSAR_ADDRESS_MASK, iobase+IRCC_LSAR);
+ lsr= inb(iobase+IRCC_LSR);
+ msgcnt = inb(iobase+IRCC_LCR_B) & 0x08;
+
+ IRDA_DEBUG(2, "%s: dma count = %d\n", __FUNCTION__,
+ get_dma_residue(self->io.dma));
+
+ len = self->rx_buff.truesize - get_dma_residue(self->io.dma);
+
+ /* Look for errors
+ */
+
+ if(lsr & (IRCC_LSR_FRAME_ERROR | IRCC_LSR_CRC_ERROR | IRCC_LSR_SIZE_ERROR)) {
+ self->stats.rx_errors++;
+ if(lsr & IRCC_LSR_FRAME_ERROR) self->stats.rx_frame_errors++;
+ if(lsr & IRCC_LSR_CRC_ERROR) self->stats.rx_crc_errors++;
+ if(lsr & IRCC_LSR_SIZE_ERROR) self->stats.rx_length_errors++;
+ if(lsr & (IRCC_LSR_UNDERRUN | IRCC_LSR_OVERRUN)) self->stats.rx_length_errors++;
+ return;
+ }
+ /* Remove CRC */
+ if (self->io.speed < 4000000)
+ len -= 2;
+ else
+ len -= 4;
+
+ if ((len < 2) || (len > 2050)) {
+ WARNING("%s(), bogus len=%d\n", __FUNCTION__, len);
+ return;
+ }
+ IRDA_DEBUG(2, "%s: msgcnt = %d, len=%d\n", __FUNCTION__, msgcnt, len);
+
+ skb = dev_alloc_skb(len+1);
+ if (!skb) {
+ WARNING("%s(), memory squeeze, dropping frame.\n",
+ __FUNCTION__);
+ return;
+ }
+ /* Make sure IP header gets aligned */
+ skb_reserve(skb, 1);
+
+ memcpy(skb_put(skb, len), self->rx_buff.data, len);
+ self->stats.rx_packets++;
+ self->stats.rx_bytes += len;
+
+ skb->dev = self->netdev;
+ skb->mac.raw = skb->data;
+ skb->protocol = htons(ETH_P_IRDA);
+ netif_rx(skb);
+}
+
+/*
+ * Function smsc_ircc_sir_receive (self)
+ *
+ * Receive one frame from the infrared port
+ *
+ */
+static void smsc_ircc_sir_receive(struct smsc_ircc_cb *self)
+{
+ int boguscount = 0;
+ int iobase;
+
+ ASSERT(self != NULL, return;);
+
+ iobase = self->io.sir_base;
+
+ /*
+ * Receive all characters in Rx FIFO, unwrap and unstuff them.
+ * async_unwrap_char will deliver all found frames
+ */
+ do {
+ async_unwrap_char(self->netdev, &self->stats, &self->rx_buff,
+ inb(iobase+UART_RX));
+
+ /* Make sure we don't stay here to long */
+ if (boguscount++ > 32) {
+ IRDA_DEBUG(2, "%s(), breaking!\n", __FUNCTION__);
+ break;
+ }
+ } while (inb(iobase+UART_LSR) & UART_LSR_DR);
+}
+
+
+/*
+ * Function smsc_ircc_interrupt (irq, dev_id, regs)
+ *
+ * An interrupt from the chip has arrived. Time to do some work
+ *
+ */
+static irqreturn_t smsc_ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct smsc_ircc_cb *self;
+ int iobase, iir, lcra, lsr;
+
+ if (dev == NULL) {
+ printk(KERN_WARNING "%s: irq %d for unknown device.\n",
+ driver_name, irq);
+ return IRQ_NONE;
+ }
+ self = (struct smsc_ircc_cb *) dev->priv;
+ ASSERT(self != NULL, return IRQ_NONE;);
+
+ /* Serialise the interrupt handler in various CPUs, stop Tx path */
+ spin_lock(&self->lock);
+
+ /* Check if we should use the SIR interrupt handler */
+ if (self->io.speed <= SMSC_IRCC2_MAX_SIR_SPEED) {
+ smsc_ircc_interrupt_sir(irq, dev_id, regs);
+ spin_unlock(&self->lock);
+ return IRQ_HANDLED;
+ }
+ iobase = self->io.fir_base;
+
+ register_bank(iobase, 0);
+ iir = inb(iobase+IRCC_IIR);
+ /* Disable interrupts */
+ outb(0, iobase+IRCC_IER);
+ lcra = inb(iobase+IRCC_LCR_A);
+ lsr = inb(iobase+IRCC_LSR);
+
+ IRDA_DEBUG(2, "%s(), iir = 0x%02x\n", __FUNCTION__, iir);
+
+ if (iir & IRCC_IIR_EOM) {
+ if (self->io.direction == IO_RECV)
+ smsc_ircc_dma_receive_complete(self, iobase);
+ else
+ smsc_ircc_dma_xmit_complete(self, iobase);
+
+ smsc_ircc_dma_receive(self, iobase);
+ }
+
+ if (iir & IRCC_IIR_ACTIVE_FRAME) {
+ /*printk(KERN_WARNING __FUNCTION__ "(): Active Frame\n");*/
+ }
+
+ /* Enable interrupts again */
+
+ register_bank(iobase, 0);
+ outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, iobase+IRCC_IER);
+
+ spin_unlock(&self->lock);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Function irport_interrupt_sir (irq, dev_id, regs)
+ *
+ * Interrupt handler for SIR modes
+ */
+void smsc_ircc_interrupt_sir(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct smsc_ircc_cb *self;
+ int boguscount = 0;
+ int iobase;
+ int iir, lsr;
+
+ if (!dev) {
+ WARNING("%s() irq %d for unknown device.\n",
+ __FUNCTION__, irq);
+ return;
+ }
+ self = (struct smsc_ircc_cb *) dev->priv;
+
+ /* Already locked comming here in smsc_ircc_interrupt() */
+ /*spin_lock(&self->lock);*/
+
+ iobase = self->io.sir_base;
+
+ iir = inb(iobase+UART_IIR) & UART_IIR_ID;
+ while (iir) {
+ /* Clear interrupt */
+ lsr = inb(iobase+UART_LSR);
+
+ IRDA_DEBUG(4, "%s(), iir=%02x, lsr=%02x, iobase=%#x\n",
+ __FUNCTION__, iir, lsr, iobase);
+
+ switch (iir) {
+ case UART_IIR_RLSI:
+ IRDA_DEBUG(2, "%s(), RLSI\n", __FUNCTION__);
+ break;
+ case UART_IIR_RDI:
+ /* Receive interrupt */
+ smsc_ircc_sir_receive(self);
+ break;
+ case UART_IIR_THRI:
+ if (lsr & UART_LSR_THRE)
+ /* Transmitter ready for data */
+ smsc_ircc_sir_write_wakeup(self);
+ break;
+ default:
+ IRDA_DEBUG(0, "%s(), unhandled IIR=%#x\n",
+ __FUNCTION__, iir);
+ break;
+ }
+
+ /* Make sure we don't stay here to long */
+ if (boguscount++ > 100)
+ break;
+
+ iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+ }
+ /*spin_unlock(&self->lock);*/
+}
+
+
+#if 0 /* unused */
+/*
+ * Function ircc_is_receiving (self)
+ *
+ * Return TRUE is we are currently receiving a frame
+ *
+ */
+static int ircc_is_receiving(struct smsc_ircc_cb *self)
+{
+ int status = FALSE;
+ /* int iobase; */
+
+ IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+ ASSERT(self != NULL, return FALSE;);
+
+ IRDA_DEBUG(0, "%s: dma count = %d\n", __FUNCTION__,
+ get_dma_residue(self->io.dma));
+
+ status = (self->rx_buff.state != OUTSIDE_FRAME);
+
+ return status;
+}
+#endif /* unused */
+
+static int smsc_ircc_net_init(struct net_device *dev)
+{
+ /* Keep track of module usage */
+ SET_MODULE_OWNER(dev);
+
+ /* Set up to be a normal IrDA network device driver */
+ irda_device_setup(dev);
+
+ /* Insert overrides below this line! */
+
+ return 0;
+}
+
+/*
+ * Function smsc_ircc_net_open (dev)
+ *
+ * Start the device
+ *
+ */
+static int smsc_ircc_net_open(struct net_device *dev)
+{
+ struct smsc_ircc_cb *self;
+ int iobase;
+ char hwname[16];
+ unsigned long flags;
+
+ IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+ ASSERT(dev != NULL, return -1;);
+ self = (struct smsc_ircc_cb *) dev->priv;
+ ASSERT(self != NULL, return 0;);
+
+ iobase = self->io.fir_base;
+
+ if (request_irq(self->io.irq, smsc_ircc_interrupt, 0, dev->name,
+ (void *) dev)) {
+ IRDA_DEBUG(0, "%s(), unable to allocate irq=%d\n",
+ __FUNCTION__, self->io.irq);
+ return -EAGAIN;
+ }
+
+ spin_lock_irqsave(&self->lock, flags);
+ /*smsc_ircc_sir_start(self);*/
+ self->io.speed = 0;
+ smsc_ircc_change_speed(self, SMSC_IRCC2_C_IRDA_FALLBACK_SPEED);
+ spin_unlock_irqrestore(&self->lock, flags);
+
+ /* Give self a hardware name */
+ /* It would be cool to offer the chip revision here - Jean II */
+ sprintf(hwname, "SMSC @ 0x%03x", self->io.fir_base);
+
+ /*
+ * Open new IrLAP layer instance, now that everything should be
+ * initialized properly
+ */
+ self->irlap = irlap_open(dev, &self->qos, hwname);
+
+ /*
+ * Always allocate the DMA channel after the IRQ,
+ * and clean up on failure.
+ */
+ if (request_dma(self->io.dma, dev->name)) {
+ smsc_ircc_net_close(dev);
+
+ WARNING("%s(), unable to allocate DMA=%d\n",
+ __FUNCTION__, self->io.dma);
+ return -EAGAIN;
+ }
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/*
+ * Function smsc_ircc_net_close (dev)
+ *
+ * Stop the device
+ *
+ */
+static int smsc_ircc_net_close(struct net_device *dev)
+{
+ struct smsc_ircc_cb *self;
+ int iobase;
+
+ IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+ ASSERT(dev != NULL, return -1;);
+ self = (struct smsc_ircc_cb *) dev->priv;
+ ASSERT(self != NULL, return 0;);
+
+ iobase = self->io.fir_base;
+
+ /* Stop device */
+ netif_stop_queue(dev);
+
+ /* Stop and remove instance of IrLAP */
+ if (self->irlap)
+ irlap_close(self->irlap);
+ self->irlap = NULL;
+
+ free_irq(self->io.irq, dev);
+
+ disable_dma(self->io.dma);
+
+ free_dma(self->io.dma);
+
+ return 0;
+}
+
+
+static void smsc_ircc_suspend(struct smsc_ircc_cb *self)
+{
+ MESSAGE("%s, Suspending\n", driver_name);
+
+ if (self->io.suspended)
+ return;
+
+ smsc_ircc_net_close(self->netdev);
+
+ self->io.suspended = 1;
+}
+
+static void smsc_ircc_wakeup(struct smsc_ircc_cb *self)
+{
+ if (!self->io.suspended)
+ return;
+
+ /* The code was doing a "cli()" here, but this can't be right.
+ * If you need protection, do it in net_open with a spinlock
+ * or give a good reason. - Jean II */
+
+ smsc_ircc_net_open(self->netdev);
+
+ MESSAGE("%s, Waking up\n", driver_name);
+}
+
+static int smsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+ struct smsc_ircc_cb *self = (struct smsc_ircc_cb*) dev->data;
+ if (self) {
+ switch (rqst) {
+ case PM_SUSPEND:
+ smsc_ircc_suspend(self);
+ break;
+ case PM_RESUME:
+ smsc_ircc_wakeup(self);
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Function smsc_ircc_close (self)
+ *
+ * Close driver instance
+ *
+ */
+static int __exit smsc_ircc_close(struct smsc_ircc_cb *self)
+{
+ int iobase;
+ unsigned long flags;
+
+ IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+ ASSERT(self != NULL, return -1;);
+
+ iobase = self->io.fir_base;
+
+ if (self->pmdev)
+ pm_unregister(self->pmdev);
+
+ /* Remove netdevice */
+ if (self->netdev) {
+ rtnl_lock();
+ unregister_netdevice(self->netdev);
+ rtnl_unlock();
+ }
+
+ /* Make sure the irq handler is not exectuting */
+ spin_lock_irqsave(&self->lock, flags);
+
+ /* Stop interrupts */
+ register_bank(iobase, 0);
+ outb(0, iobase+IRCC_IER);
+ outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER);
+ outb(0x00, iobase+IRCC_MASTER);
+#if 0
+ /* Reset to SIR mode */
+ register_bank(iobase, 1);
+ outb(IRCC_CFGA_IRDA_SIR_A|IRCC_CFGA_TX_POLARITY, iobase+IRCC_SCE_CFGA);
+ outb(IRCC_CFGB_IR, iobase+IRCC_SCE_CFGB);
+#endif
+ spin_unlock_irqrestore(&self->lock, flags);
+
+ /* Release the PORTS that this driver is using */
+ IRDA_DEBUG(0, "%s(), releasing 0x%03x\n", __FUNCTION__,
+ self->io.fir_base);
+
+ release_region(self->io.fir_base, self->io.fir_ext);
+
+ IRDA_DEBUG(0, "%s(), releasing 0x%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.head)
+ kfree(self->rx_buff.head);
+
+ kfree(self);
+
+ return 0;
+}
+
+void __exit smsc_ircc_cleanup(void)
+{
+ int i;
+
+ IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+ for (i=0; i < 2; i++) {
+ if (dev_self[i])
+ smsc_ircc_close(dev_self[i]);
+ }
+}
+
+/*
+ * Start SIR operations
+ *
+ * This function *must* be called with spinlock held, because it may
+ * be called from the irq handler (via smsc_ircc_change_speed()). - Jean II
+ */
+void smsc_ircc_sir_start(struct smsc_ircc_cb *self)
+{
+ struct net_device *dev;
+ int fir_base, sir_base;
+
+ IRDA_DEBUG(3, "%s\n", __FUNCTION__);
+
+ ASSERT(self != NULL, return;);
+ dev= self->netdev;
+ ASSERT(dev != NULL, return;);
+ dev->hard_start_xmit = &smsc_ircc_hard_xmit_sir;
+
+ fir_base = self->io.fir_base;
+ sir_base = self->io.sir_base;
+
+ /* Reset everything */
+ outb(IRCC_MASTER_RESET, fir_base+IRCC_MASTER);
+
+ #if SMSC_IRCC2_C_SIR_STOP
+ /*smsc_ircc_sir_stop(self);*/
+ #endif
+
+ register_bank(fir_base, 1);
+ outb(((inb(fir_base+IRCC_SCE_CFGA) & IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK) | IRCC_CFGA_IRDA_SIR_A), fir_base+IRCC_SCE_CFGA);
+
+ /* Initialize UART */
+ outb(UART_LCR_WLEN8, sir_base+UART_LCR); /* Reset DLAB */
+ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), sir_base+UART_MCR);
+
+ /* Turn on interrups */
+ outb(UART_IER_RLSI | UART_IER_RDI |UART_IER_THRI, sir_base+UART_IER);
+
+ IRDA_DEBUG(3, "%s() - exit\n", __FUNCTION__);
+
+ outb(0x00, fir_base+IRCC_MASTER);
+}
+
+#if SMSC_IRCC2_C_SIR_STOP
+void smsc_ircc_sir_stop(struct smsc_ircc_cb *self)
+{
+ int iobase;
+
+ IRDA_DEBUG(3, "%s\n", __FUNCTION__);
+ iobase = self->io.sir_base;
+
+ /* Reset UART */
+ outb(0, iobase+UART_MCR);
+
+ /* Turn off interrupts */
+ outb(0, iobase+UART_IER);
+}
+#endif
+
+/*
+ * Function smsc_sir_write_wakeup (self)
+ *
+ * Called by the SIR interrupt handler when there's room for more data.
+ * If we have more packets to send, we send them here.
+ *
+ */
+static void smsc_ircc_sir_write_wakeup(struct smsc_ircc_cb *self)
+{
+ int actual = 0;
+ int iobase;
+ int fcr;
+
+ ASSERT(self != NULL, return;);
+
+ IRDA_DEBUG(4, "%s\n", __FUNCTION__);
+
+ iobase = self->io.sir_base;
+
+ /* Finished with frame? */
+ if (self->tx_buff.len > 0) {
+ /* Write data left in transmit buffer */
+ actual = smsc_ircc_sir_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 {
+
+ /*if (self->tx_buff.len ==0) {*/
+
+ /*
+ * 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, "%s(), Changing speed to %d.\n",
+ __FUNCTION__, self->new_speed);
+ smsc_ircc_sir_wait_hw_transmitter_finish(self);
+ smsc_ircc_change_speed(self, 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++;
+
+ if(self->io.speed <= 115200) {
+ /*
+ * Reset Rx FIFO to make sure that all reflected transmit data
+ * is discarded. This is needed for half duplex operation
+ */
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR;
+ if (self->io.speed < 38400)
+ fcr |= UART_FCR_TRIGGER_1;
+ else
+ fcr |= UART_FCR_TRIGGER_14;
+
+ outb(fcr, iobase+UART_FCR);
+
+ /* Turn on receive interrupts */
+ outb(UART_IER_RDI, iobase+UART_IER);
+ }
+ }
+}
+
+/*
+ * Function smsc_ircc_sir_write (iobase, fifo_size, buf, len)
+ *
+ * Fill Tx FIFO with transmit data
+ *
+ */
+static int smsc_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len)
+{
+ int actual = 0;
+
+ /* Tx FIFO should be empty! */
+ if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) {
+ WARNING("%s(), failed, fifo not empty!\n", __FUNCTION__);
+ return 0;
+ }
+
+ /* Fill FIFO with current frame */
+ while ((fifo_size-- > 0) && (actual < len)) {
+ /* Transmit next byte */
+ outb(buf[actual], iobase+UART_TX);
+ actual++;
+ }
+ return actual;
+}
+
+/*
+ * Function smsc_ircc_is_receiving (self)
+ *
+ * Returns true is we are currently receiving data
+ *
+ */
+static int smsc_ircc_is_receiving(struct smsc_ircc_cb *self)
+{
+ return (self->rx_buff.state != OUTSIDE_FRAME);
+}
+
+
+/*
+ * Function smsc_ircc_probe_transceiver(self)
+ *
+ * Tries to find the used Transceiver
+ *
+ */
+static void smsc_ircc_probe_transceiver(struct smsc_ircc_cb *self)
+{
+ unsigned int i;
+
+ ASSERT(self != NULL, return;);
+
+ for(i=0; smsc_transceivers[i].name!=NULL; i++)
+ if((*smsc_transceivers[i].probe)(self->io.fir_base)) {
+ MESSAGE(" %s transceiver found\n", smsc_transceivers[i].name);
+ self->transceiver= i+1;
+ return;
+ }
+ MESSAGE("No transceiver found. Defaulting to %s\n", smsc_transceivers[SMSC_IRCC2_C_DEFAULT_TRANSCEIVER].name);
+
+ self->transceiver= SMSC_IRCC2_C_DEFAULT_TRANSCEIVER;
+}
+
+
+/*
+ * Function smsc_ircc_set_transceiver_for_speed(self, speed)
+ *
+ * Set the transceiver according to the speed
+ *
+ */
+static void smsc_ircc_set_transceiver_for_speed(struct smsc_ircc_cb *self, u32 speed)
+{
+ unsigned int trx;
+
+ trx = self->transceiver;
+ if(trx>0) (*smsc_transceivers[trx-1].set_for_speed)(self->io.fir_base, speed);
+}
+
+/*
+ * Function smsc_ircc_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 smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self)
+{
+ int iobase;
+ int count = SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US;
+
+ iobase = self->io.sir_base;
+
+ /* Calibrated busy loop */
+ while((count-- > 0) && !(inb(iobase+UART_LSR) & UART_LSR_TEMT))
+ udelay(1);
+
+ if(count == 0)
+ IRDA_DEBUG(0, "%s(): stuck transmitter\n", __FUNCTION__);
+}
+
+
+/* PROBING
+ *
+ *
+ */
+
+static int __init smsc_ircc_look_for_chips(void)
+{
+ smsc_chip_address_t *address;
+ char *type;
+ unsigned int cfg_base, found;
+
+ found = 0;
+ address = possible_addresses;
+
+ while(address->cfg_base){
+ cfg_base = address->cfg_base;
+
+ /*printk(KERN_WARNING __FUNCTION__ "(): probing: 0x%02x for: 0x%02x\n", cfg_base, address->type);*/
+
+ if( address->type & SMSCSIO_TYPE_FDC){
+ type = "FDC";
+ if((address->type) & SMSCSIO_TYPE_FLAT) {
+ if(!smsc_superio_flat(fdc_chips_flat,cfg_base, type)) found++;
+ }
+ if((address->type) & SMSCSIO_TYPE_PAGED) {
+ if(!smsc_superio_paged(fdc_chips_paged,cfg_base, type)) found++;
+ }
+ }
+ if( address->type & SMSCSIO_TYPE_LPC){
+ type = "LPC";
+ if((address->type) & SMSCSIO_TYPE_FLAT) {
+ if(!smsc_superio_flat(lpc_chips_flat,cfg_base,type)) found++;
+ }
+ if((address->type) & SMSCSIO_TYPE_PAGED) {
+ if(!smsc_superio_paged(lpc_chips_paged,cfg_base,"LPC")) found++;
+ }
+ }
+ address++;
+ }
+ return found;
+}
+
+/*
+ * Function smsc_superio_flat (chip, base, type)
+ *
+ * Try to get configuration of a smc SuperIO chip with flat register model
+ *
+ */
+static int __init smsc_superio_flat(const smsc_chip_t *chips, unsigned short cfgbase, char *type)
+{
+ unsigned short firbase, sirbase;
+ u8 mode, dma, irq;
+ int ret = -ENODEV;
+
+ IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+ if (smsc_ircc_probe(cfgbase, SMSCSIOFLAT_DEVICEID_REG, chips, type)==NULL)
+ return ret;
+
+ outb(SMSCSIOFLAT_UARTMODE0C_REG, cfgbase);
+ mode = inb(cfgbase+1);
+
+ /*printk(KERN_WARNING __FUNCTION__ "(): mode: 0x%02x\n", mode);*/
+
+ if(!(mode & SMSCSIOFLAT_UART2MODE_VAL_IRDA))
+ WARNING("%s(): IrDA not enabled\n", __FUNCTION__);
+
+ outb(SMSCSIOFLAT_UART2BASEADDR_REG, cfgbase);
+ sirbase = inb(cfgbase+1) << 2;
+
+ /* FIR iobase */
+ outb(SMSCSIOFLAT_FIRBASEADDR_REG, cfgbase);
+ firbase = inb(cfgbase+1) << 3;
+
+ /* DMA */
+ outb(SMSCSIOFLAT_FIRDMASELECT_REG, cfgbase);
+ dma = inb(cfgbase+1) & SMSCSIOFLAT_FIRDMASELECT_MASK;
+
+ /* IRQ */
+ outb(SMSCSIOFLAT_UARTIRQSELECT_REG, cfgbase);
+ irq = inb(cfgbase+1) & SMSCSIOFLAT_UART2IRQSELECT_MASK;
+
+ MESSAGE("%s(): fir: 0x%02x, sir: 0x%02x, dma: %02d, irq: %d, mode: 0x%02x\n", __FUNCTION__, firbase, sirbase, dma, irq, mode);
+
+ if (firbase) {
+ if (smsc_ircc_open(firbase, sirbase, dma, irq) == 0)
+ ret=0;
+ }
+
+ /* Exit configuration */
+ outb(SMSCSIO_CFGEXITKEY, cfgbase);
+
+ return ret;
+}
+
+/*
+ * Function smsc_superio_paged (chip, base, type)
+ *
+ * Try to get configuration of a smc SuperIO chip with paged register model
+ *
+ */
+static int __init smsc_superio_paged(const smsc_chip_t *chips, unsigned short cfg_base, char *type)
+{
+ unsigned short fir_io, sir_io;
+ int ret = -ENODEV;
+
+ IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+ if (smsc_ircc_probe(cfg_base,0x20,chips,type)==NULL)
+ return ret;
+
+ /* Select logical device (UART2) */
+ outb(0x07, cfg_base);
+ outb(0x05, cfg_base + 1);
+
+ /* SIR iobase */
+ outb(0x60, cfg_base);
+ sir_io = inb(cfg_base + 1) << 8;
+ outb(0x61, cfg_base);
+ sir_io |= inb(cfg_base + 1);
+
+ /* Read FIR base */
+ outb(0x62, cfg_base);
+ fir_io = inb(cfg_base + 1) << 8;
+ outb(0x63, cfg_base);
+ fir_io |= inb(cfg_base + 1);
+ outb(0x2b, cfg_base); /* ??? */
+
+ if (fir_io) {
+ if (smsc_ircc_open(fir_io, sir_io, ircc_dma, ircc_irq) == 0)
+ ret=0;
+ }
+
+ /* Exit configuration */
+ outb(SMSCSIO_CFGEXITKEY, cfg_base);
+
+ return ret;
+}
+
+
+static int __init smsc_access(unsigned short cfg_base,unsigned char reg)
+{
+ IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+ outb(reg, cfg_base);
+
+ if (inb(cfg_base)!=reg)
+ return -1;
+
+ return 0;
+}
+
+static const smsc_chip_t * __init smsc_ircc_probe(unsigned short cfg_base,u8 reg,const smsc_chip_t *chip,char *type)
+{
+ u8 devid,xdevid,rev;
+
+ IRDA_DEBUG(1, "%s\n", __FUNCTION__);
+
+ /* Leave configuration */
+
+ outb(SMSCSIO_CFGEXITKEY, cfg_base);
+
+ if (inb(cfg_base) == SMSCSIO_CFGEXITKEY) /* not a smc superio chip */
+ return NULL;
+
+ outb(reg, cfg_base);
+
+ xdevid=inb(cfg_base+1);
+
+ /* Enter configuration */
+
+ outb(SMSCSIO_CFGACCESSKEY, cfg_base);
+
+ #if 0
+ if (smsc_access(cfg_base,0x55)) /* send second key and check */
+ return NULL;
+ #endif
+
+ /* probe device ID */
+
+ if (smsc_access(cfg_base,reg))
+ return NULL;
+
+ devid=inb(cfg_base+1);
+
+ if (devid==0) /* typical value for unused port */
+ return NULL;
+
+ if (devid==0xff) /* typical value for unused port */
+ return NULL;
+
+ /* probe revision ID */
+
+ if (smsc_access(cfg_base,reg+1))
+ return NULL;
+
+ rev=inb(cfg_base+1);
+
+ if (rev>=128) /* i think this will make no sense */
+ return NULL;
+
+ if (devid==xdevid) /* protection against false positives */
+ return NULL;
+
+ /* Check for expected device ID; are there others? */
+
+ while(chip->devid!=devid) {
+
+ chip++;
+
+ if (chip->name==NULL)
+ return NULL;
+ }
+
+ MESSAGE("found SMC SuperIO Chip (devid=0x%02x rev=%02X base=0x%04x): %s%s\n",devid,rev,cfg_base,type,chip->name);
+
+ if (chip->rev>rev){
+ MESSAGE("Revision higher than expected\n");
+ return NULL;
+ }
+
+ if (chip->flags&NoIRDA)
+ MESSAGE("chipset does not support IRDA\n");
+
+ return chip;
+}
+
+static int __init smsc_superio_fdc(unsigned short cfg_base)
+{
+ if (check_region(cfg_base, 2) < 0) {
+ WARNING("%s: can't get cfg_base of 0x%03x\n",
+ __FUNCTION__, cfg_base);
+ return -1;
+ }
+
+ if (!smsc_superio_flat(fdc_chips_flat,cfg_base,"FDC")||!smsc_superio_paged(fdc_chips_paged,cfg_base,"FDC"))
+ return 0;
+
+ return -1;
+}
+
+static int __init smsc_superio_lpc(unsigned short cfg_base)
+{
+#if 0
+ if (check_region(cfg_base, 2) < 0) {
+ IRDA_DEBUG(0, __FUNCTION__ ": can't get cfg_base of 0x%03x\n",
+ cfg_base);
+ return -1;
+ }
+#endif
+
+ if (!smsc_superio_flat(lpc_chips_flat,cfg_base,"LPC")||!smsc_superio_paged(lpc_chips_paged,cfg_base,"LPC"))
+ return 0;
+
+ return -1;
+}
+
+/************************************************
+ *
+ * Transceivers specific functions
+ *
+ ************************************************/
+
+
+/*
+ * Function smsc_ircc_set_transceiver_smsc_ircc_atc(fir_base, speed)
+ *
+ * Program transceiver through smsc-ircc ATC circuitry
+ *
+ */
+
+static void smsc_ircc_set_transceiver_smsc_ircc_atc(int fir_base, u32 speed)
+{
+ unsigned long jiffies_now, jiffies_timeout;
+ u8 val;
+
+ jiffies_now= jiffies;
+ jiffies_timeout= jiffies+SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES;
+
+ /* ATC */
+ register_bank(fir_base, 4);
+ outb((inb(fir_base+IRCC_ATC) & IRCC_ATC_MASK) |IRCC_ATC_nPROGREADY|IRCC_ATC_ENABLE, fir_base+IRCC_ATC);
+ while((val=(inb(fir_base+IRCC_ATC) & IRCC_ATC_nPROGREADY)) && !time_after(jiffies, jiffies_timeout));
+ if(val)
+ WARNING("%s(): ATC: 0x%02x\n", __FUNCTION__,
+ inb(fir_base+IRCC_ATC));
+}
+
+/*
+ * Function smsc_ircc_probe_transceiver_smsc_ircc_atc(fir_base)
+ *
+ * Probe transceiver smsc-ircc ATC circuitry
+ *
+ */
+
+static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base)
+{
+ return 0;
+}
+
+/*
+ * Function smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(self, speed)
+ *
+ * Set transceiver
+ *
+ */
+
+static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed)
+{
+ u8 fast_mode;
+
+ switch(speed)
+ {
+ default:
+ case 576000 :
+ fast_mode = 0;
+ break;
+ case 1152000 :
+ case 4000000 :
+ fast_mode = IRCC_LCR_A_FAST;
+ break;
+
+ }
+ register_bank(fir_base, 0);
+ outb((inb(fir_base+IRCC_LCR_A) & 0xbf) | fast_mode, fir_base+IRCC_LCR_A);
+}
+
+/*
+ * Function smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(fir_base)
+ *
+ * Probe transceiver
+ *
+ */
+
+static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base)
+{
+ return 0;
+}
+
+/*
+ * Function smsc_ircc_set_transceiver_toshiba_sat1800(fir_base, speed)
+ *
+ * Set transceiver
+ *
+ */
+
+static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed)
+{
+ u8 fast_mode;
+
+ switch(speed)
+ {
+ default:
+ case 576000 :
+ fast_mode = 0;
+ break;
+ case 1152000 :
+ case 4000000 :
+ fast_mode = /*IRCC_LCR_A_FAST |*/ IRCC_LCR_A_GP_DATA;
+ break;
+
+ }
+ /* This causes an interrupt */
+ register_bank(fir_base, 0);
+ outb((inb(fir_base+IRCC_LCR_A) & 0xbf) | fast_mode, fir_base+IRCC_LCR_A);
+}
+
+/*
+ * Function smsc_ircc_probe_transceiver_toshiba_sat1800(fir_base)
+ *
+ * Probe transceiver
+ *
+ */
+
+static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base)
+{
+ return 0;
+}
+
+
+module_init(smsc_ircc_init);
+module_exit(smsc_ircc_cleanup);
+
+MODULE_AUTHOR("Daniele Peri <peri@csai.unipa.it>");
+MODULE_DESCRIPTION("SMC IrCC SIR/FIR controller driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(ircc_dma, "1i");
+MODULE_PARM_DESC(ircc_dma, "DMA channel");
+MODULE_PARM(ircc_irq, "1i");
+MODULE_PARM_DESC(ircc_irq, "IRQ line");
+MODULE_PARM(ircc_fir, "1-4i");
+MODULE_PARM_DESC(ircc_fir, "FIR Base Address");
+MODULE_PARM(ircc_sir, "1-4i");
+MODULE_PARM_DESC(ircc_sir, "SIR Base Address");
+MODULE_PARM(ircc_cfg, "1-4i");
+MODULE_PARM_DESC(ircc_cfg, "Configuration register base address");
+MODULE_PARM(ircc_transceiver, "1i");
+MODULE_PARM_DESC(ircc_transceiver, "Transceiver type");
diff --git a/drivers/net/irda/smsc-ircc2.h b/drivers/net/irda/smsc-ircc2.h
new file mode 100644
index 000000000000..458611cc0d40
--- /dev/null
+++ b/drivers/net/irda/smsc-ircc2.h
@@ -0,0 +1,194 @@
+/*********************************************************************
+ * $Id: smsc-ircc2.h,v 1.12.2.1 2002/10/27 10:52:37 dip Exp $
+ *
+ * Description: Definitions for the SMC IrCC chipset
+ * Status: Experimental.
+ * Author: Daniele Peri (peri@csai.unipa.it)
+ *
+ * Copyright (c) 2002 Daniele Peri
+ * All Rights Reserved.
+ *
+ * Based on smc-ircc.h:
+ *
+ * Copyright (c) 1999-2000, Dag Brattli <dagb@cs.uit.no>
+ * Copyright (c) 1998-1999, Thomas Davis (tadavis@jps.net>
+ * 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
+ *
+ ********************************************************************/
+
+#ifndef SMSC_IRCC2_H
+#define SMSC_IRCC2_H
+
+/* DMA modes needed */
+#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */
+#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */
+
+/* Master Control Register */
+#define IRCC_MASTER 0x07
+#define IRCC_MASTER_POWERDOWN 0x80
+#define IRCC_MASTER_RESET 0x40
+#define IRCC_MASTER_INT_EN 0x20
+#define IRCC_MASTER_ERROR_RESET 0x10
+
+/* Register block 0 */
+
+/* Interrupt Identification */
+#define IRCC_IIR 0x01
+#define IRCC_IIR_ACTIVE_FRAME 0x80
+#define IRCC_IIR_EOM 0x40
+#define IRCC_IIR_RAW_MODE 0x20
+#define IRCC_IIR_FIFO 0x10
+
+/* Interrupt Enable */
+#define IRCC_IER 0x02
+#define IRCC_IER_ACTIVE_FRAME 0x80
+#define IRCC_IER_EOM 0x40
+#define IRCC_IER_RAW_MODE 0x20
+#define IRCC_IER_FIFO 0x10
+
+/* Line Status Register */
+#define IRCC_LSR 0x03
+#define IRCC_LSR_UNDERRUN 0x80
+#define IRCC_LSR_OVERRUN 0x40
+#define IRCC_LSR_FRAME_ERROR 0x20
+#define IRCC_LSR_SIZE_ERROR 0x10
+#define IRCC_LSR_CRC_ERROR 0x80
+#define IRCC_LSR_FRAME_ABORT 0x40
+
+/* Line Status Address Register */
+#define IRCC_LSAR 0x03
+#define IRCC_LSAR_ADDRESS_MASK 0x07
+
+/* Line Control Register A */
+#define IRCC_LCR_A 0x04
+#define IRCC_LCR_A_FIFO_RESET 0x80
+#define IRCC_LCR_A_FAST 0x40
+#define IRCC_LCR_A_GP_DATA 0x20
+#define IRCC_LCR_A_RAW_TX 0x10
+#define IRCC_LCR_A_RAW_RX 0x08
+#define IRCC_LCR_A_ABORT 0x04
+#define IRCC_LCR_A_DATA_DONE 0x02
+
+/* Line Control Register B */
+#define IRCC_LCR_B 0x05
+#define IRCC_LCR_B_SCE_DISABLED 0x00
+#define IRCC_LCR_B_SCE_TRANSMIT 0x40
+#define IRCC_LCR_B_SCE_RECEIVE 0x80
+#define IRCC_LCR_B_SCE_UNDEFINED 0xc0
+#define IRCC_LCR_B_SIP_ENABLE 0x20
+#define IRCC_LCR_B_BRICK_WALL 0x10
+
+/* Bus Status Register */
+#define IRCC_BSR 0x06
+#define IRCC_BSR_NOT_EMPTY 0x80
+#define IRCC_BSR_FIFO_FULL 0x40
+#define IRCC_BSR_TIMEOUT 0x20
+
+/* Register block 1 */
+
+#define IRCC_FIFO_THRESHOLD 0x02
+
+#define IRCC_SCE_CFGA 0x00
+#define IRCC_CFGA_AUX_IR 0x80
+#define IRCC_CFGA_HALF_DUPLEX 0x04
+#define IRCC_CFGA_TX_POLARITY 0x02
+#define IRCC_CFGA_RX_POLARITY 0x01
+
+#define IRCC_CFGA_COM 0x00
+#define IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK 0x87
+#define IRCC_CFGA_IRDA_SIR_A 0x08
+#define IRCC_CFGA_ASK_SIR 0x10
+#define IRCC_CFGA_IRDA_SIR_B 0x18
+#define IRCC_CFGA_IRDA_HDLC 0x20
+#define IRCC_CFGA_IRDA_4PPM 0x28
+#define IRCC_CFGA_CONSUMER 0x30
+#define IRCC_CFGA_RAW_IR 0x38
+#define IRCC_CFGA_OTHER 0x40
+
+#define IRCC_IR_HDLC 0x04
+#define IRCC_IR_4PPM 0x01
+#define IRCC_IR_CONSUMER 0x02
+
+#define IRCC_SCE_CFGB 0x01
+#define IRCC_CFGB_LOOPBACK 0x20
+#define IRCC_CFGB_LPBCK_TX_CRC 0x10
+#define IRCC_CFGB_NOWAIT 0x08
+#define IRCC_CFGB_STRING_MOVE 0x04
+#define IRCC_CFGB_DMA_BURST 0x02
+#define IRCC_CFGB_DMA_ENABLE 0x01
+
+#define IRCC_CFGB_MUX_COM 0x00
+#define IRCC_CFGB_MUX_IR 0x40
+#define IRCC_CFGB_MUX_AUX 0x80
+#define IRCC_CFGB_MUX_INACTIVE 0xc0
+
+/* Register block 3 - Identification Registers! */
+#define IRCC_ID_HIGH 0x00 /* 0x10 */
+#define IRCC_ID_LOW 0x01 /* 0xB8 */
+#define IRCC_CHIP_ID 0x02 /* 0xF1 */
+#define IRCC_VERSION 0x03 /* 0x01 */
+#define IRCC_INTERFACE 0x04 /* low 4 = DMA, high 4 = IRQ */
+#define IRCC_INTERFACE_DMA_MASK 0x0F /* low 4 = DMA, high 4 = IRQ */
+#define IRCC_INTERFACE_IRQ_MASK 0xF0 /* low 4 = DMA, high 4 = IRQ */
+
+/* Register block 4 - IrDA */
+#define IRCC_CONTROL 0x00
+#define IRCC_BOF_COUNT_LO 0x01 /* Low byte */
+#define IRCC_BOF_COUNT_HI 0x00 /* High nibble (bit 0-3) */
+#define IRCC_BRICKWALL_CNT_LO 0x02 /* Low byte */
+#define IRCC_BRICKWALL_CNT_HI 0x03 /* High nibble (bit 4-7) */
+#define IRCC_TX_SIZE_LO 0x04 /* Low byte */
+#define IRCC_TX_SIZE_HI 0x03 /* High nibble (bit 0-3) */
+#define IRCC_RX_SIZE_HI 0x05 /* High nibble (bit 0-3) */
+#define IRCC_RX_SIZE_LO 0x06 /* Low byte */
+
+#define IRCC_1152 0x80
+#define IRCC_CRC 0x40
+
+/* Register block 5 - IrDA */
+#define IRCC_ATC 0x00
+#define IRCC_ATC_nPROGREADY 0x80
+#define IRCC_ATC_SPEED 0x40
+#define IRCC_ATC_ENABLE 0x20
+#define IRCC_ATC_MASK 0xE0
+
+
+#define IRCC_IRHALFDUPLEX_TIMEOUT 0x01
+
+#define IRCC_SCE_TX_DELAY_TIMER 0x02
+
+/*
+ * Other definitions
+ */
+
+#define SMSC_IRCC2_MAX_SIR_SPEED 115200
+#define SMSC_IRCC2_FIR_CHIP_IO_EXTENT 8
+#define SMSC_IRCC2_SIR_CHIP_IO_EXTENT 8
+#define SMSC_IRCC2_FIFO_SIZE 16
+#define SMSC_IRCC2_FIFO_THRESHOLD 64
+/* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
+#define SMSC_IRCC2_RX_BUFF_TRUESIZE 14384
+#define SMSC_IRCC2_TX_BUFF_TRUESIZE 14384
+#define SMSC_IRCC2_MIN_TURN_TIME 0x07
+#define SMSC_IRCC2_WINDOW_SIZE 0x07
+/* Maximum wait for hw transmitter to finish */
+#define SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US 1000 /* 1 ms */
+/* Maximum wait for ATC transceiver programming to finish */
+#define SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES 1
+#endif /* SMSC_IRCC2_H */
diff --git a/drivers/net/irda/smsc-sio.h b/drivers/net/irda/smsc-sio.h
new file mode 100644
index 000000000000..59e20e653ebe
--- /dev/null
+++ b/drivers/net/irda/smsc-sio.h
@@ -0,0 +1,100 @@
+#ifndef SMSC_SIO_H
+#define SMSC_SIO_H
+
+/******************************************
+ Keys. They should work with every SMsC SIO
+ ******************************************/
+
+#define SMSCSIO_CFGACCESSKEY 0x55
+#define SMSCSIO_CFGEXITKEY 0xaa
+
+/*****************************
+ * Generic SIO Flat (!?) *
+ *****************************/
+
+/* Register 0x0d */
+#define SMSCSIOFLAT_DEVICEID_REG 0x0d
+
+/* Register 0x0c */
+#define SMSCSIOFLAT_UARTMODE0C_REG 0x0c
+#define SMSCSIOFLAT_UART2MODE_MASK 0x38
+#define SMSCSIOFLAT_UART2MODE_VAL_COM 0x00
+#define SMSCSIOFLAT_UART2MODE_VAL_IRDA 0x08
+#define SMSCSIOFLAT_UART2MODE_VAL_ASKIR 0x10
+
+/* Register 0x25 */
+#define SMSCSIOFLAT_UART2BASEADDR_REG 0x25
+
+/* Register 0x2b */
+#define SMSCSIOFLAT_FIRBASEADDR_REG 0x2b
+
+/* Register 0x2c */
+#define SMSCSIOFLAT_FIRDMASELECT_REG 0x2c
+#define SMSCSIOFLAT_FIRDMASELECT_MASK 0x0f
+
+/* Register 0x28 */
+#define SMSCSIOFLAT_UARTIRQSELECT_REG 0x28
+#define SMSCSIOFLAT_UART2IRQSELECT_MASK 0x0f
+#define SMSCSIOFLAT_UART1IRQSELECT_MASK 0xf0
+#define SMSCSIOFLAT_UARTIRQSELECT_VAL_NONE 0x00
+
+
+/*********************
+ * LPC47N227 *
+ *********************/
+
+#define LPC47N227_CFGACCESSKEY 0x55
+#define LPC47N227_CFGEXITKEY 0xaa
+
+/* Register 0x00 */
+#define LPC47N227_FDCPOWERVALIDCONF_REG 0x00
+#define LPC47N227_FDCPOWER_MASK 0x08
+#define LPC47N227_VALID_MASK 0x80
+
+/* Register 0x02 */
+#define LPC47N227_UART12POWER_REG 0x02
+#define LPC47N227_UART1POWERDOWN_MASK 0x08
+#define LPC47N227_UART2POWERDOWN_MASK 0x80
+
+/* Register 0x07 */
+#define LPC47N227_APMBOOTDRIVE_REG 0x07
+#define LPC47N227_PARPORT2AUTOPWRDOWN_MASK 0x10 /* auto power down on if set */
+#define LPC47N227_UART2AUTOPWRDOWN_MASK 0x20 /* auto power down on if set */
+#define LPC47N227_UART1AUTOPWRDOWN_MASK 0x40 /* auto power down on if set */
+
+/* Register 0x0c */
+#define LPC47N227_UARTMODE0C_REG 0x0c
+#define LPC47N227_UART2MODE_MASK 0x38
+#define LPC47N227_UART2MODE_VAL_COM 0x00
+#define LPC47N227_UART2MODE_VAL_IRDA 0x08
+#define LPC47N227_UART2MODE_VAL_ASKIR 0x10
+
+/* Register 0x0d */
+#define LPC47N227_DEVICEID_REG 0x0d
+#define LPC47N227_DEVICEID_DEFVAL 0x5a
+
+/* Register 0x0e */
+#define LPC47N227_REVISIONID_REG 0x0e
+
+/* Register 0x25 */
+#define LPC47N227_UART2BASEADDR_REG 0x25
+
+/* Register 0x28 */
+#define LPC47N227_UARTIRQSELECT_REG 0x28
+#define LPC47N227_UART2IRQSELECT_MASK 0x0f
+#define LPC47N227_UART1IRQSELECT_MASK 0xf0
+#define LPC47N227_UARTIRQSELECT_VAL_NONE 0x00
+
+/* Register 0x2b */
+#define LPC47N227_FIRBASEADDR_REG 0x2b
+
+/* Register 0x2c */
+#define LPC47N227_FIRDMASELECT_REG 0x2c
+#define LPC47N227_FIRDMASELECT_MASK 0x0f
+#define LPC47N227_FIRDMASELECT_VAL_DMA1 0x01 /* 47n227 has three dma channels */
+#define LPC47N227_FIRDMASELECT_VAL_DMA2 0x02
+#define LPC47N227_FIRDMASELECT_VAL_DMA3 0x03
+#define LPC47N227_FIRDMASELECT_VAL_NONE 0x0f
+
+
+#endif
diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c
index 3013e4e821ab..709f375bb1d9 100644
--- a/drivers/net/irda/w83977af_ir.c
+++ b/drivers/net/irda/w83977af_ir.c
@@ -524,6 +524,7 @@ int w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev)
/* Check for empty frame */
if (!skb->len) {
w83977af_change_speed(self, speed);
+ dev->trans_start = jiffies;
dev_kfree_skb(skb);
return 0;
} else
@@ -579,6 +580,7 @@ int w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev)
switch_bank(iobase, SET0);
outb(ICR_ETXTHI, iobase+ICR);
}
+ dev->trans_start = jiffies;
dev_kfree_skb(skb);
/* Restore set register */
diff --git a/include/net/irda/iriap.h b/include/net/irda/iriap.h
index 917e6701e7ab..ccd692fdef36 100644
--- a/include/net/irda/iriap.h
+++ b/include/net/irda/iriap.h
@@ -66,7 +66,7 @@ struct iriap_cb {
__u32 daddr;
__u8 operation;
- struct sk_buff *skb;
+ struct sk_buff *request_skb;
struct lsap_cb *lsap;
__u8 slsap_sel;
diff --git a/include/net/irda/irlan_common.h b/include/net/irda/irlan_common.h
index 02ecaf8e30cb..de3a7fdbebc3 100644
--- a/include/net/irda/irlan_common.h
+++ b/include/net/irda/irlan_common.h
@@ -195,8 +195,6 @@ struct irlan_cb {
struct irlan_cb *irlan_open(__u32 saddr, __u32 daddr);
void irlan_close(struct irlan_cb *self);
void irlan_close_tsaps(struct irlan_cb *self);
-void irlan_mod_inc_use_count(void);
-void irlan_mod_dec_use_count(void);
int irlan_register_netdev(struct irlan_cb *self);
void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel);
diff --git a/include/net/irda/irlmp_event.h b/include/net/irda/irlmp_event.h
index b056e06c29d1..03c6f81a502a 100644
--- a/include/net/irda/irlmp_event.h
+++ b/include/net/irda/irlmp_event.h
@@ -79,26 +79,6 @@ typedef enum {
LM_LAP_IDLE_TIMEOUT,
} IRLMP_EVENT;
-/*
- * Information which is used by the current thread, when executing in the
- * state machine.
- */
-struct irlmp_event {
- IRLMP_EVENT *event;
- struct sk_buff *skb;
-
- __u8 hint;
- __u32 daddr;
- __u32 saddr;
-
- __u8 slsap;
- __u8 dlsap;
-
- int reason;
-
- struct discovery_t *discovery;
-};
-
extern const char *irlmp_state[];
extern const char *irlsap_state[];
diff --git a/include/net/irda/irport.h b/include/net/irda/irport.h
index 79b4d8904346..991f956c096c 100644
--- a/include/net/irda/irport.h
+++ b/include/net/irda/irport.h
@@ -67,6 +67,7 @@ struct irport_cb {
__u32 new_speed;
int mode;
int index; /* Instance index */
+ int transmitting; /* Are we transmitting ? */
spinlock_t lock; /* For serializing operations */
diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c
index 90077d0f1168..6d0245b8c7b4 100644
--- a/net/irda/af_irda.c
+++ b/net/irda/af_irda.c
@@ -11,7 +11,7 @@
* Sources: af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc.
*
* Copyright (c) 1999 Dag Brattli <dagb@cs.uit.no>
- * Copyright (c) 1999-2001 Jean Tourrilhes <jt@hpl.hp.com>
+ * Copyright (c) 1999-2003 Jean Tourrilhes <jt@hpl.hp.com>
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
@@ -190,6 +190,9 @@ static void irda_connect_confirm(void *instance, void *sap,
if (sk == NULL)
return;
+ dev_kfree_skb(skb);
+ // Should be ??? skb_queue_tail(&sk->receive_queue, skb);
+
/* How much header space do we need to reserve */
self->max_header_size = max_header_size;
@@ -220,8 +223,6 @@ static void irda_connect_confirm(void *instance, void *sap,
self->max_data_size);
memcpy(&self->qos_tx, qos, sizeof(struct qos_info));
- dev_kfree_skb(skb);
- // Should be ??? skb_queue_tail(&sk->receive_queue, skb);
/* We are now connected! */
sk->state = TCP_ESTABLISHED;
@@ -260,6 +261,7 @@ static void irda_connect_indication(void *instance, void *sap,
case SOCK_STREAM:
if (max_sdu_size != 0) {
ERROR("%s: max_sdu_size must be 0\n", __FUNCTION__);
+ kfree_skb(skb);
return;
}
self->max_data_size = irttp_get_max_seg_size(self->tsap);
@@ -267,6 +269,7 @@ static void irda_connect_indication(void *instance, void *sap,
case SOCK_SEQPACKET:
if (max_sdu_size == 0) {
ERROR("%s: max_sdu_size cannot be 0\n", __FUNCTION__);
+ kfree_skb(skb);
return;
}
self->max_data_size = max_sdu_size;
@@ -359,7 +362,7 @@ static void irda_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
* doesn't touch the dtsap_sel and save the full value structure...
*/
static void irda_getvalue_confirm(int result, __u16 obj_id,
- struct ias_value *value, void *priv)
+ struct ias_value *value, void *priv)
{
struct irda_sock *self;
@@ -908,6 +911,7 @@ static int irda_accept(struct socket *sock, struct socket *newsock, int flags)
new->tsap = irttp_dup(self->tsap, new);
if (!new->tsap) {
IRDA_DEBUG(0, "%s(), dup failed!\n", __FUNCTION__);
+ kfree_skb(skb);
return -1;
}
@@ -926,6 +930,7 @@ static int irda_accept(struct socket *sock, struct socket *newsock, int flags)
/* Clean up the original one to keep it in listen state */
irttp_listen(self->tsap);
+ /* Wow ! What is that ? Jean II */
skb->sk = NULL;
skb->destructor = NULL;
kfree_skb(skb);
diff --git a/net/irda/ircomm/ircomm_core.c b/net/irda/ircomm/ircomm_core.c
index 122e62971306..552a105d7a9c 100644
--- a/net/irda/ircomm/ircomm_core.c
+++ b/net/irda/ircomm/ircomm_core.c
@@ -10,6 +10,7 @@
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -251,7 +252,6 @@ void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb,
info->max_header_size, skb);
else {
IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ );
- dev_kfree_skb(skb);
}
}
@@ -295,7 +295,6 @@ void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb,
info->max_header_size, skb);
else {
IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ );
- dev_kfree_skb(skb);
}
}
@@ -338,7 +337,6 @@ void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb)
self->notify.data_indication(self->notify.instance, self, skb);
else {
IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ );
- dev_kfree_skb(skb);
}
}
@@ -370,9 +368,8 @@ void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb)
if (skb->len)
ircomm_data_indication(self, skb);
else {
- IRDA_DEBUG(4,
- "%s(), data was control info only!\n", __FUNCTION__ );
- dev_kfree_skb(skb);
+ IRDA_DEBUG(4, "%s(), data was control info only!\n",
+ __FUNCTION__ );
}
}
@@ -408,24 +405,28 @@ EXPORT_SYMBOL(ircomm_control_request);
static void ircomm_control_indication(struct ircomm_cb *self,
struct sk_buff *skb, int clen)
{
- struct sk_buff *ctrl_skb;
-
IRDA_DEBUG(2, "%s()\n", __FUNCTION__ );
- ctrl_skb = skb_clone(skb, GFP_ATOMIC);
- if (!ctrl_skb)
- return;
+ /* Use udata for delivering data on the control channel */
+ if (self->notify.udata_indication) {
+ struct sk_buff *ctrl_skb;
+
+ /* We don't own the skb, so clone it */
+ ctrl_skb = skb_clone(skb, GFP_ATOMIC);
+ if (!ctrl_skb)
+ return;
- /* Remove data channel from control channel */
- skb_trim(ctrl_skb, clen+1);
+ /* Remove data channel from control channel */
+ skb_trim(ctrl_skb, clen+1);
- /* Use udata for delivering data on the control channel */
- if (self->notify.udata_indication)
self->notify.udata_indication(self->notify.instance, self,
ctrl_skb);
- else {
+
+ /* Drop reference count -
+ * see ircomm_tty_control_indication(). */
+ dev_kfree_skb(ctrl_skb);
+ } else {
IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ );
- dev_kfree_skb(skb);
}
}
@@ -470,7 +471,6 @@ void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb,
info->reason, skb);
} else {
IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ );
- dev_kfree_skb(skb);
}
}
diff --git a/net/irda/ircomm/ircomm_event.c b/net/irda/ircomm/ircomm_event.c
index 21dbea74e50a..3ad421cbea83 100644
--- a/net/irda/ircomm/ircomm_event.c
+++ b/net/irda/ircomm/ircomm_event.c
@@ -109,9 +109,7 @@ static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event,
default:
IRDA_DEBUG(4, "%s(), unknown event: %s\n", __FUNCTION__ ,
ircomm_event[event]);
- if (skb)
- dev_kfree_skb(skb);
- return -EINVAL;
+ ret = -EINVAL;
}
return ret;
}
@@ -141,8 +139,6 @@ static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event,
default:
IRDA_DEBUG(0, "%s(), unknown event: %s\n", __FUNCTION__ ,
ircomm_event[event]);
- if (skb)
- dev_kfree_skb(skb);
ret = -EINVAL;
}
return ret;
@@ -176,8 +172,6 @@ static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
default:
IRDA_DEBUG(0, "%s(), unknown event = %s\n", __FUNCTION__ ,
ircomm_event[event]);
- if (skb)
- dev_kfree_skb(skb);
ret = -EINVAL;
}
return ret;
@@ -220,8 +214,6 @@ static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
default:
IRDA_DEBUG(0, "%s(), unknown event = %s\n", __FUNCTION__ ,
ircomm_event[event]);
- if (skb)
- dev_kfree_skb(skb);
ret = -EINVAL;
}
return ret;
diff --git a/net/irda/ircomm/ircomm_lmp.c b/net/irda/ircomm/ircomm_lmp.c
index 6b01349b6d4d..af27a51d08c5 100644
--- a/net/irda/ircomm/ircomm_lmp.c
+++ b/net/irda/ircomm/ircomm_lmp.c
@@ -11,6 +11,7 @@
* Sources: Previous IrLPT work by Thomas Davis
*
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -93,6 +94,10 @@ int ircomm_lmp_connect_request(struct ircomm_cb *self,
IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
+ /* Don't forget to refcount it - should be NULL anyway */
+ if(userdata)
+ skb_get(userdata);
+
ret = irlmp_connect_request(self->lsap, info->dlsap_sel,
info->saddr, info->daddr, NULL, userdata);
return ret;
@@ -106,29 +111,32 @@ int ircomm_lmp_connect_request(struct ircomm_cb *self,
*/
int ircomm_lmp_connect_response(struct ircomm_cb *self, struct sk_buff *userdata)
{
- struct sk_buff *skb;
+ struct sk_buff *tx_skb;
int ret;
IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
/* Any userdata supplied? */
if (userdata == NULL) {
- skb = dev_alloc_skb(64);
- if (!skb)
+ tx_skb = dev_alloc_skb(64);
+ if (!tx_skb)
return -ENOMEM;
/* Reserve space for MUX and LAP header */
- skb_reserve(skb, LMP_MAX_HEADER);
+ skb_reserve(tx_skb, LMP_MAX_HEADER);
} else {
- skb = userdata;
/*
* Check that the client has reserved enough space for
* headers
*/
- ASSERT(skb_headroom(skb) >= LMP_MAX_HEADER, return -1;);
+ ASSERT(skb_headroom(userdata) >= LMP_MAX_HEADER, return -1;);
+
+ /* Don't forget to refcount it - should be NULL anyway */
+ skb_get(userdata);
+ tx_skb = userdata;
}
- ret = irlmp_connect_response(self->lsap, skb);
+ ret = irlmp_connect_response(self->lsap, tx_skb);
return 0;
}
@@ -137,20 +145,24 @@ int ircomm_lmp_disconnect_request(struct ircomm_cb *self,
struct sk_buff *userdata,
struct ircomm_info *info)
{
- struct sk_buff *skb;
+ struct sk_buff *tx_skb;
int ret;
IRDA_DEBUG(0, "%s()\n", __FUNCTION__ );
if (!userdata) {
- skb = dev_alloc_skb(64);
- if (!skb)
+ tx_skb = dev_alloc_skb(64);
+ if (!tx_skb)
return -ENOMEM;
/* Reserve space for MUX and LAP header */
- skb_reserve(skb, LMP_MAX_HEADER);
- userdata = skb;
+ skb_reserve(tx_skb, LMP_MAX_HEADER);
+ userdata = tx_skb;
+ } else {
+ /* Don't forget to refcount it - should be NULL anyway */
+ skb_get(userdata);
}
+
ret = irlmp_disconnect_request(self->lsap, userdata);
return ret;
@@ -217,8 +229,11 @@ int ircomm_lmp_data_request(struct ircomm_cb *self, struct sk_buff *skb,
IRDA_DEBUG(4, "%s(), sending frame\n", __FUNCTION__ );
+ /* Don't forget to refcount it - see ircomm_tty_do_softint() */
+ skb_get(skb);
+
skb->destructor = ircomm_lmp_flow_control;
-
+
if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) {
IRDA_DEBUG(2, "%s(), asking TTY to slow down!\n", __FUNCTION__ );
self->flow_status = FLOW_STOP;
@@ -229,7 +244,7 @@ int ircomm_lmp_data_request(struct ircomm_cb *self, struct sk_buff *skb,
ret = irlmp_data_request(self->lsap, skb);
if (ret) {
ERROR("%s(), failed\n", __FUNCTION__);
- dev_kfree_skb(skb);
+ /* irlmp_data_request already free the packet */
}
return ret;
@@ -254,6 +269,9 @@ int ircomm_lmp_data_indication(void *instance, void *sap,
ircomm_do_event(self, IRCOMM_LMP_DATA_INDICATION, skb, NULL);
+ /* Drop reference count - see ircomm_tty_data_indication(). */
+ dev_kfree_skb(skb);
+
return 0;
}
@@ -285,6 +303,9 @@ void ircomm_lmp_connect_confirm(void *instance, void *sap,
info.qos = qos;
ircomm_do_event(self, IRCOMM_LMP_CONNECT_CONFIRM, skb, &info);
+
+ /* Drop reference count - see ircomm_tty_connect_confirm(). */
+ dev_kfree_skb(skb);
}
/*
@@ -315,6 +336,9 @@ void ircomm_lmp_connect_indication(void *instance, void *sap,
info.qos = qos;
ircomm_do_event(self, IRCOMM_LMP_CONNECT_INDICATION, skb, &info);
+
+ /* Drop reference count - see ircomm_tty_connect_indication(). */
+ dev_kfree_skb(skb);
}
/*
@@ -338,4 +362,8 @@ void ircomm_lmp_disconnect_indication(void *instance, void *sap,
info.reason = reason;
ircomm_do_event(self, IRCOMM_LMP_DISCONNECT_INDICATION, skb, &info);
+
+ /* Drop reference count - see ircomm_tty_disconnect_indication(). */
+ if(skb)
+ dev_kfree_skb(skb);
}
diff --git a/net/irda/ircomm/ircomm_ttp.c b/net/irda/ircomm/ircomm_ttp.c
index ec53428259b9..c3b946e40fe6 100644
--- a/net/irda/ircomm/ircomm_ttp.c
+++ b/net/irda/ircomm/ircomm_ttp.c
@@ -10,6 +10,7 @@
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -94,9 +95,14 @@ int ircomm_ttp_connect_request(struct ircomm_cb *self,
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
+ /* Don't forget to refcount it - should be NULL anyway */
+ if(userdata)
+ skb_get(userdata);
+
ret = irttp_connect_request(self->tsap, info->dlsap_sel,
info->saddr, info->daddr, NULL,
TTP_SAR_DISABLE, userdata);
+
return ret;
}
@@ -106,13 +112,18 @@ int ircomm_ttp_connect_request(struct ircomm_cb *self,
*
*
*/
-int ircomm_ttp_connect_response(struct ircomm_cb *self, struct sk_buff *skb)
+int ircomm_ttp_connect_response(struct ircomm_cb *self,
+ struct sk_buff *userdata)
{
int ret;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__ );
- ret = irttp_connect_response(self->tsap, TTP_SAR_DISABLE, skb);
+ /* Don't forget to refcount it - should be NULL anyway */
+ if(userdata)
+ skb_get(userdata);
+
+ ret = irttp_connect_response(self->tsap, TTP_SAR_DISABLE, userdata);
return ret;
}
@@ -126,7 +137,8 @@ int ircomm_ttp_connect_response(struct ircomm_cb *self, struct sk_buff *skb)
* some of them are sent after connection establishment, so this can
* increase the latency a bit.
*/
-int ircomm_ttp_data_request(struct ircomm_cb *self, struct sk_buff *skb,
+int ircomm_ttp_data_request(struct ircomm_cb *self,
+ struct sk_buff *skb,
int clen)
{
int ret;
@@ -140,6 +152,10 @@ int ircomm_ttp_data_request(struct ircomm_cb *self, struct sk_buff *skb,
* only frames, to make things easier and avoid queueing
*/
ASSERT(skb_headroom(skb) >= IRCOMM_HEADER_SIZE, return -1;);
+
+ /* Don't forget to refcount it - see ircomm_tty_do_softint() */
+ skb_get(skb);
+
skb_push(skb, IRCOMM_HEADER_SIZE);
skb->data[0] = clen;
@@ -147,7 +163,7 @@ int ircomm_ttp_data_request(struct ircomm_cb *self, struct sk_buff *skb,
ret = irttp_data_request(self->tsap, skb);
if (ret) {
ERROR("%s(), failed\n", __FUNCTION__);
- dev_kfree_skb(skb);
+ /* irttp_data_request already free the packet */
}
return ret;
@@ -172,6 +188,9 @@ int ircomm_ttp_data_indication(void *instance, void *sap,
ircomm_do_event(self, IRCOMM_TTP_DATA_INDICATION, skb, NULL);
+ /* Drop reference count - see ircomm_tty_data_indication(). */
+ dev_kfree_skb(skb);
+
return 0;
}
@@ -189,12 +208,11 @@ void ircomm_ttp_connect_confirm(void *instance, void *sap,
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_MAGIC, return;);
ASSERT(skb != NULL, return;);
- ASSERT(qos != NULL, return;);
+ ASSERT(qos != NULL, goto out;);
if (max_sdu_size != TTP_SAR_DISABLE) {
ERROR("%s(), SAR not allowed for IrCOMM!\n", __FUNCTION__);
- dev_kfree_skb(skb);
- return;
+ goto out;
}
info.max_data_size = irttp_get_max_seg_size(self->tsap)
@@ -203,6 +221,10 @@ void ircomm_ttp_connect_confirm(void *instance, void *sap,
info.qos = qos;
ircomm_do_event(self, IRCOMM_TTP_CONNECT_CONFIRM, skb, &info);
+
+out:
+ /* Drop reference count - see ircomm_tty_connect_confirm(). */
+ dev_kfree_skb(skb);
}
/*
@@ -226,12 +248,11 @@ void ircomm_ttp_connect_indication(void *instance, void *sap,
ASSERT(self != NULL, return;);
ASSERT(self->magic == IRCOMM_MAGIC, return;);
ASSERT(skb != NULL, return;);
- ASSERT(qos != NULL, return;);
+ ASSERT(qos != NULL, goto out;);
if (max_sdu_size != TTP_SAR_DISABLE) {
ERROR("%s(), SAR not allowed for IrCOMM!\n", __FUNCTION__);
- dev_kfree_skb(skb);
- return;
+ goto out;
}
info.max_data_size = irttp_get_max_seg_size(self->tsap)
@@ -240,6 +261,10 @@ void ircomm_ttp_connect_indication(void *instance, void *sap,
info.qos = qos;
ircomm_do_event(self, IRCOMM_TTP_CONNECT_INDICATION, skb, &info);
+
+out:
+ /* Drop reference count - see ircomm_tty_connect_indication(). */
+ dev_kfree_skb(skb);
}
/*
@@ -254,6 +279,10 @@ int ircomm_ttp_disconnect_request(struct ircomm_cb *self,
{
int ret;
+ /* Don't forget to refcount it - should be NULL anyway */
+ if(userdata)
+ skb_get(userdata);
+
ret = irttp_disconnect_request(self->tsap, userdata, P_NORMAL);
return ret;
@@ -280,6 +309,10 @@ void ircomm_ttp_disconnect_indication(void *instance, void *sap,
info.reason = reason;
ircomm_do_event(self, IRCOMM_TTP_DISCONNECT_INDICATION, skb, &info);
+
+ /* Drop reference count - see ircomm_tty_disconnect_indication(). */
+ if(skb)
+ dev_kfree_skb(skb);
}
/*
diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c
index e8ba645fc366..19a25ea6c3b9 100644
--- a/net/irda/ircomm/ircomm_tty.c
+++ b/net/irda/ircomm/ircomm_tty.c
@@ -11,6 +11,7 @@
* Sources: serial.c and previous IrCOMM work by Takahide Higuchi
*
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
+ * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -663,8 +664,12 @@ static void ircomm_tty_do_softint(void *private_)
spin_unlock_irqrestore(&self->spinlock, flags);
/* Flush control buffer if any */
- if (ctrl_skb && self->flow == FLOW_START)
- ircomm_control_request(self->ircomm, ctrl_skb);
+ if(ctrl_skb) {
+ if(self->flow == FLOW_START)
+ ircomm_control_request(self->ircomm, ctrl_skb);
+ /* Drop reference count - see ircomm_ttp_data_request(). */
+ dev_kfree_skb(ctrl_skb);
+ }
if (tty->hw_stopped)
return;
@@ -678,8 +683,11 @@ static void ircomm_tty_do_softint(void *private_)
spin_unlock_irqrestore(&self->spinlock, flags);
/* Flush transmit buffer if any */
- if (skb)
+ if (skb) {
ircomm_tty_do_event(self, IRCOMM_TTY_DATA_REQUEST, skb, NULL);
+ /* Drop reference count - see ircomm_ttp_data_request(). */
+ dev_kfree_skb(skb);
+ }
/* Check if user (still) wants to be waken up */
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
@@ -1179,7 +1187,6 @@ static int ircomm_tty_data_indication(void *instance, void *sap,
if (!self->tty) {
IRDA_DEBUG(0, "%s(), no tty!\n", __FUNCTION__ );
- dev_kfree_skb(skb);
return 0;
}
@@ -1204,7 +1211,8 @@ static int ircomm_tty_data_indication(void *instance, void *sap,
* handler
*/
self->tty->ldisc.receive_buf(self->tty, skb->data, NULL, skb->len);
- dev_kfree_skb(skb);
+
+ /* No need to kfree_skb - see ircomm_ttp_data_indication() */
return 0;
}
@@ -1231,7 +1239,8 @@ static int ircomm_tty_control_indication(void *instance, void *sap,
irda_param_extract_all(self, skb->data+1, IRDA_MIN(skb->len-1, clen),
&ircomm_param_info);
- dev_kfree_skb(skb);
+
+ /* No need to kfree_skb - see ircomm_control_indication() */
return 0;
}
diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c
index 2261d8ca5528..fa066ee2026a 100644
--- a/net/irda/ircomm/ircomm_tty_attach.c
+++ b/net/irda/ircomm/ircomm_tty_attach.c
@@ -10,6 +10,7 @@
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved.
+ * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -236,8 +237,9 @@ static void ircomm_tty_ias_register(struct ircomm_tty_cb *self)
irias_insert_object(self->obj);
}
self->skey = irlmp_register_service(hints);
- self->ckey = irlmp_register_client(
- hints, ircomm_tty_discovery_indication, NULL, (void *) self);
+ self->ckey = irlmp_register_client(hints,
+ ircomm_tty_discovery_indication,
+ NULL, (void *) self);
}
/*
@@ -459,7 +461,7 @@ void ircomm_tty_connect_confirm(void *instance, void *sap,
ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_CONFIRM, NULL, NULL);
- dev_kfree_skb(skb);
+ /* No need to kfree_skb - see ircomm_ttp_connect_confirm() */
}
/*
@@ -496,7 +498,7 @@ void ircomm_tty_connect_indication(void *instance, void *sap,
ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_INDICATION, NULL, NULL);
- dev_kfree_skb(skb);
+ /* No need to kfree_skb - see ircomm_ttp_connect_indication() */
}
/*
@@ -647,7 +649,7 @@ static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
ircomm_tty_event[event]);
- return -EINVAL;
+ ret = -EINVAL;
}
return ret;
}
@@ -718,7 +720,7 @@ static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
ircomm_tty_event[event]);
- return -EINVAL;
+ ret = -EINVAL;
}
return ret;
}
@@ -774,7 +776,7 @@ static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
ircomm_tty_event[event]);
- return -EINVAL;
+ ret = -EINVAL;
}
return ret;
}
@@ -822,7 +824,7 @@ static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
ircomm_tty_event[event]);
- return -EINVAL;
+ ret = -EINVAL;
}
return ret;
}
@@ -874,7 +876,7 @@ static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
ircomm_tty_event[event]);
- return -EINVAL;
+ ret = -EINVAL;
}
return ret;
}
@@ -917,7 +919,7 @@ static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
default:
IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ ,
ircomm_tty_event[event]);
- return -EINVAL;
+ ret = -EINVAL;
}
return ret;
}
diff --git a/net/irda/iriap.c b/net/irda/iriap.c
index 6a16559dab9e..232fb7a78a6e 100644
--- a/net/irda/iriap.c
+++ b/net/irda/iriap.c
@@ -11,7 +11,7 @@
*
* Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
* All Rights Reserved.
- * Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
+ * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -210,8 +210,8 @@ static void __iriap_close(struct iriap_cb *self)
del_timer(&self->watchdog_timer);
- if (self->skb)
- dev_kfree_skb(self->skb);
+ if (self->request_skb)
+ dev_kfree_skb(self->request_skb);
self->magic = 0;
@@ -278,7 +278,7 @@ static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode)
*/
static void iriap_disconnect_indication(void *instance, void *sap,
LM_REASON reason,
- struct sk_buff *userdata)
+ struct sk_buff *skb)
{
struct iriap_cb *self;
@@ -293,6 +293,10 @@ static void iriap_disconnect_indication(void *instance, void *sap,
del_timer(&self->watchdog_timer);
+ /* Not needed */
+ if (skb)
+ dev_kfree_skb(skb);
+
if (self->mode == IAS_CLIENT) {
IRDA_DEBUG(4, "%s(), disconnect as client\n", __FUNCTION__);
@@ -312,9 +316,6 @@ static void iriap_disconnect_indication(void *instance, void *sap,
NULL);
iriap_close(self);
}
-
- if (userdata)
- dev_kfree_skb(userdata);
}
/*
@@ -322,15 +323,15 @@ static void iriap_disconnect_indication(void *instance, void *sap,
*/
void iriap_disconnect_request(struct iriap_cb *self)
{
- struct sk_buff *skb;
+ struct sk_buff *tx_skb;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
ASSERT(self != NULL, return;);
ASSERT(self->magic == IAS_MAGIC, return;);
- skb = dev_alloc_skb(64);
- if (skb == NULL) {
+ tx_skb = dev_alloc_skb(64);
+ if (tx_skb == NULL) {
IRDA_DEBUG(0, "%s(), Could not allocate an sk_buff of length %d\n",
__FUNCTION__, 64);
return;
@@ -339,9 +340,9 @@ void iriap_disconnect_request(struct iriap_cb *self)
/*
* Reserve space for MUX control and LAP header
*/
- skb_reserve(skb, LMP_MAX_HEADER);
+ skb_reserve(tx_skb, LMP_MAX_HEADER);
- irlmp_disconnect_request(self->lsap, skb);
+ irlmp_disconnect_request(self->lsap, tx_skb);
}
void iriap_getinfobasedetails_request(void)
@@ -379,7 +380,7 @@ int iriap_getvaluebyclass_request(struct iriap_cb *self,
__u32 saddr, __u32 daddr,
char *name, char *attr)
{
- struct sk_buff *skb;
+ struct sk_buff *tx_skb;
int name_len, attr_len, skb_len;
__u8 *frame;
@@ -405,14 +406,14 @@ int iriap_getvaluebyclass_request(struct iriap_cb *self,
attr_len = strlen(attr); /* Up to IAS_MAX_ATTRIBNAME = 60 */
skb_len = self->max_header_size+2+name_len+1+attr_len+4;
- skb = dev_alloc_skb(skb_len);
- if (!skb)
+ tx_skb = dev_alloc_skb(skb_len);
+ if (!tx_skb)
return -ENOMEM;
/* Reserve space for MUX and LAP header */
- skb_reserve(skb, self->max_header_size);
- skb_put(skb, 3+name_len+attr_len);
- frame = skb->data;
+ skb_reserve(tx_skb, self->max_header_size);
+ skb_put(tx_skb, 3+name_len+attr_len);
+ frame = tx_skb->data;
/* Build frame */
frame[0] = IAP_LST | GET_VALUE_BY_CLASS;
@@ -421,7 +422,10 @@ int iriap_getvaluebyclass_request(struct iriap_cb *self,
frame[2+name_len] = attr_len; /* Insert length of attr */
memcpy(frame+3+name_len, attr, attr_len); /* Insert attr */
- iriap_do_client_event(self, IAP_CALL_REQUEST_GVBC, skb);
+ iriap_do_client_event(self, IAP_CALL_REQUEST_GVBC, tx_skb);
+
+ /* Drop reference count - see state_s_disconnect(). */
+ dev_kfree_skb(tx_skb);
return 0;
}
@@ -495,7 +499,6 @@ void iriap_getvaluebyclass_confirm(struct iriap_cb *self, struct sk_buff *skb)
/* Aborting, close connection! */
iriap_disconnect_request(self);
- dev_kfree_skb(skb);
return;
/* break; */
}
@@ -533,7 +536,6 @@ void iriap_getvaluebyclass_confirm(struct iriap_cb *self, struct sk_buff *skb)
IRDA_DEBUG(0, "%s(), missing handler!\n", __FUNCTION__);
irias_delete_value(value);
}
- dev_kfree_skb(skb);
}
/*
@@ -545,7 +547,7 @@ void iriap_getvaluebyclass_confirm(struct iriap_cb *self, struct sk_buff *skb)
void iriap_getvaluebyclass_response(struct iriap_cb *self, __u16 obj_id,
__u8 ret_code, struct ias_value *value)
{
- struct sk_buff *skb;
+ struct sk_buff *tx_skb;
int n;
__u32 tmp_be32, tmp_be16;
__u8 *fp;
@@ -565,15 +567,15 @@ void iriap_getvaluebyclass_response(struct iriap_cb *self, __u16 obj_id,
* value. We add 32 bytes because of the 6 bytes for the frame and
* max 5 bytes for the value coding.
*/
- skb = dev_alloc_skb(value->len + self->max_header_size + 32);
- if (!skb)
+ tx_skb = dev_alloc_skb(value->len + self->max_header_size + 32);
+ if (!tx_skb)
return;
/* Reserve space for MUX and LAP header */
- skb_reserve(skb, self->max_header_size);
- skb_put(skb, 6);
+ skb_reserve(tx_skb, self->max_header_size);
+ skb_put(tx_skb, 6);
- fp = skb->data;
+ fp = tx_skb->data;
/* Build frame */
fp[n++] = GET_VALUE_BY_CLASS | IAP_LST;
@@ -589,21 +591,21 @@ void iriap_getvaluebyclass_response(struct iriap_cb *self, __u16 obj_id,
switch (value->type) {
case IAS_STRING:
- skb_put(skb, 3 + value->len);
+ skb_put(tx_skb, 3 + value->len);
fp[n++] = value->type;
fp[n++] = 0; /* ASCII */
fp[n++] = (__u8) value->len;
memcpy(fp+n, value->t.string, value->len); n+=value->len;
break;
case IAS_INTEGER:
- skb_put(skb, 5);
+ skb_put(tx_skb, 5);
fp[n++] = value->type;
tmp_be32 = cpu_to_be32(value->t.integer);
memcpy(fp+n, &tmp_be32, 4); n += 4;
break;
case IAS_OCT_SEQ:
- skb_put(skb, 3 + value->len);
+ skb_put(tx_skb, 3 + value->len);
fp[n++] = value->type;
tmp_be16 = cpu_to_be16(value->len);
@@ -612,14 +614,17 @@ void iriap_getvaluebyclass_response(struct iriap_cb *self, __u16 obj_id,
break;
case IAS_MISSING:
IRDA_DEBUG( 3, "%s: sending IAS_MISSING\n", __FUNCTION__);
- skb_put(skb, 1);
+ skb_put(tx_skb, 1);
fp[n++] = value->type;
break;
default:
IRDA_DEBUG(0, "%s(), type not implemented!\n", __FUNCTION__);
break;
}
- iriap_do_r_connect_event(self, IAP_CALL_RESPONSE, skb);
+ iriap_do_r_connect_event(self, IAP_CALL_RESPONSE, tx_skb);
+
+ /* Drop reference count - see state_r_execute(). */
+ dev_kfree_skb(tx_skb);
}
/*
@@ -657,9 +662,6 @@ void iriap_getvaluebyclass_indication(struct iriap_cb *self,
memcpy(attr, fp+n, attr_len); n+=attr_len;
attr[attr_len] = '\0';
- /* We do not need the buffer anymore */
- dev_kfree_skb(skb);
-
IRDA_DEBUG(4, "LM-IAS: Looking up %s: %s\n", name, attr);
obj = irias_find_object(name);
@@ -694,7 +696,7 @@ void iriap_getvaluebyclass_indication(struct iriap_cb *self,
*/
void iriap_send_ack(struct iriap_cb *self)
{
- struct sk_buff *skb;
+ struct sk_buff *tx_skb;
__u8 *frame;
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
@@ -702,19 +704,19 @@ void iriap_send_ack(struct iriap_cb *self)
ASSERT(self != NULL, return;);
ASSERT(self->magic == IAS_MAGIC, return;);
- skb = dev_alloc_skb(64);
- if (!skb)
+ tx_skb = dev_alloc_skb(64);
+ if (!tx_skb)
return;
/* Reserve space for MUX and LAP header */
- skb_reserve(skb, self->max_header_size);
- skb_put(skb, 1);
- frame = skb->data;
+ skb_reserve(tx_skb, self->max_header_size);
+ skb_put(tx_skb, 1);
+ frame = tx_skb->data;
/* Build frame */
frame[0] = IAP_LST | IAP_ACK | self->operation;
- irlmp_data_request(self->lsap, skb);
+ irlmp_data_request(self->lsap, tx_skb);
}
void iriap_connect_request(struct iriap_cb *self)
@@ -742,7 +744,7 @@ void iriap_connect_request(struct iriap_cb *self)
static void iriap_connect_confirm(void *instance, void *sap,
struct qos_info *qos, __u32 max_seg_size,
__u8 max_header_size,
- struct sk_buff *userdata)
+ struct sk_buff *skb)
{
struct iriap_cb *self;
@@ -750,14 +752,17 @@ static void iriap_connect_confirm(void *instance, void *sap,
ASSERT(self != NULL, return;);
ASSERT(self->magic == IAS_MAGIC, return;);
- ASSERT(userdata != NULL, return;);
+ ASSERT(skb != NULL, return;);
self->max_data_size = max_seg_size;
self->max_header_size = max_header_size;
del_timer(&self->watchdog_timer);
- iriap_do_client_event(self, IAP_LM_CONNECT_CONFIRM, userdata);
+ iriap_do_client_event(self, IAP_LM_CONNECT_CONFIRM, skb);
+
+ /* Drop reference count - see state_s_make_call(). */
+ dev_kfree_skb(skb);
}
/*
@@ -769,7 +774,7 @@ static void iriap_connect_confirm(void *instance, void *sap,
static void iriap_connect_indication(void *instance, void *sap,
struct qos_info *qos, __u32 max_seg_size,
__u8 max_header_size,
- struct sk_buff *userdata)
+ struct sk_buff *skb)
{
struct iriap_cb *self, *new;
@@ -777,22 +782,22 @@ static void iriap_connect_indication(void *instance, void *sap,
self = (struct iriap_cb *) instance;
- ASSERT(self != NULL, return;);
- ASSERT(self->magic == IAS_MAGIC, return;);
+ ASSERT(skb != NULL, return;);
+ ASSERT(self != NULL, goto out;);
+ ASSERT(self->magic == IAS_MAGIC, goto out;);
/* Start new server */
new = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL);
if (!new) {
IRDA_DEBUG(0, "%s(), open failed\n", __FUNCTION__);
- dev_kfree_skb(userdata);
- return;
+ goto out;
}
/* Now attach up the new "socket" */
new->lsap = irlmp_dup(self->lsap, new);
if (!new->lsap) {
IRDA_DEBUG(0, "%s(), dup failed!\n", __FUNCTION__);
- return;
+ goto out;
}
new->max_data_size = max_seg_size;
@@ -801,7 +806,11 @@ static void iriap_connect_indication(void *instance, void *sap,
/* Clean up the original one to keep it in listen state */
irlmp_listen(self->lsap);
- iriap_do_server_event(new, IAP_LM_CONNECT_INDICATION, userdata);
+ iriap_do_server_event(new, IAP_LM_CONNECT_INDICATION, skb);
+
+out:
+ /* Drop reference count - see state_r_disconnect(). */
+ dev_kfree_skb(skb);
}
/*
@@ -821,10 +830,9 @@ static int iriap_data_indication(void *instance, void *sap,
self = (struct iriap_cb *) instance;
- ASSERT(self != NULL, return 0;);
- ASSERT(self->magic == IAS_MAGIC, return 0;);
-
ASSERT(skb != NULL, return 0;);
+ ASSERT(self != NULL, goto out;);
+ ASSERT(self->magic == IAS_MAGIC, goto out;);
frame = skb->data;
@@ -832,22 +840,19 @@ static int iriap_data_indication(void *instance, void *sap,
/* Call server */
IRDA_DEBUG(4, "%s(), Calling server!\n", __FUNCTION__);
iriap_do_r_connect_event(self, IAP_RECV_F_LST, skb);
-
- return 0;
+ goto out;
}
opcode = frame[0];
if (~opcode & IAP_LST) {
WARNING("%s:, IrIAS multiframe commands or "
"results is not implemented yet!\n", __FUNCTION__);
- dev_kfree_skb(skb);
- return 0;
+ goto out;
}
/* Check for ack frames since they don't contain any data */
if (opcode & IAP_ACK) {
IRDA_DEBUG(0, "%s() Got ack frame!\n", __FUNCTION__);
- dev_kfree_skb(skb);
- return 0;
+ goto out;
}
opcode &= ~IAP_LST; /* Mask away LST bit */
@@ -855,7 +860,6 @@ static int iriap_data_indication(void *instance, void *sap,
switch (opcode) {
case GET_INFO_BASE:
IRDA_DEBUG(0, "IrLMP GetInfoBaseDetails not implemented!\n");
- dev_kfree_skb(skb);
break;
case GET_VALUE_BY_CLASS:
iriap_do_call_event(self, IAP_RECV_F_LST, NULL);
@@ -876,7 +880,6 @@ static int iriap_data_indication(void *instance, void *sap,
if (self->confirm)
self->confirm(IAS_CLASS_UNKNOWN, 0, NULL,
self->priv);
- dev_kfree_skb(skb);
break;
case IAS_ATTRIB_UNKNOWN:
IRDA_DEBUG(1, "%s(), No such attribute!\n", __FUNCTION__);
@@ -890,16 +893,18 @@ static int iriap_data_indication(void *instance, void *sap,
if (self->confirm)
self->confirm(IAS_ATTRIB_UNKNOWN, 0, NULL,
self->priv);
- dev_kfree_skb(skb);
break;
}
break;
default:
IRDA_DEBUG(0, "%s(), Unknown op-code: %02x\n", __FUNCTION__,
opcode);
- dev_kfree_skb(skb);
break;
}
+
+out:
+ /* Cleanup - sub-calls will have done skb_get() as needed. */
+ dev_kfree_skb(skb);
return 0;
}
@@ -939,6 +944,7 @@ void iriap_call_indication(struct iriap_cb *self, struct sk_buff *skb)
iriap_getvaluebyclass_indication(self, skb);
break;
}
+ /* skb will be cleaned up in iriap_data_indication */
}
/*
diff --git a/net/irda/iriap_event.c b/net/irda/iriap_event.c
index 07f27182e743..ce209ad44eaf 100644
--- a/net/irda/iriap_event.c
+++ b/net/irda/iriap_event.c
@@ -11,6 +11,7 @@
*
* Copyright (c) 1997, 1999-2000 Dag Brattli <dagb@cs.uit.no>,
* All Rights Reserved.
+ * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -174,8 +175,11 @@ static void state_s_disconnect(struct iriap_cb *self, IRIAP_EVENT event,
switch (event) {
case IAP_CALL_REQUEST_GVBC:
iriap_next_client_state(self, S_CONNECTING);
- ASSERT(self->skb == NULL, return;);
- self->skb = skb;
+ ASSERT(self->request_skb == NULL, return;);
+ /* Don't forget to refcount it -
+ * see iriap_getvaluebyclass_request(). */
+ skb_get(skb);
+ self->request_skb = skb;
iriap_connect_request(self);
break;
case IAP_LM_DISCONNECT_INDICATION:
@@ -251,20 +255,21 @@ static void state_s_call(struct iriap_cb *self, IRIAP_EVENT event,
static void state_s_make_call(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
+ struct sk_buff *tx_skb;
+
ASSERT(self != NULL, return;);
switch (event) {
case IAP_CALL_REQUEST:
- skb = self->skb;
- self->skb = NULL;
+ /* Already refcounted - see state_s_disconnect() */
+ tx_skb = self->request_skb;
+ self->request_skb = NULL;
- irlmp_data_request(self->lsap, skb);
+ irlmp_data_request(self->lsap, tx_skb);
iriap_next_call_state(self, S_OUTSTANDING);
break;
default:
IRDA_DEBUG(0, "%s(), Unknown event %d\n", __FUNCTION__, event);
- if (skb)
- dev_kfree_skb(skb);
break;
}
}
@@ -379,10 +384,6 @@ static void state_r_disconnect(struct iriap_cb *self, IRIAP_EVENT event,
* care about LM_Idle_request()!
*/
iriap_next_r_connect_state(self, R_RECEIVING);
-
- if (skb)
- dev_kfree_skb(skb);
-
break;
default:
IRDA_DEBUG(0, "%s(), unknown event %d\n", __FUNCTION__, event);
@@ -450,7 +451,6 @@ static void state_r_receiving(struct iriap_cb *self, IRIAP_EVENT event,
IRDA_DEBUG(0, "%s(), unknown event!\n", __FUNCTION__);
break;
}
-
}
/*
@@ -465,11 +465,8 @@ static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event,
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
ASSERT(skb != NULL, return;);
-
- if (!self || self->magic != IAS_MAGIC) {
- IRDA_DEBUG(0, "%s(), bad pointer self\n", __FUNCTION__);
- return;
- }
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == IAS_MAGIC, return;);
switch (event) {
case IAP_CALL_RESPONSE:
@@ -479,6 +476,10 @@ static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event,
*/
iriap_next_r_connect_state(self, R_RECEIVING);
+ /* Don't forget to refcount it - see
+ * iriap_getvaluebyclass_response(). */
+ skb_get(skb);
+
irlmp_data_request(self->lsap, skb);
break;
default:
diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c
index 727c1eb64c2f..69236887ac55 100644
--- a/net/irda/irlan/irlan_common.c
+++ b/net/irda/irlan/irlan_common.c
@@ -1180,20 +1180,6 @@ void print_ret_code(__u8 code)
}
}
-void irlan_mod_inc_use_count(void)
-{
-#ifdef MODULE
- MOD_INC_USE_COUNT;
-#endif
-}
-
-void irlan_mod_dec_use_count(void)
-{
-#ifdef MODULE
- MOD_DEC_USE_COUNT;
-#endif
-}
-
MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
MODULE_DESCRIPTION("The Linux IrDA LAN protocol");
MODULE_LICENSE("GPL");
diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c
index 8639f6a5ea07..6ba413f5e82b 100644
--- a/net/irda/irlan/irlan_eth.c
+++ b/net/irda/irlan/irlan_eth.c
@@ -31,6 +31,7 @@
#include <linux/inetdevice.h>
#include <linux/if_arp.h>
#include <linux/random.h>
+#include <linux/module.h>
#include <net/arp.h>
#include <net/irda/irda.h>
@@ -61,6 +62,7 @@ int irlan_eth_init(struct net_device *dev)
dev->hard_start_xmit = irlan_eth_xmit;
dev->get_stats = irlan_eth_get_stats;
dev->set_multicast_list = irlan_eth_set_multicast_list;
+ SET_MODULE_OWNER(dev);
ether_setup(dev);
@@ -112,8 +114,6 @@ int irlan_eth_open(struct net_device *dev)
self->disconnect_reason = 0;
irlan_client_wakeup(self, self->saddr, self->daddr);
- irlan_mod_inc_use_count();
-
/* Make sure we have a hardware address before we return, so DHCP clients gets happy */
interruptible_sleep_on(&self->open_wait);
@@ -138,8 +138,6 @@ int irlan_eth_close(struct net_device *dev)
/* Stop device */
netif_stop_queue(dev);
- irlan_mod_dec_use_count();
-
irlan_close_data_channel(self);
irlan_close_tsaps(self);
@@ -206,7 +204,7 @@ int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev)
* confuse do_dev_queue_xmit() in dev.c! I have
* tried :-) DB
*/
- dev_kfree_skb(skb);
+ /* irttp_data_request already free the packet */
self->stats.tx_dropped++;
} else {
self->stats.tx_packets++;
diff --git a/net/irda/irlap.c b/net/irda/irlap.c
index 59b08f0d698c..627278c5fef9 100644
--- a/net/irda/irlap.c
+++ b/net/irda/irlap.c
@@ -10,7 +10,7 @@
* Modified by: Dag Brattli <dagb@cs.uit.no>
*
* Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved.
- * Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
+ * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -244,7 +244,6 @@ void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb)
irlap_init_qos_capabilities(self, NULL); /* No user QoS! */
- skb_get(skb); /*LEVEL4*/
irlmp_link_connect_indication(self->notify.instance, self->saddr,
self->daddr, &self->qos_tx, skb);
}
@@ -255,12 +254,11 @@ void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb)
* Service user has accepted incoming connection
*
*/
-void irlap_connect_response(struct irlap_cb *self, struct sk_buff *skb)
+void irlap_connect_response(struct irlap_cb *self, struct sk_buff *userdata)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
- irlap_do_event(self, CONNECT_RESPONSE, skb, NULL);
- kfree_skb(skb);
+ irlap_do_event(self, CONNECT_RESPONSE, userdata, NULL);
}
/*
@@ -305,7 +303,6 @@ void irlap_connect_confirm(struct irlap_cb *self, struct sk_buff *skb)
ASSERT(self != NULL, return;);
ASSERT(self->magic == LAP_MAGIC, return;);
- skb_get(skb); /*LEVEL4*/
irlmp_link_connect_confirm(self->notify.instance, &self->qos_tx, skb);
}
@@ -322,7 +319,6 @@ void irlap_data_indication(struct irlap_cb *self, struct sk_buff *skb,
/* Hide LAP header from IrLMP layer */
skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
- skb_get(skb); /*LEVEL4*/
irlmp_link_data_indication(self->notify.instance, skb, unreliable);
}
@@ -354,6 +350,9 @@ void irlap_data_request(struct irlap_cb *self, struct sk_buff *skb,
else
skb->data[1] = I_FRAME;
+ /* Don't forget to refcount it - see irlmp_connect_request(). */
+ skb_get(skb);
+
/* Add at the end of the queue (keep ordering) - Jean II */
skb_queue_tail(&self->txq, skb);
@@ -392,6 +391,8 @@ void irlap_unitdata_request(struct irlap_cb *self, struct sk_buff *skb)
skb->data[0] = CBROADCAST;
skb->data[1] = UI_FRAME;
+ /* Don't need to refcount, see irlmp_connless_data_request() */
+
skb_queue_tail(&self->txq_ultra, skb);
irlap_do_event(self, SEND_UI_FRAME, NULL, NULL);
@@ -416,7 +417,6 @@ void irlap_unitdata_indication(struct irlap_cb *self, struct sk_buff *skb)
/* Hide LAP header from IrLMP layer */
skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
- skb_get(skb); /*LEVEL4*/
irlmp_link_unitdata_indication(self->notify.instance, skb);
}
#endif /* CONFIG_IRDA_ULTRA */
diff --git a/net/irda/irlap_event.c b/net/irda/irlap_event.c
index 975c30d3d119..b6df8f99d6f6 100644
--- a/net/irda/irlap_event.c
+++ b/net/irda/irlap_event.c
@@ -12,7 +12,7 @@
* Copyright (c) 1998-2000 Dag Brattli <dag@brattli.net>,
* Copyright (c) 1998 Thomas Davis <ratbert@radiks.net>
* All Rights Reserved.
- * Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
+ * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -287,6 +287,9 @@ void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event,
/* Send one frame */
ret = (*state[self->state])(self, SEND_I_CMD,
skb, NULL);
+ /* Drop reference count.
+ * It will be increase as needed in
+ * irlap_send_data_xxx() */
kfree_skb(skb);
/* Poll the higher layers for one more frame */
@@ -517,6 +520,8 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
CMD_FRAME);
else
break;
+ /* irlap_send_ui_frame() won't increase skb reference
+ * count, so no dev_kfree_skb() - Jean II */
}
if (i == 2) {
/* Force us to listen 500 ms again */
diff --git a/net/irda/irlap_frame.c b/net/irda/irlap_frame.c
index c40c1197c129..856c3c63cfe4 100644
--- a/net/irda/irlap_frame.c
+++ b/net/irda/irlap_frame.c
@@ -11,7 +11,7 @@
*
* Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
* All Rights Reserved.
- * Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
+ * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -106,7 +106,7 @@ void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb)
*/
void irlap_send_snrm_frame(struct irlap_cb *self, struct qos_info *qos)
{
- struct sk_buff *skb;
+ struct sk_buff *tx_skb;
struct snrm_frame *frame;
int ret;
@@ -114,11 +114,11 @@ void irlap_send_snrm_frame(struct irlap_cb *self, struct qos_info *qos)
ASSERT(self->magic == LAP_MAGIC, return;);
/* Allocate frame */
- skb = dev_alloc_skb(64);
- if (!skb)
+ tx_skb = dev_alloc_skb(64);
+ if (!tx_skb)
return;
- frame = (struct snrm_frame *) skb_put(skb, 2);
+ frame = (struct snrm_frame *) skb_put(tx_skb, 2);
/* Insert connection address field */
if (qos)
@@ -133,19 +133,19 @@ void irlap_send_snrm_frame(struct irlap_cb *self, struct qos_info *qos)
* If we are establishing a connection then insert QoS paramerters
*/
if (qos) {
- skb_put(skb, 9); /* 21 left */
+ skb_put(tx_skb, 9); /* 21 left */
frame->saddr = cpu_to_le32(self->saddr);
frame->daddr = cpu_to_le32(self->daddr);
frame->ncaddr = self->caddr;
- ret = irlap_insert_qos_negotiation_params(self, skb);
+ ret = irlap_insert_qos_negotiation_params(self, tx_skb);
if (ret < 0) {
- dev_kfree_skb(skb);
+ dev_kfree_skb(tx_skb);
return;
}
}
- irlap_queue_xmit(self, skb);
+ irlap_queue_xmit(self, tx_skb);
}
/*
@@ -162,8 +162,8 @@ static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb,
frame = (struct snrm_frame *) skb->data;
if (skb->len >= sizeof(struct snrm_frame)) {
- /* Copy the new connection address */
- info->caddr = frame->ncaddr;
+ /* Copy the new connection address ignoring the C/R bit */
+ info->caddr = frame->ncaddr & 0xFE;
/* Check if the new connection address is valid */
if ((info->caddr == 0x00) || (info->caddr == 0xfe)) {
@@ -197,7 +197,7 @@ static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb,
*/
void irlap_send_ua_response_frame(struct irlap_cb *self, struct qos_info *qos)
{
- struct sk_buff *skb;
+ struct sk_buff *tx_skb;
struct ua_frame *frame;
int ret;
@@ -206,14 +206,12 @@ void irlap_send_ua_response_frame(struct irlap_cb *self, struct qos_info *qos)
ASSERT(self != NULL, return;);
ASSERT(self->magic == LAP_MAGIC, return;);
- skb = NULL;
-
/* Allocate frame */
- skb = dev_alloc_skb(64);
- if (!skb)
+ tx_skb = dev_alloc_skb(64);
+ if (!tx_skb)
return;
- frame = (struct ua_frame *) skb_put(skb, 10);
+ frame = (struct ua_frame *) skb_put(tx_skb, 10);
/* Build UA response */
frame->caddr = self->caddr;
@@ -224,14 +222,14 @@ void irlap_send_ua_response_frame(struct irlap_cb *self, struct qos_info *qos)
/* Should we send QoS negotiation parameters? */
if (qos) {
- ret = irlap_insert_qos_negotiation_params(self, skb);
+ ret = irlap_insert_qos_negotiation_params(self, tx_skb);
if (ret < 0) {
- dev_kfree_skb(skb);
+ dev_kfree_skb(tx_skb);
return;
}
}
- irlap_queue_xmit(self, skb);
+ irlap_queue_xmit(self, tx_skb);
}
@@ -243,17 +241,17 @@ void irlap_send_ua_response_frame(struct irlap_cb *self, struct qos_info *qos)
*/
void irlap_send_dm_frame( struct irlap_cb *self)
{
- struct sk_buff *skb = NULL;
+ struct sk_buff *tx_skb = NULL;
__u8 *frame;
ASSERT(self != NULL, return;);
ASSERT(self->magic == LAP_MAGIC, return;);
- skb = dev_alloc_skb(32);
- if (!skb)
+ tx_skb = dev_alloc_skb(32);
+ if (!tx_skb)
return;
- frame = skb_put( skb, 2);
+ frame = skb_put(tx_skb, 2);
if (self->state == LAP_NDM)
frame[0] = CBROADCAST;
@@ -262,7 +260,7 @@ void irlap_send_dm_frame( struct irlap_cb *self)
frame[1] = DM_RSP | PF_BIT;
- irlap_queue_xmit(self, skb);
+ irlap_queue_xmit(self, tx_skb);
}
/*
@@ -273,7 +271,7 @@ void irlap_send_dm_frame( struct irlap_cb *self)
*/
void irlap_send_disc_frame(struct irlap_cb *self)
{
- struct sk_buff *skb = NULL;
+ struct sk_buff *tx_skb = NULL;
__u8 *frame;
IRDA_DEBUG(3, "%s()\n", __FUNCTION__);
@@ -281,16 +279,16 @@ void irlap_send_disc_frame(struct irlap_cb *self)
ASSERT(self != NULL, return;);
ASSERT(self->magic == LAP_MAGIC, return;);
- skb = dev_alloc_skb(16);
- if (!skb)
+ tx_skb = dev_alloc_skb(16);
+ if (!tx_skb)
return;
- frame = skb_put(skb, 2);
+ frame = skb_put(tx_skb, 2);
frame[0] = self->caddr | CMD_FRAME;
frame[1] = DISC_CMD | PF_BIT;
- irlap_queue_xmit(self, skb);
+ irlap_queue_xmit(self, tx_skb);
}
/*
@@ -302,7 +300,7 @@ void irlap_send_disc_frame(struct irlap_cb *self)
void irlap_send_discovery_xid_frame(struct irlap_cb *self, int S, __u8 s,
__u8 command, discovery_t *discovery)
{
- struct sk_buff *skb = NULL;
+ struct sk_buff *tx_skb = NULL;
struct xid_frame *frame;
__u32 bcast = BROADCAST;
__u8 *info;
@@ -314,12 +312,12 @@ void irlap_send_discovery_xid_frame(struct irlap_cb *self, int S, __u8 s,
ASSERT(self->magic == LAP_MAGIC, return;);
ASSERT(discovery != NULL, return;);
- skb = dev_alloc_skb(64);
- if (!skb)
+ tx_skb = dev_alloc_skb(64);
+ if (!tx_skb)
return;
- skb_put(skb, 14);
- frame = (struct xid_frame *) skb->data;
+ skb_put(tx_skb, 14);
+ frame = (struct xid_frame *) tx_skb->data;
if (command) {
frame->caddr = CBROADCAST | CMD_FRAME;
@@ -367,21 +365,21 @@ void irlap_send_discovery_xid_frame(struct irlap_cb *self, int S, __u8 s,
int len;
if (discovery->data.hints[0] & HINT_EXTENSION) {
- info = skb_put(skb, 2);
+ info = skb_put(tx_skb, 2);
info[0] = discovery->data.hints[0];
info[1] = discovery->data.hints[1];
} else {
- info = skb_put(skb, 1);
+ info = skb_put(tx_skb, 1);
info[0] = discovery->data.hints[0];
}
- info = skb_put(skb, 1);
+ info = skb_put(tx_skb, 1);
info[0] = discovery->data.charset;
- len = IRDA_MIN(discovery->name_len, skb_tailroom(skb));
- info = skb_put(skb, len);
+ len = IRDA_MIN(discovery->name_len, skb_tailroom(tx_skb));
+ info = skb_put(tx_skb, len);
memcpy(info, discovery->data.info, len);
}
- irlap_queue_xmit(self, skb);
+ irlap_queue_xmit(self, tx_skb);
}
/*
@@ -498,7 +496,6 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self,
break;
default:
/* Error!! */
- dev_kfree_skb(skb);
return;
}
info->s = xid->slotnr;
@@ -561,21 +558,21 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self,
*/
void irlap_send_rr_frame(struct irlap_cb *self, int command)
{
- struct sk_buff *skb;
+ struct sk_buff *tx_skb;
__u8 *frame;
- skb = dev_alloc_skb(16);
- if (!skb)
+ tx_skb = dev_alloc_skb(16);
+ if (!tx_skb)
return;
- frame = skb_put(skb, 2);
+ frame = skb_put(tx_skb, 2);
frame[0] = self->caddr;
frame[0] |= (command) ? CMD_FRAME : 0;
frame[1] = RR | PF_BIT | (self->vr << 5);
- irlap_queue_xmit(self, skb);
+ irlap_queue_xmit(self, tx_skb);
}
/*
@@ -586,19 +583,19 @@ void irlap_send_rr_frame(struct irlap_cb *self, int command)
*/
void irlap_send_rd_frame(struct irlap_cb *self)
{
- struct sk_buff *skb;
+ struct sk_buff *tx_skb;
__u8 *frame;
- skb = dev_alloc_skb(16);
- if (!skb)
+ tx_skb = dev_alloc_skb(16);
+ if (!tx_skb)
return;
- frame = skb_put(skb, 2);
+ frame = skb_put(tx_skb, 2);
frame[0] = self->caddr;
frame[1] = RD_RSP | PF_BIT;
- irlap_queue_xmit(self, skb);
+ irlap_queue_xmit(self, tx_skb);
}
/*
@@ -623,17 +620,17 @@ static inline void irlap_recv_rr_frame(struct irlap_cb *self,
void irlap_send_frmr_frame( struct irlap_cb *self, int command)
{
- struct sk_buff *skb = NULL;
+ struct sk_buff *tx_skb = NULL;
__u8 *frame;
ASSERT( self != NULL, return;);
ASSERT( self->magic == LAP_MAGIC, return;);
- skb = dev_alloc_skb( 32);
- if (!skb)
+ tx_skb = dev_alloc_skb( 32);
+ if (!tx_skb)
return;
- frame = skb_put( skb, 2);
+ frame = skb_put(tx_skb, 2);
frame[0] = self->caddr;
frame[0] |= (command) ? CMD_FRAME : 0;
@@ -646,7 +643,7 @@ void irlap_send_frmr_frame( struct irlap_cb *self, int command)
IRDA_DEBUG(4, "%s(), vr=%d, %ld\n", __FUNCTION__, self->vr, jiffies);
- irlap_queue_xmit(self, skb);
+ irlap_queue_xmit(self, tx_skb);
}
/*
@@ -739,17 +736,19 @@ void irlap_send_data_primary(struct irlap_cb *self, struct sk_buff *skb)
*/
skb->data[1] = I_FRAME | (self->vs << 1);
+ /*
+ * Insert frame in store, in case of retransmissions
+ * Increase skb reference count, see irlap_do_event()
+ */
+ skb_get(skb);
+ skb_queue_tail(&self->wx_list, skb);
+
/* Copy buffer */
tx_skb = skb_clone(skb, GFP_ATOMIC);
if (tx_skb == NULL) {
return;
}
- /*
- * Insert frame in store, in case of retransmissions
- */
- skb_queue_tail(&self->wx_list, skb_get(skb));
-
self->vs = (self->vs + 1) % 8;
self->ack_required = FALSE;
self->window -= 1;
@@ -782,6 +781,13 @@ void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb)
*/
skb->data[1] = I_FRAME | (self->vs << 1);
+ /*
+ * Insert frame in store, in case of retransmissions
+ * Increase skb reference count, see irlap_do_event()
+ */
+ skb_get(skb);
+ skb_queue_tail(&self->wx_list, skb);
+
/* Copy buffer */
tx_skb = skb_clone(skb, GFP_ATOMIC);
if (tx_skb == NULL) {
@@ -789,11 +795,6 @@ void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb)
}
/*
- * Insert frame in store, in case of retransmissions
- */
- skb_queue_tail(&self->wx_list, skb_get(skb));
-
- /*
* Set poll bit if necessary. We do this to the copied
* skb, since retransmitted need to set or clear the poll
* bit depending on when they are sent.
@@ -850,14 +851,18 @@ void irlap_send_data_secondary_final(struct irlap_cb *self,
*/
skb->data[1] = I_FRAME | (self->vs << 1);
+ /*
+ * Insert frame in store, in case of retransmissions
+ * Increase skb reference count, see irlap_do_event()
+ */
+ skb_get(skb);
+ skb_queue_tail(&self->wx_list, skb);
+
tx_skb = skb_clone(skb, GFP_ATOMIC);
if (tx_skb == NULL) {
return;
}
- /* Insert frame in store */
- skb_queue_tail(&self->wx_list, skb_get(skb));
-
tx_skb->data[1] |= PF_BIT;
self->vs = (self->vs + 1) % 8;
@@ -903,14 +908,18 @@ void irlap_send_data_secondary(struct irlap_cb *self, struct sk_buff *skb)
*/
skb->data[1] = I_FRAME | (self->vs << 1);
+ /*
+ * Insert frame in store, in case of retransmissions
+ * Increase skb reference count, see irlap_do_event()
+ */
+ skb_get(skb);
+ skb_queue_tail(&self->wx_list, skb);
+
tx_skb = skb_clone(skb, GFP_ATOMIC);
if (tx_skb == NULL) {
return;
}
- /* Insert frame in store */
- skb_queue_tail(&self->wx_list, skb_get(skb));
-
self->vs = (self->vs + 1) % 8;
self->ack_required = FALSE;
self->window -= 1;
@@ -939,8 +948,6 @@ void irlap_resend_rejected_frames(struct irlap_cb *self, int command)
ASSERT(self->magic == LAP_MAGIC, return;);
/* Initialize variables */
- skb = tx_skb = NULL;
-
count = skb_queue_len(&self->wx_list);
/* Resend unacknowledged frame(s) */
@@ -1020,9 +1027,6 @@ void irlap_resend_rejected_frame(struct irlap_cb *self, int command)
ASSERT(self != NULL, return;);
ASSERT(self->magic == LAP_MAGIC, return;);
- /* Initialize variables */
- skb = tx_skb = NULL;
-
/* Resend unacknowledged frame(s) */
skb = skb_peek(&self->wx_list);
if (skb != NULL) {
@@ -1186,35 +1190,35 @@ static void irlap_recv_frmr_frame(struct irlap_cb *self, struct sk_buff *skb,
void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr,
struct sk_buff *cmd)
{
- struct sk_buff *skb;
+ struct sk_buff *tx_skb;
struct test_frame *frame;
__u8 *info;
- skb = dev_alloc_skb(cmd->len+sizeof(struct test_frame));
- if (!skb)
+ tx_skb = dev_alloc_skb(cmd->len+sizeof(struct test_frame));
+ if (!tx_skb)
return;
/* Broadcast frames must include saddr and daddr fields */
if (caddr == CBROADCAST) {
frame = (struct test_frame *)
- skb_put(skb, sizeof(struct test_frame));
+ skb_put(tx_skb, sizeof(struct test_frame));
/* Insert the swapped addresses */
frame->saddr = cpu_to_le32(self->saddr);
frame->daddr = cpu_to_le32(daddr);
} else
- frame = (struct test_frame *) skb_put(skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER);
+ frame = (struct test_frame *) skb_put(tx_skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER);
frame->caddr = caddr;
frame->control = TEST_RSP | PF_BIT;
/* Copy info */
- info = skb_put(skb, cmd->len);
+ info = skb_put(tx_skb, cmd->len);
memcpy(info, cmd->data, cmd->len);
/* Return to sender */
irlap_wait_min_turn_around(self, &self->qos_tx);
- irlap_queue_xmit(self, skb);
+ irlap_queue_xmit(self, tx_skb);
}
/*
@@ -1263,6 +1267,15 @@ static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb,
* Called when a frame is received. Dispatches the right receive function
* for processing of the frame.
*
+ * Note on skb management :
+ * After calling the higher layers of the IrDA stack, we always
+ * kfree() the skb, which drop the reference count (and potentially
+ * destroy it).
+ * If a higher layer of the stack want to keep the skb around (to put
+ * in a queue or pass it to the higher layer), it will need to use
+ * skb_get() to keep a reference on it. This is usually done at the
+ * LMP level in irlmp.c.
+ * Jean II
*/
int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *ptype)
@@ -1286,6 +1299,7 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
* we don't need to be clever about it. Jean II */
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
ERROR("%s: can't clone shared skb!\n", __FUNCTION__);
+ dev_kfree_skb(skb);
return -1;
}
if (skb_is_nonlinear(skb))
@@ -1390,6 +1404,7 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
break;
}
out:
+ /* Always drop our reference on the skb */
dev_kfree_skb(skb);
return 0;
}
diff --git a/net/irda/irlmp.c b/net/irda/irlmp.c
index a44327be74d8..b6d41fc2772f 100644
--- a/net/irda/irlmp.c
+++ b/net/irda/irlmp.c
@@ -11,7 +11,7 @@
*
* Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
* All Rights Reserved.
- * Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
+ * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -345,9 +345,10 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
__u32 saddr, __u32 daddr,
struct qos_info *qos, struct sk_buff *userdata)
{
- struct sk_buff *skb = NULL;
+ struct sk_buff *tx_skb = userdata;
struct lap_cb *lap;
struct lsap_cb *lsap;
+ int ret;
ASSERT(self != NULL, return -EBADR;);
ASSERT(self->magic == LMP_LSAP_MAGIC, return -EBADR;);
@@ -356,26 +357,29 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
"%s(), slsap_sel=%02x, dlsap_sel=%02x, saddr=%08x, daddr=%08x\n",
__FUNCTION__, self->slsap_sel, dlsap_sel, saddr, daddr);
- if (test_bit(0, &self->connected))
- return -EISCONN;
+ if (test_bit(0, &self->connected)) {
+ ret = -EISCONN;
+ goto err;
+ }
/* Client must supply destination device address */
- if (!daddr)
- return -EINVAL;
+ if (!daddr) {
+ ret = -EINVAL;
+ goto err;
+ }
/* Any userdata? */
- if (userdata == NULL) {
- skb = dev_alloc_skb(64);
- if (!skb)
+ if (tx_skb == NULL) {
+ tx_skb = dev_alloc_skb(64);
+ if (!tx_skb)
return -ENOMEM;
- skb_reserve(skb, LMP_MAX_HEADER);
- } else
- skb = userdata;
+ skb_reserve(tx_skb, LMP_MAX_HEADER);
+ }
/* Make room for MUX control header (3 bytes) */
- ASSERT(skb_headroom(skb) >= LMP_CONTROL_HEADER, return -1;);
- skb_push(skb, LMP_CONTROL_HEADER);
+ ASSERT(skb_headroom(tx_skb) >= LMP_CONTROL_HEADER, return -1;);
+ skb_push(tx_skb, LMP_CONTROL_HEADER);
self->dlsap_sel = dlsap_sel;
@@ -409,7 +413,8 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
lap = hashbin_lock_find(irlmp->links, saddr, NULL);
if (lap == NULL) {
IRDA_DEBUG(1, "%s(), Unable to find a usable link!\n", __FUNCTION__);
- return -EHOSTUNREACH;
+ ret = -EHOSTUNREACH;
+ goto err;
}
/* Check if LAP is disconnected or already connected */
@@ -423,13 +428,15 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
* Maybe we could give LAP a bit of help in this case.
*/
IRDA_DEBUG(0, "%s(), sorry, but I'm waiting for LAP to timeout!\n", __FUNCTION__);
- return -EAGAIN;
+ ret = -EAGAIN;
+ goto err;
}
/* LAP is already connected to a different node, and LAP
* can only talk to one node at a time */
IRDA_DEBUG(0, "%s(), sorry, but link is busy!\n", __FUNCTION__);
- return -EBUSY;
+ ret = -EBUSY;
+ goto err;
}
self->lap = lap;
@@ -456,9 +463,18 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
if (qos)
self->qos = *qos;
- irlmp_do_lsap_event(self, LM_CONNECT_REQUEST, skb);
+ irlmp_do_lsap_event(self, LM_CONNECT_REQUEST, tx_skb);
+
+ /* Drop reference count - see irlap_data_request(). */
+ dev_kfree_skb(tx_skb);
return 0;
+
+err:
+ /* Cleanup */
+ if(tx_skb)
+ dev_kfree_skb(tx_skb);
+ return ret;
}
/*
@@ -495,12 +511,13 @@ void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb)
/* Hide LMP_CONTROL_HEADER header from layer above */
skb_pull(skb, LMP_CONTROL_HEADER);
- if (self->notify.connect_indication)
+ if (self->notify.connect_indication) {
+ /* Don't forget to refcount it - see irlap_driver_rcv(). */
+ skb_get(skb);
self->notify.connect_indication(self->notify.instance, self,
&self->qos, max_seg_size,
max_header_size, skb);
- else
- dev_kfree_skb(skb);
+ }
}
/*
@@ -526,6 +543,9 @@ int irlmp_connect_response(struct lsap_cb *self, struct sk_buff *userdata)
irlmp_do_lsap_event(self, LM_CONNECT_RESPONSE, userdata);
+ /* Drop reference count - see irlap_data_request(). */
+ dev_kfree_skb(userdata);
+
return 0;
}
@@ -560,11 +580,12 @@ void irlmp_connect_confirm(struct lsap_cb *self, struct sk_buff *skb)
skb_pull(skb, LMP_CONTROL_HEADER);
if (self->notify.connect_confirm) {
+ /* Don't forget to refcount it - see irlap_driver_rcv() */
+ skb_get(skb);
self->notify.connect_confirm(self->notify.instance, self,
&self->qos, max_seg_size,
max_header_size, skb);
- } else
- dev_kfree_skb(skb);
+ }
}
/*
@@ -602,6 +623,7 @@ struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance)
memcpy(new, orig, sizeof(struct lsap_cb));
/* new->lap = orig->lap; => done in the memcpy() */
/* new->slsap_sel = orig->slsap_sel; => done in the memcpy() */
+ new->conn_skb = NULL;
spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags);
@@ -653,6 +675,9 @@ int irlmp_disconnect_request(struct lsap_cb *self, struct sk_buff *userdata)
*/
irlmp_do_lsap_event(self, LM_DISCONNECT_REQUEST, userdata);
+ /* Drop reference count - see irlap_data_request(). */
+ dev_kfree_skb(userdata);
+
/*
* Remove LSAP from list of connected LSAPs for the particular link
* and insert it into the list of unconnected LSAPs
@@ -686,7 +711,7 @@ int irlmp_disconnect_request(struct lsap_cb *self, struct sk_buff *userdata)
* LSAP is being closed!
*/
void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason,
- struct sk_buff *userdata)
+ struct sk_buff *skb)
{
struct lsap_cb *lsap;
@@ -703,8 +728,6 @@ void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason,
* Jean II */
if (! test_and_clear_bit(0, &self->connected)) {
IRDA_DEBUG(0, "%s(), already disconnected!\n", __FUNCTION__);
- if (userdata)
- dev_kfree_skb(userdata);
return;
}
@@ -730,13 +753,14 @@ void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason,
/*
* Inform service user
*/
- if (self->notify.disconnect_indication)
+ if (self->notify.disconnect_indication) {
+ /* Don't forget to refcount it - see irlap_driver_rcv(). */
+ if(skb)
+ skb_get(skb);
self->notify.disconnect_indication(self->notify.instance,
- self, reason, userdata);
- else {
+ self, reason, skb);
+ } else {
IRDA_DEBUG(0, "%s(), no handler\n", __FUNCTION__);
- if (userdata)
- dev_kfree_skb(userdata);
}
}
@@ -1047,17 +1071,31 @@ discovery_t *irlmp_get_discovery_response()
*
* Send some data to peer device
*
+ * Note on skb management :
+ * After calling the lower layers of the IrDA stack, we always
+ * kfree() the skb, which drop the reference count (and potentially
+ * destroy it).
+ * IrLMP and IrLAP may queue the packet, and in those cases will need
+ * to use skb_get() to keep it around.
+ * Jean II
*/
-int irlmp_data_request(struct lsap_cb *self, struct sk_buff *skb)
+int irlmp_data_request(struct lsap_cb *self, struct sk_buff *userdata)
{
+ int ret;
+
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
/* Make room for MUX header */
- ASSERT(skb_headroom(skb) >= LMP_HEADER, return -1;);
- skb_push(skb, LMP_HEADER);
+ ASSERT(skb_headroom(userdata) >= LMP_HEADER, return -1;);
+ skb_push(userdata, LMP_HEADER);
+
+ ret = irlmp_do_lsap_event(self, LM_DATA_REQUEST, userdata);
- return irlmp_do_lsap_event(self, LM_DATA_REQUEST, skb);
+ /* Drop reference count - see irlap_data_request(). */
+ dev_kfree_skb(userdata);
+
+ return ret;
}
/*
@@ -1071,26 +1109,34 @@ void irlmp_data_indication(struct lsap_cb *self, struct sk_buff *skb)
/* Hide LMP header from layer above */
skb_pull(skb, LMP_HEADER);
- if (self->notify.data_indication)
+ if (self->notify.data_indication) {
+ /* Don't forget to refcount it - see irlap_driver_rcv(). */
+ skb_get(skb);
self->notify.data_indication(self->notify.instance, self, skb);
- else
- dev_kfree_skb(skb);
+ }
}
/*
* Function irlmp_udata_request (self, skb)
*/
-int irlmp_udata_request(struct lsap_cb *self, struct sk_buff *skb)
+int irlmp_udata_request(struct lsap_cb *self, struct sk_buff *userdata)
{
+ int ret;
+
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
- ASSERT(skb != NULL, return -1;);
+ ASSERT(userdata != NULL, return -1;);
/* Make room for MUX header */
- ASSERT(skb_headroom(skb) >= LMP_HEADER, return -1;);
- skb_push(skb, LMP_HEADER);
+ ASSERT(skb_headroom(userdata) >= LMP_HEADER, return -1;);
+ skb_push(userdata, LMP_HEADER);
+
+ ret = irlmp_do_lsap_event(self, LM_UDATA_REQUEST, userdata);
- return irlmp_do_lsap_event(self, LM_UDATA_REQUEST, skb);
+ /* Drop reference count - see irlap_data_request(). */
+ dev_kfree_skb(userdata);
+
+ return ret;
}
/*
@@ -1110,51 +1156,57 @@ void irlmp_udata_indication(struct lsap_cb *self, struct sk_buff *skb)
/* Hide LMP header from layer above */
skb_pull(skb, LMP_HEADER);
- if (self->notify.udata_indication)
+ if (self->notify.udata_indication) {
+ /* Don't forget to refcount it - see irlap_driver_rcv(). */
+ skb_get(skb);
self->notify.udata_indication(self->notify.instance, self,
skb);
- else
- dev_kfree_skb(skb);
+ }
}
/*
* Function irlmp_connless_data_request (self, skb)
*/
#ifdef CONFIG_IRDA_ULTRA
-int irlmp_connless_data_request(struct lsap_cb *self, struct sk_buff *skb)
+int irlmp_connless_data_request(struct lsap_cb *self, struct sk_buff *userdata)
{
struct sk_buff *clone_skb;
struct lap_cb *lap;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
- ASSERT(skb != NULL, return -1;);
+ ASSERT(userdata != NULL, return -1;);
/* Make room for MUX and PID header */
- ASSERT(skb_headroom(skb) >= LMP_HEADER+LMP_PID_HEADER, return -1;);
+ ASSERT(skb_headroom(userdata) >= LMP_HEADER+LMP_PID_HEADER,
+ return -1;);
/* Insert protocol identifier */
- skb_push(skb, LMP_PID_HEADER);
- skb->data[0] = self->pid;
+ skb_push(userdata, LMP_PID_HEADER);
+ userdata->data[0] = self->pid;
/* Connectionless sockets must use 0x70 */
- skb_push(skb, LMP_HEADER);
- skb->data[0] = skb->data[1] = LSAP_CONNLESS;
+ skb_push(userdata, LMP_HEADER);
+ userdata->data[0] = userdata->data[1] = LSAP_CONNLESS;
/* Try to send Connectionless packets out on all links */
lap = (struct lap_cb *) hashbin_get_first(irlmp->links);
while (lap != NULL) {
ASSERT(lap->magic == LMP_LAP_MAGIC, return -1;);
- clone_skb = skb_clone(skb, GFP_ATOMIC);
- if (!clone_skb)
+ clone_skb = skb_clone(userdata, GFP_ATOMIC);
+ if (!clone_skb) {
+ dev_kfree_skb(userdata);
return -ENOMEM;
+ }
irlap_unitdata_request(lap->irlap, clone_skb);
+ /* irlap_unitdata_request() don't increase refcount,
+ * so no dev_kfree_skb() - Jean II */
lap = (struct lap_cb *) hashbin_get_next(irlmp->links);
}
- dev_kfree_skb(skb);
+ dev_kfree_skb(userdata);
return 0;
}
@@ -1178,11 +1230,12 @@ void irlmp_connless_data_indication(struct lsap_cb *self, struct sk_buff *skb)
/* Hide LMP and PID header from layer above */
skb_pull(skb, LMP_HEADER+LMP_PID_HEADER);
- if (self->notify.udata_indication)
+ if (self->notify.udata_indication) {
+ /* Don't forget to refcount it - see irlap_driver_rcv(). */
+ skb_get(skb);
self->notify.udata_indication(self->notify.instance, self,
skb);
- else
- dev_kfree_skb(skb);
+ }
}
#endif /* CONFIG_IRDA_ULTRA */
diff --git a/net/irda/irlmp_event.c b/net/irda/irlmp_event.c
index 0d976b6662c6..91f8ad67bc33 100644
--- a/net/irda/irlmp_event.c
+++ b/net/irda/irlmp_event.c
@@ -11,7 +11,7 @@
*
* Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>,
* All Rights Reserved.
- * Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
+ * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -295,8 +295,6 @@ static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event,
default:
IRDA_DEBUG(0, "%s(), Unknown event %s\n",
__FUNCTION__, irlmp_event[event]);
- if (skb)
- dev_kfree_skb(skb);
break;
}
}
@@ -373,8 +371,6 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
default:
IRDA_DEBUG(0, "%s(), Unknown event %s\n",
__FUNCTION__, irlmp_event[event]);
- if (skb)
- dev_kfree_skb(skb);
break;
}
}
@@ -468,8 +464,6 @@ static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
default:
IRDA_DEBUG(0, "%s(), Unknown event %s\n",
__FUNCTION__, irlmp_event[event]);
- if (skb)
- dev_kfree_skb(skb);
break;
}
}
@@ -499,6 +493,9 @@ static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event,
switch (event) {
#ifdef CONFIG_IRDA_ULTRA
case LM_UDATA_INDICATION:
+ /* This is most bizzare. Those packets are aka unreliable
+ * connected, aka IrLPT or SOCK_DGRAM/IRDAPROTO_UNITDATA.
+ * Why do we pass them as Ultra ??? Jean II */
irlmp_connless_data_indication(self, skb);
break;
#endif /* CONFIG_IRDA_ULTRA */
@@ -510,6 +507,8 @@ static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event,
__FUNCTION__);
return -EBUSY;
}
+ /* Don't forget to refcount it (see irlmp_connect_request()) */
+ skb_get(skb);
self->conn_skb = skb;
irlmp_next_lsap_state(self, LSAP_SETUP_PEND);
@@ -525,6 +524,8 @@ static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event,
__FUNCTION__);
return -EBUSY;
}
+ /* Don't forget to refcount it (see irlap_driver_rcv()) */
+ skb_get(skb);
self->conn_skb = skb;
irlmp_next_lsap_state(self, LSAP_CONNECT_PEND);
@@ -547,8 +548,6 @@ static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event,
default:
IRDA_DEBUG(1, "%s(), Unknown event %s on LSAP %#02x\n",
__FUNCTION__, irlmp_event[event], self->slsap_sel);
- if (skb)
- dev_kfree_skb(skb);
break;
}
return ret;
@@ -606,8 +605,6 @@ static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event,
default:
IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n",
__FUNCTION__, irlmp_event[event], self->slsap_sel);
- if (skb)
- dev_kfree_skb(skb);
break;
}
return ret;
@@ -622,6 +619,7 @@ static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event,
static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
+ struct sk_buff *tx_skb;
int ret = 0;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
@@ -647,10 +645,12 @@ static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event,
IRDA_DEBUG(4, "%s(), LS_CONNECT_CONFIRM\n", __FUNCTION__);
irlmp_next_lsap_state(self, LSAP_CONNECT);
- skb = self->conn_skb;
+ tx_skb = self->conn_skb;
self->conn_skb = NULL;
- irlmp_connect_indication(self, skb);
+ irlmp_connect_indication(self, tx_skb);
+ /* Drop reference count - see irlmp_connect_indication(). */
+ dev_kfree_skb(tx_skb);
break;
case LM_WATCHDOG_TIMEOUT:
/* Will happen in some rare cases because of a race condition.
@@ -668,8 +668,6 @@ static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event,
default:
IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n",
__FUNCTION__, irlmp_event[event], self->slsap_sel);
- if (skb)
- dev_kfree_skb(skb);
break;
}
return ret;
@@ -759,8 +757,6 @@ static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event,
default:
IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n",
__FUNCTION__, irlmp_event[event], self->slsap_sel);
- if (skb)
- dev_kfree_skb(skb);
break;
}
return ret;
@@ -832,8 +828,6 @@ static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event,
default:
IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n",
__FUNCTION__, irlmp_event[event], self->slsap_sel);
- if (skb)
- dev_kfree_skb(skb);
break;
}
return ret;
@@ -850,6 +844,7 @@ static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event,
static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
+ struct sk_buff *tx_skb;
LM_REASON reason;
int ret = 0;
@@ -862,11 +857,13 @@ static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event,
case LM_LAP_CONNECT_CONFIRM:
ASSERT(self->conn_skb != NULL, return -1;);
- skb = self->conn_skb;
+ tx_skb = self->conn_skb;
self->conn_skb = NULL;
irlmp_send_lcf_pdu(self->lap, self->dlsap_sel,
- self->slsap_sel, CONNECT_CMD, skb);
+ self->slsap_sel, CONNECT_CMD, tx_skb);
+ /* Drop reference count - see irlap_data_request(). */
+ dev_kfree_skb(tx_skb);
irlmp_next_lsap_state(self, LSAP_SETUP);
break;
@@ -891,8 +888,6 @@ static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event,
default:
IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n",
__FUNCTION__, irlmp_event[event], self->slsap_sel);
- if (skb)
- dev_kfree_skb(skb);
break;
}
return ret;
diff --git a/net/irda/irlmp_frame.c b/net/irda/irlmp_frame.c
index 616fd22f01d1..c00ccfd31022 100644
--- a/net/irda/irlmp_frame.c
+++ b/net/irda/irlmp_frame.c
@@ -11,7 +11,7 @@
*
* Copyright (c) 1998-1999 Dag Brattli <dagb@cs.uit.no>
* All Rights Reserved.
- * Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
+ * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -144,7 +144,6 @@ void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb,
} else {
IRDA_DEBUG(2, "%s(), received data frame\n", __FUNCTION__);
}
- dev_kfree_skb(skb);
return;
}
@@ -168,16 +167,13 @@ void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb,
break;
case ACCESSMODE_CMD:
IRDA_DEBUG(0, "Access mode cmd not implemented!\n");
- dev_kfree_skb(skb);
break;
case ACCESSMODE_CNF:
IRDA_DEBUG(0, "Access mode cnf not implemented!\n");
- dev_kfree_skb(skb);
break;
default:
IRDA_DEBUG(0, "%s(), Unknown control frame %02x\n",
__FUNCTION__, fp[2]);
- dev_kfree_skb(skb);
break;
}
} else if (unreliable) {
@@ -230,16 +226,12 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
if (pid & 0x80) {
IRDA_DEBUG(0, "%s(), extension in PID not supp!\n",
__FUNCTION__);
- dev_kfree_skb(skb);
-
return;
}
/* Check if frame is addressed to the connectionless LSAP */
if ((slsap_sel != LSAP_CONNLESS) || (dlsap_sel != LSAP_CONNLESS)) {
IRDA_DEBUG(0, "%s(), dropping frame!\n", __FUNCTION__);
- dev_kfree_skb(skb);
-
return;
}
@@ -264,7 +256,6 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
irlmp_connless_data_indication(lsap, skb);
else {
IRDA_DEBUG(0, "%s(), found no matching LSAP!\n", __FUNCTION__);
- dev_kfree_skb(skb);
}
}
#endif /* CONFIG_IRDA_ULTRA */
@@ -278,7 +269,7 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
void irlmp_link_disconnect_indication(struct lap_cb *lap,
struct irlap_cb *irlap,
LAP_REASON reason,
- struct sk_buff *userdata)
+ struct sk_buff *skb)
{
IRDA_DEBUG(2, "%s()\n", __FUNCTION__);
@@ -288,9 +279,7 @@ void irlmp_link_disconnect_indication(struct lap_cb *lap,
lap->reason = reason;
lap->daddr = DEV_ADDR_ANY;
- /* FIXME: must do something with the userdata if any */
- if (userdata)
- dev_kfree_skb(userdata);
+ /* FIXME: must do something with the skb if any */
/*
* Inform station state machine
@@ -327,7 +316,7 @@ void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr,
*
*/
void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos,
- struct sk_buff *userdata)
+ struct sk_buff *skb)
{
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
@@ -335,9 +324,7 @@ void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos,
ASSERT(self->magic == LMP_LAP_MAGIC, return;);
ASSERT(qos != NULL, return;);
- /* Don't need use the userdata for now */
- if (userdata)
- dev_kfree_skb(userdata);
+ /* Don't need use the skb for now */
/* Copy QoS settings for this session */
self->qos = qos;
diff --git a/net/irda/irnet/irnet.h b/net/irda/irnet/irnet.h
index 37aa3d7e4b9f..0ed25a123047 100644
--- a/net/irda/irnet/irnet.h
+++ b/net/irda/irnet/irnet.h
@@ -229,6 +229,11 @@
* v14 - 20.2.03 - Jean II
* o Add discovery hint bits in the control channel.
* o Remove obsolete MOD_INC/DEC_USE_COUNT in favor of .owner
+ *
+ * v15 - 7.4.03 - Jean II
+ * o Replace spin_lock_irqsave() with spin_lock_bh() so that we can
+ * use ppp_unit_number(). It's probably also better overall...
+ * o Disable call to ppp_unregister_channel(), because we can't do it.
*/
/***************************** INCLUDES *****************************/
@@ -276,6 +281,7 @@
#undef CONNECT_INDIC_KICK /* Might mess IrDA, not needed */
#undef FAIL_SEND_DISCONNECT /* Might mess IrDA, not needed */
#undef PASS_CONNECT_PACKETS /* Not needed ? Safe */
+#undef MISSING_PPP_API /* Stuff I wish I could do */
/* PPP side of the business */
#define BLOCK_WHEN_CONNECT /* Block packets when connecting */
diff --git a/net/irda/irnet/irnet_irda.c b/net/irda/irnet/irnet_irda.c
index 517b76e3281e..83966549d07f 100644
--- a/net/irda/irnet/irnet_irda.c
+++ b/net/irda/irnet/irnet_irda.c
@@ -31,7 +31,6 @@ irnet_post_event(irnet_socket * ap,
char * name,
__u16 hints)
{
- unsigned long flags; /* For spinlock */
int index; /* In the log */
DENTER(CTRL_TRACE, "(ap=0x%X, event=%d, daddr=%08x, name=``%s'')\n",
@@ -41,7 +40,7 @@ irnet_post_event(irnet_socket * ap,
* Note : as we are the only event producer, we only need to exclude
* ourself when touching the log, which is nice and easy.
*/
- spin_lock_irqsave(&irnet_events.spinlock, flags);
+ spin_lock_bh(&irnet_events.spinlock);
/* Copy the event in the log */
index = irnet_events.index;
@@ -69,7 +68,7 @@ irnet_post_event(irnet_socket * ap,
DEBUG(CTRL_INFO, "New event index is %d\n", irnet_events.index);
/* Spin lock end */
- spin_unlock_irqrestore(&irnet_events.spinlock, flags);
+ spin_unlock_bh(&irnet_events.spinlock);
/* Now : wake up everybody waiting for events... */
wake_up_interruptible_all(&irnet_events.rwait);
@@ -536,10 +535,9 @@ irda_irnet_connect(irnet_socket * self)
* Can't re-insert (MUST remove first) so check for that... */
if((irnet_server.running) && (self->q.q_next == NULL))
{
- unsigned long flags;
- spin_lock_irqsave(&irnet_server.spinlock, flags);
+ spin_lock_bh(&irnet_server.spinlock);
hashbin_insert(irnet_server.list, (irda_queue_t *) self, 0, self->rname);
- spin_unlock_irqrestore(&irnet_server.spinlock, flags);
+ spin_unlock_bh(&irnet_server.spinlock);
DEBUG(IRDA_SOCK_INFO, "Inserted ``%s'' in hashbin...\n", self->rname);
}
@@ -596,12 +594,11 @@ irda_irnet_destroy(irnet_socket * self)
if((irnet_server.running) && (self->q.q_next != NULL))
{
struct irnet_socket * entry;
- unsigned long flags;
DEBUG(IRDA_SOCK_INFO, "Removing from hash..\n");
- spin_lock_irqsave(&irnet_server.spinlock, flags);
+ spin_lock_bh(&irnet_server.spinlock);
entry = hashbin_remove_this(irnet_server.list, (irda_queue_t *) self);
self->q.q_next = NULL;
- spin_unlock_irqrestore(&irnet_server.spinlock, flags);
+ spin_unlock_bh(&irnet_server.spinlock);
DASSERT(entry == self, , IRDA_SOCK_ERROR, "Can't remove from hash.\n");
}
@@ -723,7 +720,6 @@ static inline irnet_socket *
irnet_find_socket(irnet_socket * self)
{
irnet_socket * new = (irnet_socket *) NULL;
- unsigned long flags;
int err;
DENTER(IRDA_SERV_TRACE, "(self=0x%X)\n", (unsigned int) self);
@@ -736,7 +732,7 @@ irnet_find_socket(irnet_socket * self)
err = irnet_daddr_to_dname(self);
/* Protect access to the instance list */
- spin_lock_irqsave(&irnet_server.spinlock, flags);
+ spin_lock_bh(&irnet_server.spinlock);
/* So now, try to get an socket having specifically
* requested that nickname */
@@ -790,7 +786,7 @@ irnet_find_socket(irnet_socket * self)
}
/* Spin lock end */
- spin_unlock_irqrestore(&irnet_server.spinlock, flags);
+ spin_unlock_bh(&irnet_server.spinlock);
DEXIT(IRDA_SERV_TRACE, " - new = 0x%X\n", (unsigned int) new);
return new;
@@ -1135,10 +1131,15 @@ irnet_disconnect_indication(void * instance,
{
if(test_open)
{
+#ifdef MISSING_PPP_API
+ /* ppp_unregister_channel() wants a user context, which we
+ * are guaranteed to NOT have here. What are we supposed
+ * to do here ? Jean II */
/* If we were connected, cleanup & close the PPP channel,
* which will kill pppd (hangup) and the rest */
ppp_unregister_channel(&self->chan);
self->ppp_open = 0;
+#endif
}
else
{
@@ -1711,7 +1712,6 @@ irnet_proc_read(char * buf,
{
irnet_socket * self;
char * state;
- unsigned long flags;
int i = 0;
len = 0;
@@ -1728,7 +1728,7 @@ irnet_proc_read(char * buf,
return len;
/* Protect access to the instance list */
- spin_lock_irqsave(&irnet_server.spinlock, flags);
+ spin_lock_bh(&irnet_server.spinlock);
/* Get the sockets one by one... */
self = (irnet_socket *) hashbin_get_first(irnet_server.list);
@@ -1780,7 +1780,7 @@ irnet_proc_read(char * buf,
}
/* Spin lock end */
- spin_unlock_irqrestore(&irnet_server.spinlock, flags);
+ spin_unlock_bh(&irnet_server.spinlock);
return len;
}
diff --git a/net/irda/irnet/irnet_ppp.c b/net/irda/irnet/irnet_ppp.c
index 6f5125b289cf..187871a4208a 100644
--- a/net/irda/irnet/irnet_ppp.c
+++ b/net/irda/irnet/irnet_ppp.c
@@ -927,7 +927,7 @@ ppp_irnet_send(struct ppp_channel * chan,
* Jean II
*/
DERROR(PPP_ERROR, "IrTTP doesn't like this packet !!! (0x%X)\n", ret);
- dev_kfree_skb(skb);
+ /* irttp_data_request already free the packet */
}
DEXIT(PPP_TRACE, "\n");
diff --git a/net/irda/irttp.c b/net/irda/irttp.c
index 5ddded65c67d..bb51edc7d99b 100644
--- a/net/irda/irttp.c
+++ b/net/irda/irttp.c
@@ -11,7 +11,7 @@
*
* Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>,
* All Rights Reserved.
- * Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
+ * Copyright (c) 2000-2003 Jean Tourrilhes <jt@hpl.hp.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -259,11 +259,17 @@ static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self)
dev_kfree_skb(frag);
}
- IRDA_DEBUG(2, "%s(), frame len=%d\n", __FUNCTION__, n);
- IRDA_DEBUG(2, "%s(), rx_sdu_size=%d\n", __FUNCTION__,
- self->rx_sdu_size);
- ASSERT(n <= self->rx_sdu_size, return NULL;);
+ IRDA_DEBUG(2,
+ "%s(), frame len=%d, rx_sdu_size=%d, rx_max_sdu_size=%d\n",
+ __FUNCTION__, n, self->rx_sdu_size, self->rx_max_sdu_size);
+ /* Note : irttp_run_rx_queue() calculate self->rx_sdu_size
+ * by summing the size of all fragments, so we should always
+ * have n == self->rx_sdu_size, except in cases where we
+ * droped the last fragment (when self->rx_sdu_size exceed
+ * self->rx_max_sdu_size), where n < self->rx_sdu_size.
+ * Jean II */
+ ASSERT(n <= self->rx_sdu_size, n = self->rx_sdu_size;);
/* Set the new length */
skb_trim(skb, n);
@@ -537,19 +543,23 @@ int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb)
if ((skb->len == 0) || (!self->connected)) {
IRDA_DEBUG(1, "%s(), No data, or not connected\n",
__FUNCTION__);
- return -1;
+ goto err;
}
if (skb->len > self->max_seg_size) {
IRDA_DEBUG(1, "%s(), UData is to large for IrLAP!\n",
__FUNCTION__);
- return -1;
+ goto err;
}
irlmp_udata_request(self->lsap, skb);
self->stats.tx_packets++;
return 0;
+
+err:
+ dev_kfree_skb(skb);
+ return -1;
}
/*
@@ -561,6 +571,7 @@ int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb)
int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb)
{
__u8 *frame;
+ int ret;
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
@@ -572,7 +583,8 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb)
/* Check that nothing bad happens */
if ((skb->len == 0) || (!self->connected)) {
WARNING("%s: No data, or not connected\n", __FUNCTION__);
- return -ENOTCONN;
+ ret = -ENOTCONN;
+ goto err;
}
/*
@@ -582,7 +594,8 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb)
if ((self->tx_max_sdu_size == 0) && (skb->len > self->max_seg_size)) {
ERROR("%s: SAR disabled, and data is to large for IrLAP!\n",
__FUNCTION__);
- return -EMSGSIZE;
+ ret = -EMSGSIZE;
+ goto err;
}
/*
@@ -595,7 +608,8 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb)
{
ERROR("%s: SAR enabled, but data is larger than TxMaxSduSize!\n",
__FUNCTION__);
- return -EMSGSIZE;
+ ret = -EMSGSIZE;
+ goto err;
}
/*
* Check if transmit queue is full
@@ -607,8 +621,9 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb)
irttp_run_tx_queue(self);
/* Drop packet. This error code should trigger the caller
- * to requeue the packet in the client code - Jean II */
- return -ENOBUFS;
+ * to resend the data in the client code - Jean II */
+ ret = -ENOBUFS;
+ goto err;
}
/* Queue frame, or queue frame segments */
@@ -651,6 +666,10 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb)
irttp_run_tx_queue(self);
return 0;
+
+err:
+ dev_kfree_skb(skb);
+ return ret;
}
/*
@@ -822,6 +841,7 @@ static int irttp_udata_indication(void *instance, void *sap,
struct sk_buff *skb)
{
struct tsap_cb *self;
+ int err;
IRDA_DEBUG(4, "%s()\n", __FUNCTION__);
@@ -831,14 +851,19 @@ static int irttp_udata_indication(void *instance, void *sap,
ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
ASSERT(skb != NULL, return -1;);
- /* Just pass data to layer above */
- if (self->notify.udata_indication)
- self->notify.udata_indication(self->notify.instance, self,skb);
- else
- dev_kfree_skb(skb);
-
self->stats.rx_packets++;
+ /* Just pass data to layer above */
+ if (self->notify.udata_indication) {
+ err = self->notify.udata_indication(self->notify.instance,
+ self,skb);
+ /* Same comment as in irttp_do_data_indication() */
+ if (err != -ENOMEM)
+ return 0;
+ }
+ /* Either no handler, or -ENOMEM */
+ dev_kfree_skb(skb);
+
return 0;
}
@@ -1040,7 +1065,7 @@ int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel,
struct qos_info *qos, __u32 max_sdu_size,
struct sk_buff *userdata)
{
- struct sk_buff *skb;
+ struct sk_buff *tx_skb;
__u8 *frame;
__u8 n;
@@ -1049,19 +1074,22 @@ int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel,
ASSERT(self != NULL, return -EBADR;);
ASSERT(self->magic == TTP_TSAP_MAGIC, return -EBADR;);
- if (self->connected)
+ if (self->connected) {
+ if(userdata)
+ dev_kfree_skb(userdata);
return -EISCONN;
+ }
/* Any userdata supplied? */
if (userdata == NULL) {
- skb = dev_alloc_skb(64);
- if (!skb)
+ tx_skb = dev_alloc_skb(64);
+ if (!tx_skb)
return -ENOMEM;
/* Reserve space for MUX_CONTROL and LAP header */
- skb_reserve(skb, TTP_MAX_HEADER);
+ skb_reserve(tx_skb, TTP_MAX_HEADER);
} else {
- skb = userdata;
+ tx_skb = userdata;
/*
* Check that the client has reserved enough space for
* headers
@@ -1094,11 +1122,11 @@ int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel,
/* SAR enabled? */
if (max_sdu_size > 0) {
- ASSERT(skb_headroom(skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER),
+ ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER),
return -1;);
/* Insert SAR parameters */
- frame = skb_push(skb, TTP_HEADER+TTP_SAR_HEADER);
+ frame = skb_push(tx_skb, TTP_HEADER+TTP_SAR_HEADER);
frame[0] = TTP_PARAMETERS | n;
frame[1] = 0x04; /* Length */
@@ -1109,7 +1137,7 @@ int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel,
(__u16 *)(frame+4));
} else {
/* Insert plain TTP header */
- frame = skb_push(skb, TTP_HEADER);
+ frame = skb_push(tx_skb, TTP_HEADER);
/* Insert initial credit in frame */
frame[0] = n & 0x7f;
@@ -1117,7 +1145,7 @@ int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel,
/* Connect with IrLMP. No QoS parameters for now */
return irlmp_connect_request(self->lsap, dtsap_sel, saddr, daddr, qos,
- skb);
+ tx_skb);
}
/*
@@ -1201,7 +1229,8 @@ static void irttp_connect_confirm(void *instance, void *sap,
self->notify.connect_confirm(self->notify.instance, self, qos,
self->tx_max_sdu_size,
self->max_header_size, skb);
- }
+ } else
+ dev_kfree_skb(skb);
}
/*
@@ -1286,7 +1315,7 @@ void irttp_connect_indication(void *instance, void *sap, struct qos_info *qos,
int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size,
struct sk_buff *userdata)
{
- struct sk_buff *skb;
+ struct sk_buff *tx_skb;
__u8 *frame;
int ret;
__u8 n;
@@ -1299,19 +1328,19 @@ int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size,
/* Any userdata supplied? */
if (userdata == NULL) {
- skb = dev_alloc_skb(64);
- if (!skb)
+ tx_skb = dev_alloc_skb(64);
+ if (!tx_skb)
return -ENOMEM;
/* Reserve space for MUX_CONTROL and LAP header */
- skb_reserve(skb, TTP_MAX_HEADER);
+ skb_reserve(tx_skb, TTP_MAX_HEADER);
} else {
- skb = userdata;
+ tx_skb = userdata;
/*
* Check that the client has reserved enough space for
* headers
*/
- ASSERT(skb_headroom(skb) >= TTP_MAX_HEADER, return -1;);
+ ASSERT(skb_headroom(tx_skb) >= TTP_MAX_HEADER, return -1;);
}
self->avail_credit = 0;
@@ -1333,11 +1362,11 @@ int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size,
/* SAR enabled? */
if (max_sdu_size > 0) {
- ASSERT(skb_headroom(skb) >= (TTP_MAX_HEADER+TTP_SAR_HEADER),
+ ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER+TTP_SAR_HEADER),
return -1;);
/* Insert TTP header with SAR parameters */
- frame = skb_push(skb, TTP_HEADER+TTP_SAR_HEADER);
+ frame = skb_push(tx_skb, TTP_HEADER+TTP_SAR_HEADER);
frame[0] = TTP_PARAMETERS | n;
frame[1] = 0x04; /* Length */
@@ -1352,12 +1381,12 @@ int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size,
(__u16 *)(frame+4));
} else {
/* Insert TTP header */
- frame = skb_push(skb, TTP_HEADER);
+ frame = skb_push(tx_skb, TTP_HEADER);
frame[0] = n & 0x7f;
}
- ret = irlmp_connect_response(self->lsap, skb);
+ ret = irlmp_connect_response(self->lsap, tx_skb);
return ret;
}
@@ -1423,7 +1452,6 @@ struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance)
int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata,
int priority)
{
- struct sk_buff *skb;
int ret;
ASSERT(self != NULL, return -1;);
@@ -1488,16 +1516,17 @@ int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata,
self->connected = FALSE;
if (!userdata) {
- skb = dev_alloc_skb(64);
- if (!skb)
+ struct sk_buff *tx_skb;
+ tx_skb = dev_alloc_skb(64);
+ if (!tx_skb)
return -ENOMEM;
/*
* Reserve space for MUX and LAP header
*/
- skb_reserve(skb, TTP_MAX_HEADER);
+ skb_reserve(tx_skb, TTP_MAX_HEADER);
- userdata = skb;
+ userdata = tx_skb;
}
ret = irlmp_disconnect_request(self->lsap, userdata);
@@ -1556,7 +1585,7 @@ void irttp_disconnect_indication(void *instance, void *sap, LM_REASON reason,
/*
* Function irttp_do_data_indication (self, skb)
*
- * Try to deliver reassebled skb to layer above, and requeue it if that
+ * Try to deliver reassembled skb to layer above, and requeue it if that
* for some reason should fail. We mark rx sdu as busy to apply back
* pressure is necessary.
*/