# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.1184 -> 1.1191 # drivers/net/irda/irtty-sir.c 1.6 -> 1.7 # net/irda/ircomm/ircomm_core.c 1.11 -> 1.12 # drivers/net/irda/nsc-ircc.c 1.22 -> 1.23 # drivers/net/irda/smc-ircc.c 1.18 -> 1.19 # net/irda/ircomm/ircomm_lmp.c 1.7 -> 1.8 # net/irda/irnet/irnet_irda.c 1.17 -> 1.18 # net/irda/irlmp_event.c 1.17 -> 1.18 # net/irda/irlap_frame.c 1.11 -> 1.13 # net/irda/irlan/irlan_eth.c 1.5 -> 1.6 # include/net/irda/iriap.h 1.2 -> 1.3 # net/irda/iriap.c 1.14 -> 1.15 # net/irda/af_irda.c 1.42 -> 1.43 # net/irda/ircomm/ircomm_tty.c 1.19 -> 1.20 # net/irda/ircomm/ircomm_event.c 1.3 -> 1.4 # drivers/net/irda/w83977af_ir.c 1.18 -> 1.19 # drivers/net/irda/ali-ircc.c 1.18 -> 1.19 # drivers/net/irda/donauboe.c 1.7 -> 1.8 # drivers/net/irda/Makefile 1.14 -> 1.15 # net/irda/irttp.c 1.13 -> 1.14 # net/irda/irlap_event.c 1.21 -> 1.22 # include/net/irda/irport.h 1.3 -> 1.4 # net/irda/irlmp_frame.c 1.9 -> 1.10 # drivers/net/irda/irport.c 1.17 -> 1.18 # net/irda/irnet/irnet.h 1.17 -> 1.18 # net/irda/irnet/irnet_ppp.c 1.12 -> 1.13 # drivers/net/irda/Kconfig 1.3 -> 1.4 # drivers/net/irda/sir_kthread.c 1.5 -> 1.6 # net/irda/iriap_event.c 1.3 -> 1.4 # net/irda/ircomm/ircomm_tty_attach.c 1.10 -> 1.11 # net/irda/irlmp.c 1.24 -> 1.25 # net/irda/irlap.c 1.19 -> 1.20 # net/irda/ircomm/ircomm_ttp.c 1.4 -> 1.5 # include/net/irda/irlmp_event.h 1.3 -> 1.4 # (new) -> 1.1 drivers/net/irda/smsc-sio.h # (new) -> 1.1 drivers/net/irda/smsc-ircc2.h # (new) -> 1.1 drivers/net/irda/smsc-ircc2.c # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 03/05/20 jt@bougret.hpl.hp.com 1.1185 # [PATCH] IrDA skb leak fixes # # ir259_skb_get-7.diff : # # o [CORRECT] Fix skb leaks in IrDA state machines # o [CORRECT] Fix skb leaks in connect/request error paths # o [FEATURE] Fix skb leaks in ASSERT # o [FEATURE] Simplify & document skb handling throughout the stack # o [FEATURE] other minor cleanups # -------------------------------------------- # 03/05/20 jt@bougret.hpl.hp.com 1.1186 # [PATCH] IrNET crasher # # ir257_irnet_bh.diff : # o [CRITICA] Replace spin_lock_irqsave() with spin_lock_bh() # to be compatible with ppp_generic locking # o [CRITICA] Disable call to ppp_unregister_channel() # -------------------------------------------- # 03/05/20 jt@bougret.hpl.hp.com 1.1187 # [PATCH] IrLAP address fix # # ir257_caddr_mask.diff : # # o [CORRECT] ignore the C/R bit in the LAP connection address. # -------------------------------------------- # 03/05/20 jt@bougret.hpl.hp.com 1.1188 # [PATCH] owner in irtty-sir # # ir259_sir_kthread_Morton-2.diff : # o [CORRECT] fix module ownership in irtty-sir # o [FEATURE] important comment in sir_kthread # -------------------------------------------- # 03/05/20 jt@bougret.hpl.hp.com 1.1189 # [PATCH] Various IrDA drivers # # ir259_trans_start-4.diff : # o [CORRECT] Properly initialise dev->trans_start in various drivers # o [CRITICA] Unregister power management at unload in smc-ircc # o [CORRECT] fix module ownership in smc-ircc # -------------------------------------------- # 03/05/20 jt@bougret.hpl.hp.com 1.1190 # [PATCH] irport fixes # # ir259_irport-6.diff : # o [CORRECT] fix module ownership in irport # o [CORRECT] Properly initialise dev->trans_start in irport # o [CORRECT] Add delay to drain the Tx fifo to avoid corrupting # last outgoing Tx byte when changing speed # o [FEATURE] Safer locking around speed change in irport_hard_xmit() # o [FEATURE] Enforce half duplex operation in interrupt handler # o [FEATURE] Optimise interrupt handler for latency and I/O ops # o [FEATURE] Optimise Tx path in irport_write() # o [FEATURE] Add ZeroCopy Rx # o [FEATURE] Better debugging in watchdog timeout # o [FEATURE] Various other cleanups and comments # -------------------------------------------- # 03/05/20 jt@bougret.hpl.hp.com 1.1191 # [PATCH] smsc-ircc2 driver # # ir259_smsc-ircc2-6.diff : # # o [FEATURE] New driver smsc-ircc2, improved FIR support # -------------------------------------------- # diff -Nru a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig --- a/drivers/net/irda/Kconfig Tue May 20 23:05:33 2003 +++ b/drivers/net/irda/Kconfig Tue May 20 23:05:33 2003 @@ -289,15 +289,28 @@ . 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 . 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 + . 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 + . The module will be + called smsc-ircc2.o. config ALI_FIR tristate "ALi M5123 FIR (EXPERIMENTAL)" diff -Nru a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile --- a/drivers/net/irda/Makefile Tue May 20 23:05:33 2003 +++ b/drivers/net/irda/Makefile Tue May 20 23:05:33 2003 @@ -15,13 +15,14 @@ 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 -Nru a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c --- a/drivers/net/irda/ali-ircc.c Tue May 20 23:05:33 2003 +++ b/drivers/net/irda/ali-ircc.c Tue May 20 23:05:33 2003 @@ -1451,6 +1451,7 @@ /* 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 @@ /* 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 @@ /* 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 @@ /* 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 -Nru a/drivers/net/irda/donauboe.c b/drivers/net/irda/donauboe.c --- a/drivers/net/irda/donauboe.c Tue May 20 23:05:33 2003 +++ b/drivers/net/irda/donauboe.c Tue May 20 23:05:33 2003 @@ -1051,7 +1051,9 @@ 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 -Nru a/drivers/net/irda/irport.c b/drivers/net/irda/irport.c --- a/drivers/net/irda/irport.c Tue May 20 23:05:33 2003 +++ b/drivers/net/irda/irport.c Tue May 20 23:05:33 2003 @@ -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 #include #include +#include #include #include @@ -72,14 +74,14 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ } /* + * 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 @@ { 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 @@ 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 @@ 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 @@ 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 @@ } /* - * 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 @@ { 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 @@ 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 @@ 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 @@ 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 @@ } /* + * 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 @@ 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); - - 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 */ - 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) + /* 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); + } + + /* A bit hackish, but working as expected... Jean II */ + if(lsr & (UART_LSR_THRE | UART_LSR_TEMT | UART_LSR_DR)) + handled = 1; + + /* 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 @@ 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 @@ } 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 @@ */ 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 @@ 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); } @@ -997,6 +1087,12 @@ ret = -EPERM; 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); diff -Nru a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c --- a/drivers/net/irda/irtty-sir.c Tue May 20 23:05:33 2003 +++ b/drivers/net/irda/irtty-sir.c Tue May 20 23:05:33 2003 @@ -504,10 +504,7 @@ 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 @@ out_put: sirdev_put_instance(dev); out: - MOD_DEC_USE_COUNT; return ret; } @@ -614,8 +610,6 @@ tty->driver->stop(tty); kfree(priv); - - MOD_DEC_USE_COUNT; } /* ------------------------------------------------------- */ @@ -633,6 +627,7 @@ .receive_buf = irtty_receive_buf, .receive_room = irtty_receive_room, .write_wakeup = irtty_write_wakeup, + .owner = THIS_MODULE, }; /* ------------------------------------------------------- */ diff -Nru a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c --- a/drivers/net/irda/nsc-ircc.c Tue May 20 23:05:33 2003 +++ b/drivers/net/irda/nsc-ircc.c Tue May 20 23:05:33 2003 @@ -1096,6 +1096,7 @@ * 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 @@ /* 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 @@ * 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 @@ /* Restore bank register */ outb(bank, iobase+BSR); + dev->trans_start = jiffies; spin_unlock_irqrestore(&self->lock, flags); dev_kfree_skb(skb); diff -Nru a/drivers/net/irda/sir_kthread.c b/drivers/net/irda/sir_kthread.c --- a/drivers/net/irda/sir_kthread.c Tue May 20 23:05:33 2003 +++ b/drivers/net/irda/sir_kthread.c Tue May 20 23:05:33 2003 @@ -151,6 +151,13 @@ 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 -Nru a/drivers/net/irda/smc-ircc.c b/drivers/net/irda/smc-ircc.c --- a/drivers/net/irda/smc-ircc.c Tue May 20 23:05:33 2003 +++ b/drivers/net/irda/smc-ircc.c Tue May 20 23:05:33 2003 @@ -529,6 +529,9 @@ 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 @@ /* 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 @@ /* 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 @@ WARNING("%s(), unable to allocate DMA=%d\n", __FUNCTION__, self->io->dma); return -EAGAIN; } - - MOD_INC_USE_COUNT; return 0; } @@ -1124,8 +1127,6 @@ free_dma(self->io->dma); - MOD_DEC_USE_COUNT; - return 0; } @@ -1186,6 +1187,9 @@ ASSERT(self != NULL, return -1;); iobase = self->irport->io.fir_base; + + if (self->pmdev) + pm_unregister(self->pmdev); /* This will destroy irport */ irport_close(self->irport); diff -Nru a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/irda/smsc-ircc2.c Tue May 20 23:05:33 2003 @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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 "); +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 -Nru a/drivers/net/irda/smsc-ircc2.h b/drivers/net/irda/smsc-ircc2.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/irda/smsc-ircc2.h Tue May 20 23:05:33 2003 @@ -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 + * 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 -Nru a/drivers/net/irda/smsc-sio.h b/drivers/net/irda/smsc-sio.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/net/irda/smsc-sio.h Tue May 20 23:05:33 2003 @@ -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 -Nru a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c --- a/drivers/net/irda/w83977af_ir.c Tue May 20 23:05:33 2003 +++ b/drivers/net/irda/w83977af_ir.c Tue May 20 23:05:33 2003 @@ -524,6 +524,7 @@ /* 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 @@ switch_bank(iobase, SET0); outb(ICR_ETXTHI, iobase+ICR); } + dev->trans_start = jiffies; dev_kfree_skb(skb); /* Restore set register */ diff -Nru a/include/net/irda/iriap.h b/include/net/irda/iriap.h --- a/include/net/irda/iriap.h Tue May 20 23:05:33 2003 +++ b/include/net/irda/iriap.h Tue May 20 23:05:33 2003 @@ -66,7 +66,7 @@ __u32 daddr; __u8 operation; - struct sk_buff *skb; + struct sk_buff *request_skb; struct lsap_cb *lsap; __u8 slsap_sel; diff -Nru a/include/net/irda/irlmp_event.h b/include/net/irda/irlmp_event.h --- a/include/net/irda/irlmp_event.h Tue May 20 23:05:33 2003 +++ b/include/net/irda/irlmp_event.h Tue May 20 23:05:33 2003 @@ -79,26 +79,6 @@ 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 -Nru a/include/net/irda/irport.h b/include/net/irda/irport.h --- a/include/net/irda/irport.h Tue May 20 23:05:33 2003 +++ b/include/net/irda/irport.h Tue May 20 23:05:33 2003 @@ -67,6 +67,7 @@ __u32 new_speed; int mode; int index; /* Instance index */ + int transmitting; /* Are we transmitting ? */ spinlock_t lock; /* For serializing operations */ diff -Nru a/net/irda/af_irda.c b/net/irda/af_irda.c --- a/net/irda/af_irda.c Tue May 20 23:05:33 2003 +++ b/net/irda/af_irda.c Tue May 20 23:05:33 2003 @@ -11,7 +11,7 @@ * Sources: af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc. * * Copyright (c) 1999 Dag Brattli - * Copyright (c) 1999-2001 Jean Tourrilhes + * Copyright (c) 1999-2003 Jean Tourrilhes * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -190,6 +190,9 @@ 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 @@ 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 @@ 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 @@ 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 @@ * 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 @@ 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 @@ /* 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 -Nru a/net/irda/ircomm/ircomm_core.c b/net/irda/ircomm/ircomm_core.c --- a/net/irda/ircomm/ircomm_core.c Tue May 20 23:05:33 2003 +++ b/net/irda/ircomm/ircomm_core.c Tue May 20 23:05:33 2003 @@ -10,6 +10,7 @@ * Modified by: Dag Brattli * * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes * * 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 @@ info->max_header_size, skb); else { IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ ); - dev_kfree_skb(skb); } } @@ -295,7 +295,6 @@ info->max_header_size, skb); else { IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ ); - dev_kfree_skb(skb); } } @@ -338,7 +337,6 @@ 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 @@ 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 @@ 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 @@ info->reason, skb); } else { IRDA_DEBUG(0, "%s(), missing handler\n", __FUNCTION__ ); - dev_kfree_skb(skb); } } diff -Nru a/net/irda/ircomm/ircomm_event.c b/net/irda/ircomm/ircomm_event.c --- a/net/irda/ircomm/ircomm_event.c Tue May 20 23:05:33 2003 +++ b/net/irda/ircomm/ircomm_event.c Tue May 20 23:05:33 2003 @@ -109,9 +109,7 @@ 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 @@ 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 @@ 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 @@ default: IRDA_DEBUG(0, "%s(), unknown event = %s\n", __FUNCTION__ , ircomm_event[event]); - if (skb) - dev_kfree_skb(skb); ret = -EINVAL; } return ret; diff -Nru a/net/irda/ircomm/ircomm_lmp.c b/net/irda/ircomm/ircomm_lmp.c --- a/net/irda/ircomm/ircomm_lmp.c Tue May 20 23:05:33 2003 +++ b/net/irda/ircomm/ircomm_lmp.c Tue May 20 23:05:33 2003 @@ -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 * * 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 @@ 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_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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 -Nru a/net/irda/ircomm/ircomm_ttp.c b/net/irda/ircomm/ircomm_ttp.c --- a/net/irda/ircomm/ircomm_ttp.c Tue May 20 23:05:33 2003 +++ b/net/irda/ircomm/ircomm_ttp.c Tue May 20 23:05:33 2003 @@ -10,6 +10,7 @@ * Modified by: Dag Brattli * * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes * * 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 @@ 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_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 @@ * 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 @@ * 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 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 @@ 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 -Nru a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c --- a/net/irda/ircomm/ircomm_tty.c Tue May 20 23:05:33 2003 +++ b/net/irda/ircomm/ircomm_tty.c Tue May 20 23:05:33 2003 @@ -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 * * 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 @@ 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 @@ 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 @@ if (!self->tty) { IRDA_DEBUG(0, "%s(), no tty!\n", __FUNCTION__ ); - dev_kfree_skb(skb); return 0; } @@ -1204,7 +1211,8 @@ * 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 @@ 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 -Nru a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c --- a/net/irda/ircomm/ircomm_tty_attach.c Tue May 20 23:05:33 2003 +++ b/net/irda/ircomm/ircomm_tty_attach.c Tue May 20 23:05:33 2003 @@ -10,6 +10,7 @@ * Modified by: Dag Brattli * * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes * * 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 @@ 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 @@ 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 @@ 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 @@ default: IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ , ircomm_tty_event[event]); - return -EINVAL; + ret = -EINVAL; } return ret; } @@ -718,7 +720,7 @@ default: IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ , ircomm_tty_event[event]); - return -EINVAL; + ret = -EINVAL; } return ret; } @@ -774,7 +776,7 @@ default: IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ , ircomm_tty_event[event]); - return -EINVAL; + ret = -EINVAL; } return ret; } @@ -822,7 +824,7 @@ default: IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ , ircomm_tty_event[event]); - return -EINVAL; + ret = -EINVAL; } return ret; } @@ -874,7 +876,7 @@ default: IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ , ircomm_tty_event[event]); - return -EINVAL; + ret = -EINVAL; } return ret; } @@ -917,7 +919,7 @@ default: IRDA_DEBUG(2, "%s(), unknown event: %s\n", __FUNCTION__ , ircomm_tty_event[event]); - return -EINVAL; + ret = -EINVAL; } return ret; } diff -Nru a/net/irda/iriap.c b/net/irda/iriap.c --- a/net/irda/iriap.c Tue May 20 23:05:33 2003 +++ b/net/irda/iriap.c Tue May 20 23:05:33 2003 @@ -11,7 +11,7 @@ * * Copyright (c) 1998-1999 Dag Brattli , * All Rights Reserved. - * Copyright (c) 2000-2001 Jean Tourrilhes + * Copyright (c) 2000-2003 Jean Tourrilhes * * 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 @@ 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 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 @@ 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 @@ NULL); iriap_close(self); } - - if (userdata) - dev_kfree_skb(userdata); } /* @@ -322,15 +323,15 @@ */ 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 @@ /* * 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 @@ __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 @@ 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 @@ 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 @@ /* Aborting, close connection! */ iriap_disconnect_request(self); - dev_kfree_skb(skb); return; /* break; */ } @@ -533,7 +536,6 @@ IRDA_DEBUG(0, "%s(), missing handler!\n", __FUNCTION__); irias_delete_value(value); } - dev_kfree_skb(skb); } /* @@ -545,7 +547,7 @@ 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 @@ * 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 @@ 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 @@ 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 @@ 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_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 @@ 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 @@ 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 @@ 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_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 @@ 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 @@ /* 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 @@ 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 @@ /* 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 @@ 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 @@ 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 @@ 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 @@ iriap_getvaluebyclass_indication(self, skb); break; } + /* skb will be cleaned up in iriap_data_indication */ } /* diff -Nru a/net/irda/iriap_event.c b/net/irda/iriap_event.c --- a/net/irda/iriap_event.c Tue May 20 23:05:33 2003 +++ b/net/irda/iriap_event.c Tue May 20 23:05:33 2003 @@ -11,6 +11,7 @@ * * Copyright (c) 1997, 1999-2000 Dag Brattli , * All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes * * 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 @@ 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_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 @@ * 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 @@ IRDA_DEBUG(0, "%s(), unknown event!\n", __FUNCTION__); break; } - } /* @@ -465,11 +465,8 @@ 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: @@ -478,6 +475,10 @@ * to state Receiving instead, DB. */ 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; diff -Nru a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c --- a/net/irda/irlan/irlan_eth.c Tue May 20 23:05:33 2003 +++ b/net/irda/irlan/irlan_eth.c Tue May 20 23:05:33 2003 @@ -206,7 +206,7 @@ * 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 -Nru a/net/irda/irlap.c b/net/irda/irlap.c --- a/net/irda/irlap.c Tue May 20 23:05:33 2003 +++ b/net/irda/irlap.c Tue May 20 23:05:33 2003 @@ -10,7 +10,7 @@ * Modified by: Dag Brattli * * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2001 Jean Tourrilhes + * Copyright (c) 2000-2003 Jean Tourrilhes * * 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 @@ 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 @@ * 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 @@ 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 @@ /* 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 @@ 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 @@ 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 @@ /* 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 -Nru a/net/irda/irlap_event.c b/net/irda/irlap_event.c --- a/net/irda/irlap_event.c Tue May 20 23:05:33 2003 +++ b/net/irda/irlap_event.c Tue May 20 23:05:33 2003 @@ -12,7 +12,7 @@ * Copyright (c) 1998-2000 Dag Brattli , * Copyright (c) 1998 Thomas Davis * All Rights Reserved. - * Copyright (c) 2000-2001 Jean Tourrilhes + * Copyright (c) 2000-2003 Jean Tourrilhes * * 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 @@ /* 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 @@ 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 -Nru a/net/irda/irlap_frame.c b/net/irda/irlap_frame.c --- a/net/irda/irlap_frame.c Tue May 20 23:05:33 2003 +++ b/net/irda/irlap_frame.c Tue May 20 23:05:33 2003 @@ -11,7 +11,7 @@ * * Copyright (c) 1998-2000 Dag Brattli , * All Rights Reserved. - * Copyright (c) 2000-2001 Jean Tourrilhes + * Copyright (c) 2000-2003 Jean Tourrilhes * * 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_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 @@ 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 @@ * 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 @@ 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 @@ */ 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 @@ 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 @@ /* 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_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 @@ frame[1] = DM_RSP | PF_BIT; - irlap_queue_xmit(self, skb); + irlap_queue_xmit(self, tx_skb); } /* @@ -273,7 +271,7 @@ */ 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 @@ 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_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 @@ 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 @@ 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 @@ break; default: /* Error!! */ - dev_kfree_skb(skb); return; } info->s = xid->slotnr; @@ -561,21 +558,21 @@ */ 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_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 @@ 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 @@ 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 @@ */ 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 @@ */ 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 @@ } /* - * 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 @@ */ 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 @@ */ 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 @@ 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 @@ 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 @@ 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 @@ * 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 @@ * 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 @@ break; } out: + /* Always drop our reference on the skb */ dev_kfree_skb(skb); return 0; } diff -Nru a/net/irda/irlmp.c b/net/irda/irlmp.c --- a/net/irda/irlmp.c Tue May 20 23:05:33 2003 +++ b/net/irda/irlmp.c Tue May 20 23:05:33 2003 @@ -11,7 +11,7 @@ * * Copyright (c) 1998-2000 Dag Brattli , * All Rights Reserved. - * Copyright (c) 2000-2001 Jean Tourrilhes + * Copyright (c) 2000-2003 Jean Tourrilhes * * 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 @@ __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 @@ "%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 @@ 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 @@ * 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 @@ 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 @@ /* 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 @@ 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 @@ 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 @@ 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 @@ */ 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 @@ * 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 @@ * 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 @@ /* * 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 @@ * * 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 @@ /* 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 @@ /* 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 @@ /* 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 -Nru a/net/irda/irlmp_event.c b/net/irda/irlmp_event.c --- a/net/irda/irlmp_event.c Tue May 20 23:05:33 2003 +++ b/net/irda/irlmp_event.c Tue May 20 23:05:33 2003 @@ -11,7 +11,7 @@ * * Copyright (c) 1998-1999 Dag Brattli , * All Rights Reserved. - * Copyright (c) 2000-2001 Jean Tourrilhes + * Copyright (c) 2000-2003 Jean Tourrilhes * * 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 @@ default: IRDA_DEBUG(0, "%s(), Unknown event %s\n", __FUNCTION__, irlmp_event[event]); - if (skb) - dev_kfree_skb(skb); break; } } @@ -373,8 +371,6 @@ default: IRDA_DEBUG(0, "%s(), Unknown event %s\n", __FUNCTION__, irlmp_event[event]); - if (skb) - dev_kfree_skb(skb); break; } } @@ -468,8 +464,6 @@ default: IRDA_DEBUG(0, "%s(), Unknown event %s\n", __FUNCTION__, irlmp_event[event]); - if (skb) - dev_kfree_skb(skb); break; } } @@ -499,6 +493,9 @@ 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 @@ __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 @@ __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 @@ 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 @@ 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_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 @@ 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 @@ 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 @@ 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 @@ 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_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 @@ 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 @@ 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 -Nru a/net/irda/irlmp_frame.c b/net/irda/irlmp_frame.c --- a/net/irda/irlmp_frame.c Tue May 20 23:05:33 2003 +++ b/net/irda/irlmp_frame.c Tue May 20 23:05:33 2003 @@ -11,7 +11,7 @@ * * Copyright (c) 1998-1999 Dag Brattli * All Rights Reserved. - * Copyright (c) 2000-2001 Jean Tourrilhes + * Copyright (c) 2000-2003 Jean Tourrilhes * * 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 @@ } else { IRDA_DEBUG(2, "%s(), received data frame\n", __FUNCTION__); } - dev_kfree_skb(skb); return; } @@ -168,16 +167,13 @@ 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 @@ 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 @@ 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_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 @@ 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_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 @@ 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 -Nru a/net/irda/irnet/irnet.h b/net/irda/irnet/irnet.h --- a/net/irda/irnet/irnet.h Tue May 20 23:05:33 2003 +++ b/net/irda/irnet/irnet.h Tue May 20 23:05:33 2003 @@ -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 -Nru a/net/irda/irnet/irnet_irda.c b/net/irda/irnet/irnet_irda.c --- a/net/irda/irnet/irnet_irda.c Tue May 20 23:05:33 2003 +++ b/net/irda/irnet/irnet_irda.c Tue May 20 23:05:33 2003 @@ -31,7 +31,6 @@ 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 @@ * 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 @@ 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 @@ * 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 @@ 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 @@ 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 @@ 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 @@ } /* 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 @@ { 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_socket * self; char * state; - unsigned long flags; int i = 0; len = 0; @@ -1728,7 +1728,7 @@ 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 @@ } /* Spin lock end */ - spin_unlock_irqrestore(&irnet_server.spinlock, flags); + spin_unlock_bh(&irnet_server.spinlock); return len; } diff -Nru a/net/irda/irnet/irnet_ppp.c b/net/irda/irnet/irnet_ppp.c --- a/net/irda/irnet/irnet_ppp.c Tue May 20 23:05:33 2003 +++ b/net/irda/irnet/irnet_ppp.c Tue May 20 23:05:33 2003 @@ -927,7 +927,7 @@ * 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 -Nru a/net/irda/irttp.c b/net/irda/irttp.c --- a/net/irda/irttp.c Tue May 20 23:05:33 2003 +++ b/net/irda/irttp.c Tue May 20 23:05:33 2003 @@ -11,7 +11,7 @@ * * Copyright (c) 1998-2000 Dag Brattli , * All Rights Reserved. - * Copyright (c) 2000-2001 Jean Tourrilhes + * Copyright (c) 2000-2003 Jean Tourrilhes * * 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 @@ 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 @@ 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_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 @@ /* 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 @@ 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 @@ { 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 @@ 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 @@ irttp_run_tx_queue(self); return 0; + +err: + dev_kfree_skb(skb); + return ret; } /* @@ -822,6 +841,7 @@ struct sk_buff *skb) { struct tsap_cb *self; + int err; IRDA_DEBUG(4, "%s()\n", __FUNCTION__); @@ -831,14 +851,19 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ (__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 @@ /* 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 @@ 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 @@ 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 @@ /* 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 @@ /* 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 @@ (__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 @@ 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 @@ 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 @@ /* * 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. */