--- linux-2.5.45/drivers/net/irda/Kconfig Sat Nov 2 18:27:41 2002 +++ v2.5.45-md/drivers/net/irda/Kconfig Sun Nov 3 00:57:46 2002 @@ -9,7 +9,7 @@ depends on IRDA help Say Y here if you want to build support for the IrTTY line - discipline. If you want to compile it as a module (irtty.o), say M + discipline. If you want to compile it as a module (irtty-sir.o), say M here and read . IrTTY makes it possible to use Linux's own serial driver for all IrDA ports that are 16550 compatible. Most IrDA chips are 16550 compatible so you @@ -18,6 +18,69 @@ If unsure, say Y. +comment "Dongle support" + +config DONGLE + bool "Serial dongle support" + help + Say Y here if you have an infrared device that connects to your + computer's serial port. These devices are called dongles. Then say Y + or M to the driver for your particular dongle below. + + Note that the answer to this question won't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about serial dongles. + +config ESI_DONGLE + tristate "ESI JetEye PC dongle" + depends on DONGLE && IRDA + help + Say Y here if you want to build support for the Extended Systems + JetEye PC dongle. If you want to compile it as a module, say M here + and read . The ESI dongle attaches + to the normal 9-pin serial port connector, and can currently only be + used by IrTTY. To activate support for ESI dongles you will have to + start irattach like this: "irattach -d esi". + +config ACTISYS_DONGLE + tristate "ACTiSYS IR-220L and IR220L+ dongle" + depends on DONGLE && IRDA + help + Say Y here if you want to build support for the ACTiSYS IR-220L and + IR220L+ dongles. If you want to compile it as a module, say M here + and read . The ACTiSYS dongles + attaches to the normal 9-pin serial port connector, and can + currently only be used by IrTTY. To activate support for ACTiSYS + dongles you will have to start irattach like this: + "irattach -d actisys" or "irattach -d actisys+". + +config TEKRAM_DONGLE + tristate "Tekram IrMate 210B dongle" + depends on DONGLE && IRDA + help + Say Y here if you want to build support for the Tekram IrMate 210B + dongle. If you want to compile it as a module, say M here and read + . The Tekram dongle attaches to the + normal 9-pin serial port connector, and can currently only be used + by IrTTY. To activate support for Tekram dongles you will have to + start irattach like this: "irattach -d tekram". + +comment "Old SIR device drivers" + +config IRTTY_OLD + tristate "Old IrTTY (broken)" + depends on IRDA + help + Say Y here if you want to build support for the IrTTY line + discipline. If you want to compile it as a module (irtty.o), say M + here and read . IrTTY makes it + possible to use Linux's own serial driver for all IrDA ports that + are 16550 compatible. Most IrDA chips are 16550 compatible so you + should probably say Y to this option. Using IrTTY will however + limit the speed of the connection to 115200 bps (IrDA SIR mode). + + If unsure, say N. + config IRPORT_SIR tristate "IrPORT (IrDA serial driver)" depends on IRDA @@ -35,10 +98,10 @@ If unsure, say Y. -comment "Dongle support" +comment "Old Serial dongle support" -config DONGLE - bool "Serial dongle support" +config DONGLE_OLD + bool "Old Serial dongle support" help Say Y here if you have an infrared device that connects to your computer's serial port. These devices are called dongles. Then say Y @@ -48,9 +111,9 @@ kernel: saying N will just cause the configurator to skip all the questions about serial dongles. -config ESI_DONGLE +config ESI_DONGLE_OLD tristate "ESI JetEye PC dongle" - depends on DONGLE && IRDA + depends on DONGLE_OLD && IRDA help Say Y here if you want to build support for the Extended Systems JetEye PC dongle. If you want to compile it as a module, say M here @@ -59,9 +122,9 @@ used by IrTTY. To activate support for ESI dongles you will have to start irattach like this: "irattach -d esi". -config ACTISYS_DONGLE +config ACTISYS_DONGLE_OLD tristate "ACTiSYS IR-220L and IR220L+ dongle" - depends on DONGLE && IRDA + depends on DONGLE_OLD && IRDA help Say Y here if you want to build support for the ACTiSYS IR-220L and IR220L+ dongles. If you want to compile it as a module, say M here @@ -71,9 +134,9 @@ dongles you will have to start irattach like this: "irattach -d actisys" or "irattach -d actisys+". -config TEKRAM_DONGLE +config TEKRAM_DONGLE_OLD tristate "Tekram IrMate 210B dongle" - depends on DONGLE && IRDA + depends on DONGLE_OLD && IRDA help Say Y here if you want to build support for the Tekram IrMate 210B dongle. If you want to compile it as a module, say M here and read @@ -84,7 +147,7 @@ config GIRBIL_DONGLE tristate "Greenwich GIrBIL dongle" - depends on DONGLE && IRDA + depends on DONGLE_OLD && IRDA help Say Y here if you want to build support for the Greenwich GIrBIL dongle. If you want to compile it as a module, say M here and read @@ -95,7 +158,7 @@ config LITELINK_DONGLE tristate "Parallax LiteLink dongle" - depends on DONGLE && IRDA + depends on DONGLE_OLD && IRDA help Say Y here if you want to build support for the Parallax Litelink dongle. If you want to compile it as a module, say M here and read @@ -106,7 +169,7 @@ config MCP2120_DONGLE tristate "Microchip MCP2120" - depends on DONGLE && IRDA + depends on DONGLE_OLD && IRDA help Say Y here if you want to build support for the Microchip MCP2120 dongle. If you want to compile it as a module, say M here and read @@ -120,7 +183,7 @@ config OLD_BELKIN_DONGLE tristate "Old Belkin dongle" - depends on DONGLE && IRDA + depends on DONGLE_OLD && IRDA help Say Y here if you want to build support for the Adaptec Airport 1000 and 2000 dongles. If you want to compile it as a module, say M here @@ -130,11 +193,11 @@ config EP7211_IR tristate "EP7211 I/R support" - depends on DONGLE && ARCH_EP7211 && IRDA + depends on DONGLE_OLD && ARCH_EP7211 && IRDA config ACT200L_DONGLE tristate "ACTiSYS IR-200L dongle (EXPERIMENTAL)" - depends on DONGLE && EXPERIMENTAL && IRDA + depends on DONGLE_OLD && EXPERIMENTAL && IRDA help Say Y here if you want to build support for the ACTiSYS IR-200L dongle. If you want to compile it as a module, say M here and read @@ -145,7 +208,7 @@ config MA600_DONGLE tristate "Mobile Action MA600 dongle (EXPERIMENTAL)" - depends on DONGLE && EXPERIMENTAL && IRDA + depends on DONGLE_OLD && EXPERIMENTAL && IRDA ---help--- Say Y here if you want to build support for the Mobile Action MA600 dongle. If you want to compile it as a module, say M here and read diff -u -p --new-file linux/drivers/net/irda-d6/Makefile linux/drivers/net/irda/Makefile --- linux/drivers/net/irda-d6/Makefile Mon Oct 21 14:15:03 2002 +++ linux/drivers/net/irda/Makefile Thu Oct 31 16:33:46 2002 @@ -5,10 +5,12 @@ # Rewritten to use lists instead of if-statements. # -export-objs = irport.o +export-objs = irport.o sir_core.o -obj-$(CONFIG_IRTTY_SIR) += irtty.o +# Old SIR drivers (irtty is broken) +obj-$(CONFIG_IRTTY_OLD) += irtty.o obj-$(CONFIG_IRPORT_SIR) += irport.o +# FIR drivers obj-$(CONFIG_USB_IRDA) += irda-usb.o obj-$(CONFIG_NSC_FIR) += nsc-ircc.o obj-$(CONFIG_WINBOND_FIR) += w83977af_ir.o @@ -18,9 +20,10 @@ obj-$(CONFIG_TOSHIBA_FIR) += donauboe.o obj-$(CONFIG_SMC_IRCC_FIR) += smc-ircc.o irport.o obj-$(CONFIG_ALI_FIR) += ali-ircc.o obj-$(CONFIG_VLSI_FIR) += vlsi_ir.o -obj-$(CONFIG_ESI_DONGLE) += esi.o -obj-$(CONFIG_TEKRAM_DONGLE) += tekram.o -obj-$(CONFIG_ACTISYS_DONGLE) += actisys.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_GIRBIL_DONGLE) += girbil.o obj-$(CONFIG_LITELINK_DONGLE) += litelink.o obj-$(CONFIG_OLD_BELKIN_DONGLE) += old_belkin.o @@ -28,5 +31,14 @@ obj-$(CONFIG_EP7211_IR) += ep7211_ir.o obj-$(CONFIG_MCP2120_DONGLE) += mcp2120.o obj-$(CONFIG_ACT200L_DONGLE) += act200l.o obj-$(CONFIG_MA600_DONGLE) += ma600.o +# New SIR drivers +obj-$(CONFIG_IRTTY_SIR) += irtty-sir.o sir-dev.o +# New dongles drivers for new SIR drivers +obj-$(CONFIG_ESI_DONGLE) += esi-sir.o +obj-$(CONFIG_TEKRAM_DONGLE) += tekram-sir.o +obj-$(CONFIG_ACTISYS_DONGLE) += actisys-sir.o + +# The SIR helper module +sir-dev-objs := sir_core.o sir_dev.o sir_dongle.o sir_kthread.o include $(TOPDIR)/Rules.make diff -u -p --new-file linux/drivers/net/irda-d6/actisys-sir.c linux/drivers/net/irda/actisys-sir.c --- linux/drivers/net/irda-d6/actisys-sir.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/irda/actisys-sir.c Thu Oct 31 16:29:44 2002 @@ -0,0 +1,240 @@ +/********************************************************************* + * + * Filename: actisys.c + * Version: 1.1 + * Description: Implementation for the ACTiSYS IR-220L and IR-220L+ + * dongles + * Status: Beta. + * Authors: Dag Brattli (initially) + * Jean Tourrilhes (new version) + * Martin Diehl (new version for sir_dev) + * Created at: Wed Oct 21 20:02:35 1998 + * Modified at: Sun Oct 27 22:02:13 2002 + * Modified by: Martin Diehl + * + * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. + * Copyright (c) 1999 Jean Tourrilhes + * Copyright (c) 2002 Martin Diehl + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsų admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +/* + * Changelog + * + * 0.8 -> 0.9999 - Jean + * o New initialisation procedure : much safer and correct + * o New procedure the change speed : much faster and simpler + * o Other cleanups & comments + * Thanks to Lichen Wang @ Actisys for his excellent help... + * + * 1.0 -> 1.1 - Martin Diehl + * modified for new sir infrastructure + */ + +#include +#include +#include + +#include + +#include "sir-dev.h" + +/* + * Define the timing of the pulses we send to the dongle (to reset it, and + * to toggle speeds). Basically, the limit here is the propagation speed of + * the signals through the serial port, the dongle being much faster. Any + * serial port support 115 kb/s, so we are sure that pulses 8.5 us wide can + * go through cleanly . If you are on the wild side, you can try to lower + * this value (Actisys recommended me 2 us, and 0 us work for me on a P233!) + */ +#define MIN_DELAY 10 /* 10 us to be on the conservative side */ + +static int actisys_open(struct sir_dev *); +static int actisys_close(struct sir_dev *); +static int actisys_change_speed(struct sir_dev *, unsigned); +static int actisys_reset(struct sir_dev *); + +/* These are the baudrates supported, in the order available */ +/* Note : the 220L doesn't support 38400, but we will fix that below */ +static __u32 baud_rates[] = { 9600, 19200, 57600, 115200, 38400 }; + +#define MAX_SPEEDS (sizeof(baud_rates)/sizeof(baud_rates[0])) + +static struct dongle_driver act220l = { + .owner = THIS_MODULE, + .driver_name = "Actisys ACT-220L", + .type = IRDA_ACTISYS_DONGLE, + .open = actisys_open, + .close = actisys_close, + .reset = actisys_reset, + .set_speed = actisys_change_speed, +}; + +static struct dongle_driver act220l_plus = { + .owner = THIS_MODULE, + .driver_name = "Actisys ACT-220L+", + .type = IRDA_ACTISYS_PLUS_DONGLE, + .open = actisys_open, + .close = actisys_close, + .reset = actisys_reset, + .set_speed = actisys_change_speed, +}; + +int __init actisys_sir_init(void) +{ + int ret; + + /* First, register an Actisys 220L dongle */ + ret = irda_register_dongle(&act220l); + if (ret < 0) + return ret; + + /* Now, register an Actisys 220L+ dongle */ + ret = irda_register_dongle(&act220l_plus); + if (ret < 0) { + irda_unregister_dongle(&act220l); + return ret; + } + return 0; +} + +void __exit actisys_sir_cleanup(void) +{ + /* We have to remove both dongles */ + irda_unregister_dongle(&act220l_plus); + irda_unregister_dongle(&act220l); +} + +static int actisys_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + dev->set_dtr_rts(dev, TRUE, TRUE); + + /* Set the speeds we can accept */ + qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + + /* Remove support for 38400 if this is not a 220L+ dongle */ + if (dev->dongle_drv->type == IRDA_ACTISYS_DONGLE) + qos->baud_rate.bits &= ~IR_38400; + + qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */ + irda_qos_bits_to_value(qos); + + return 0; +} + +static int actisys_close(struct sir_dev *dev) +{ + /* Power off the dongle */ + dev->set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function actisys_change_speed (task) + * + * Change speed of the ACTiSYS IR-220L and IR-220L+ type IrDA dongles. + * To cycle through the available baud rates, pulse RTS low for a few us. + * + * First, we reset the dongle to always start from a known state. + * Then, we cycle through the speeds by pulsing RTS low and then up. + * The dongle allow us to pulse quite fast, se we can set speed in one go, + * which is must faster ( < 100 us) and less complex than what is found + * in some other dongle drivers... + * Note that even if the new speed is the same as the current speed, + * we reassert the speed. This make sure that things are all right, + * and it's fast anyway... + * By the way, this function will work for both type of dongles, + * because the additional speed is at the end of the sequence... + */ +static int actisys_change_speed(struct sir_dev *dev, unsigned speed) +{ + int ret = 0; + int i = 0; + + IRDA_DEBUG(4, "%s(), speed=%d (was %d)\n", __FUNCTION__, + speed, dev->speed); + + /* dongle was already resetted from irda_request state machine, + * we are in known state (dongle default) + */ + + /* + * Now, we can set the speed requested. Send RTS pulses until we + * reach the target speed + */ + for (i=0; ispeed = baud_rates[i]; + break; + } + /* Set RTS low for 10 us */ + dev->set_dtr_rts(dev, TRUE, FALSE); + udelay(MIN_DELAY); + + /* Set RTS high for 10 us */ + dev->set_dtr_rts(dev, TRUE, TRUE); + udelay(MIN_DELAY); + } + + /* Check if life is sweet... */ + if (i >= MAX_SPEEDS) + ret = -1; /* This should not happen */ + + /* Basta lavoro, on se casse d'ici... */ + return ret; +} + +/* + * Function actisys_reset (task) + * + * Reset the Actisys type dongle. Warning, this function must only be + * called with a process context! + * + * We need to do two things in this function : + * o first make sure that the dongle is in a state where it can operate + * o second put the dongle in a know state + * + * The dongle is powered of the RTS and DTR lines. In the dongle, there + * is a big capacitor to accomodate the current spikes. This capacitor + * takes a least 50 ms to be charged. In theory, the Bios set those lines + * up, so by the time we arrive here we should be set. It doesn't hurt + * to be on the conservative side, so we will wait... + * + * Then, we set the speed to 9600 b/s to get in a known state (see in + * change_speed for details). It is needed because the IrDA stack + * has tried to set the speed immediately after our first return, + * so before we can be sure the dongle is up and running. + */ + +static int actisys_reset(struct sir_dev *dev) +{ + /* Reset the dongle : set DTR low for 10 us */ + dev->set_dtr_rts(dev, FALSE, TRUE); + udelay(MIN_DELAY); + + /* Go back to normal mode */ + dev->set_dtr_rts(dev, TRUE, TRUE); + + dev->speed = 9600; /* That's the default */ + + return 0; +} + +MODULE_AUTHOR("Dag Brattli - Jean Tourrilhes "); +MODULE_DESCRIPTION("ACTiSYS IR-220L and IR-220L+ dongle driver"); +MODULE_LICENSE("GPL"); + +module_init(actisys_sir_init); +module_exit(actisys_sir_cleanup); diff -u -p --new-file linux/drivers/net/irda-d6/esi-sir.c linux/drivers/net/irda/esi-sir.c --- linux/drivers/net/irda-d6/esi-sir.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/irda/esi-sir.c Thu Oct 31 16:30:19 2002 @@ -0,0 +1,145 @@ +/********************************************************************* + * + * Filename: esi.c + * Version: 1.6 + * Description: Driver for the Extended Systems JetEye PC dongle + * Status: Experimental. + * Author: Dag Brattli + * Created at: Sat Feb 21 18:54:38 1998 + * Modified at: Sun Oct 27 22:01:04 2002 + * Modified by: Martin Diehl + * + * Copyright (c) 1999 Dag Brattli, , + * Copyright (c) 1998 Thomas Davis, , + * Copyright (c) 2002 Martin Diehl, , + * 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 "sir-dev.h" + +static int esi_open(struct sir_dev *); +static int esi_close(struct sir_dev *); +static int esi_change_speed(struct sir_dev *, unsigned); +static int esi_reset(struct sir_dev *); + +static struct dongle_driver esi = { + .owner = THIS_MODULE, + .driver_name = "JetEye PC ESI-9680 PC", + .type = IRDA_ESI_DONGLE, + .open = esi_open, + .close = esi_close, + .reset = esi_reset, + .set_speed = esi_change_speed, +}; + +static int __init esi_sir_init(void) +{ + return irda_register_dongle(&esi); +} + +static void __exit esi_sir_cleanup(void) +{ + irda_unregister_dongle(&esi); +} + +static int esi_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + qos->baud_rate.bits &= IR_9600|IR_19200|IR_115200; + qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */ + irda_qos_bits_to_value(qos); + + /* shouldn't we do set_dtr_rts(FALSE, TRUE) here (power up at 9600)? */ + + return 0; +} + +static int esi_close(struct sir_dev *dev) +{ + /* Power off dongle */ + dev->set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function esi_change_speed (task) + * + * Set the speed for the Extended Systems JetEye PC ESI-9680 type dongle + * + */ +static int esi_change_speed(struct sir_dev *dev, unsigned speed) +{ + int dtr, rts; + + switch (speed) { + case 19200: + dtr = TRUE; + rts = FALSE; + break; + case 115200: + dtr = rts = TRUE; + break; + default: + speed = 9600; + /* fall through */ + case 9600: + dtr = FALSE; + rts = TRUE; + break; + } + + /* Change speed of dongle */ + dev->set_dtr_rts(dev, dtr, rts); + dev->speed = speed; + + /* do we need some delay for power stabilization? */ + + return 0; +} + +/* + * Function esi_reset (task) + * + * Reset dongle; + * + */ +static int esi_reset(struct sir_dev *dev) +{ + dev->set_dtr_rts(dev, FALSE, FALSE); + + /* Hm, probably repower to 9600 and some delays? */ + + return 0; +} + +MODULE_AUTHOR("Dag Brattli "); +MODULE_DESCRIPTION("Extended Systems JetEye PC dongle driver"); +MODULE_LICENSE("GPL"); + +module_init(esi_sir_init); +module_exit(esi_sir_cleanup); + diff -u -p --new-file linux/drivers/net/irda-d6/irtty-sir.c linux/drivers/net/irda/irtty-sir.c --- linux/drivers/net/irda-d6/irtty-sir.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/irda/irtty-sir.c Thu Oct 31 18:19:01 2002 @@ -0,0 +1,684 @@ +/********************************************************************* + * + * Filename: irtty-sir.c + * Version: 2.0 + * Description: IrDA line discipline implementation + * Status: Experimental. + * Author: Dag Brattli + * Created at: Tue Dec 9 21:18:38 1997 + * Modified at: Sun Oct 27 22:13:30 2002 + * Modified by: Martin Diehl + * Sources: slip.c by Laurence Culhane, + * Fred N. van Kempen, + * + * Copyright (c) 1998-2000 Dag Brattli, + * Copyright (c) 2002 Martin Diehl, + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsų admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "sir-dev.h" +#include "irtty-sir.h" + +MODULE_PARM(qos_mtt_bits, "i"); +MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); + +static int qos_mtt_bits = 0x03; /* 5 ms or more */ + +/* ------------------------------------------------------- */ + +/* device configuration callbacks always invoked with irda-thread context */ + +/* find out, how many chars we have in buffers below us + * this is allowed to lie, i.e. return less chars than we + * actually have. The returned value is used to determine + * how long the irdathread should wait before doing the + * real blocking wait_until_sent() + */ + +static int irtty_chars_in_buffer(struct sir_dev *dev) +{ + struct sirtty_cb *priv = dev->priv; + + ASSERT(priv != NULL, return -1;); + ASSERT(priv->magic == IRTTY_MAGIC, return -1;); + + return priv->tty->driver.chars_in_buffer(priv->tty); +} + +/* Wait (sleep) until underlaying hardware finished transmission + * i.e. hardware buffers are drained + * this must block and not return before all characters are really sent + * + * If the tty sits on top of a 16550A-like uart, there are typically + * up to 16 bytes in the fifo - f.e. 9600 bps 8N1 needs 16.7 msec + * + * With usbserial the uart-fifo is basically replaced by the converter's + * outgoing endpoint buffer, which can usually hold 64 bytes (at least). + * With pl2303 it appears we are safe with 60msec here. + * + * I really wish all serial drivers would provide + * correct implementation of wait_until_sent() + */ + +#define USBSERIAL_TX_DONE_DELAY 60 + +static void irtty_wait_until_sent(struct sir_dev *dev) +{ + struct sirtty_cb *priv = dev->priv; + struct tty_struct *tty; + + ASSERT(priv != NULL, return;); + ASSERT(priv->magic == IRTTY_MAGIC, return;); + + tty = priv->tty; + if (tty->driver.wait_until_sent) { + lock_kernel(); + tty->driver.wait_until_sent(tty, MSECS_TO_JIFFIES(100)); + unlock_kernel(); + } + else { + set_task_state(current, TASK_UNINTERRUPTIBLE); + schedule_timeout(MSECS_TO_JIFFIES(USBSERIAL_TX_DONE_DELAY)); + } +} + +/* + * Function irtty_change_speed (dev, speed) + * + * Change the speed of the serial port. + * + * This may sleep in set_termios (usbserial driver f.e.) and must + * not be called from interrupt/timer/tasklet therefore. + * All such invocations are deferred to kIrDAd now so we can sleep there. + */ + +static int irtty_change_speed(struct sir_dev *dev, unsigned speed) +{ + struct sirtty_cb *priv = dev->priv; + struct tty_struct *tty; + struct termios old_termios; + int cflag; + + ASSERT(priv != NULL, return -1;); + ASSERT(priv->magic == IRTTY_MAGIC, return -1;); + + tty = priv->tty; + + lock_kernel(); + old_termios = *(tty->termios); + cflag = tty->termios->c_cflag; + + cflag &= ~CBAUD; + + IRDA_DEBUG(2, "%s(), Setting speed to %d\n", __FUNCTION__, speed); + + switch (speed) { + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 115200: + cflag |= B115200; + break; + case 9600: + default: + cflag |= B9600; + break; + } + + tty->termios->c_cflag = cflag; + if (tty->driver.set_termios) + tty->driver.set_termios(tty, &old_termios); + unlock_kernel(); + + priv->io.speed = speed; + + return 0; +} + +/* + * Function irtty_set_dtr_rts (dev, dtr, rts) + * + * This function can be used by dongles etc. to set or reset the status + * of the dtr and rts lines + */ + +static int irtty_set_dtr_rts(struct sir_dev *dev, int dtr, int rts) +{ + struct sirtty_cb *priv = dev->priv; + int arg = 0; + + ASSERT(priv != NULL, return -1;); + ASSERT(priv->magic == IRTTY_MAGIC, return -1;); + +#ifdef TIOCM_OUT2 /* Not defined for ARM */ + arg = TIOCM_OUT2; +#endif + if (rts) + arg |= TIOCM_RTS; + if (dtr) + arg |= TIOCM_DTR; + + /* + * The ioctl() function, or actually set_modem_info() in serial.c + * expects a pointer to the argument in user space. This is working + * here because we are always called from the kIrDAd thread which + * has set_fs(KERNEL_DS) permanently set. Therefore copy_from_user() + * is happy with our arg-parameter being local here in kernel space. + */ + + lock_kernel(); + if (priv->tty->driver.ioctl(priv->tty, NULL, TIOCMSET, (unsigned long) &arg)) { + IRDA_DEBUG(2, "%s(), error doing ioctl!\n", __FUNCTION__); + } + unlock_kernel(); + + return 0; +} + +/* ------------------------------------------------------- */ + +/* called from sir_dev when there is more data to send + * context is either netdev->hard_xmit or some transmit-completion bh + * i.e. we are under spinlock here and must not sleep. + * + * Note: as of 2.5.44 the usb-serial driver calls down() on a semaphore + * hence we are hitting the might_sleep bugcatcher. IMHO the whole tty-api + * would be pretty pointless if write_room/write would be allowed to sleep. + * Furthermore other tty ldiscs (like ppp) do also require the driver not + * to sleep there. Hence this is considered a current limitation of + * usb-serial. + */ + +static int irtty_do_write(struct sir_dev *dev, const unsigned char *ptr, size_t len) +{ + struct sirtty_cb *priv = dev->priv; + struct tty_struct *tty; + int writelen; + + ASSERT(priv != NULL, return -1;); + ASSERT(priv->magic == IRTTY_MAGIC, return -1;); + + tty = priv->tty; + if (!tty->driver.write) + return 0; + tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); + if (tty->driver.write_room) { + writelen = tty->driver.write_room(tty); + if (writelen > len) + writelen = len; + } + else + writelen = len; + return tty->driver.write(tty, 0, ptr, writelen); +} + +/* ------------------------------------------------------- */ + +/* irda line discipline callbacks */ + +/* + * Function irtty_receive_buf( tty, cp, count) + * + * Handle the 'receiver data ready' interrupt. This function is called + * by the 'tty_io' module in the kernel when a block of IrDA data has + * been received, which can now be decapsulated and delivered for + * further processing + * + * calling context depends on underlying driver and tty->low_latency! + * for example (low_latency: 1 / 0): + * serial.c: uart-interrupt / softint + * usbserial: urb-complete-interrupt / softint + */ + +static void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct sir_dev *dev; + struct sirtty_cb *priv = tty->disc_data; + int i; + + if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) + return; + /* Please use ASSERT - Fix ASSERT as needed - Jean II */ + + if (unlikely(count==0)) /* yes, this happens */ + return; + + dev = priv->dev; + if (!dev) { + printk(KERN_ERR "%s(), not ready yet!\n", __FUNCTION__); + return; + } + + for (i = 0; i < count; i++) { + /* + * Characters received with a parity error, etc? + */ + if (fp && *fp++) { + IRDA_DEBUG(0, "Framing or parity error!\n"); + sirdev_receive(dev, NULL, 0); /* notify sir_dev (updating stats) */ + return; + } + } + + sirdev_receive(dev, cp, count); +} + +/* + * Function irtty_receive_room (tty) + * + * Used by the TTY to find out how much data we can receive at a time + * +*/ +static int irtty_receive_room(struct tty_struct *tty) +{ + struct sirtty_cb *priv = tty->disc_data; + + if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) + return 0; + + return 65536; /* We can handle an infinite amount of data. :-) */ +} + +/* + * Function irtty_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 irtty_write_wakeup(struct tty_struct *tty) +{ + struct sirtty_cb *priv = tty->disc_data; + + if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) + return; + + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + + if (priv->dev) + sirdev_write_complete(priv->dev); +} + +/* ------------------------------------------------------- */ + +/* + * Function irtty_stop_receiver (tty, stop) + * + */ + +static inline void irtty_stop_receiver(struct tty_struct *tty, int stop) +{ + struct termios old_termios; + int cflag; + + lock_kernel(); + old_termios = *(tty->termios); + cflag = tty->termios->c_cflag; + + if (stop) + cflag &= ~CREAD; + else + cflag |= CREAD; + + tty->termios->c_cflag = cflag; + if (tty->driver.set_termios) + tty->driver.set_termios(tty, &old_termios); + unlock_kernel(); +} + +/*****************************************************************/ + +DECLARE_MUTEX(irtty_sem); /* serialize ldisc open/close with sir_dev */ + +/* notifier from sir_dev when irda% device gets opened (ifup) */ + +static int irtty_start_dev(struct sir_dev *dev) +{ + struct sirtty_cb *priv; + struct tty_struct *tty; + + /* serialize with ldisc open/close */ + down(&irtty_sem); + + priv = dev->priv; + if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) { + up(&irtty_sem); + return -ESTALE; + } + + tty = priv->tty; + + if (tty->driver.start) + tty->driver.start(tty); + /* Make sure we can receive more data */ + irtty_stop_receiver(tty, FALSE); + + up(&irtty_sem); + return 0; +} + +/* notifier from sir_dev when irda% device gets closed (ifdown) */ + +static int irtty_stop_dev(struct sir_dev *dev) +{ + struct sirtty_cb *priv; + struct tty_struct *tty; + + /* serialize with ldisc open/close */ + down(&irtty_sem); + + priv = dev->priv; + if (unlikely(!priv || priv->magic!=IRTTY_MAGIC)) { + up(&irtty_sem); + return -ESTALE; + } + + tty = priv->tty; + + /* Make sure we don't receive more data */ + irtty_stop_receiver(tty, TRUE); + if (tty->driver.stop) + tty->driver.stop(tty); + + up(&irtty_sem); + + return 0; +} + +/* ------------------------------------------------------- */ + +struct sir_driver sir_tty_drv = { + .owner = THIS_MODULE, + .driver_name = "sir_tty", + .start_dev = irtty_start_dev, + .stop_dev = irtty_stop_dev, + .do_write = irtty_do_write, + .chars_in_buffer = irtty_chars_in_buffer, + .wait_until_sent = irtty_wait_until_sent, + .set_speed = irtty_change_speed, + .set_dtr_rts = irtty_set_dtr_rts, +}; + +/* ------------------------------------------------------- */ + +/* + * Function irtty_ioctl (tty, file, cmd, arg) + * + * The Swiss army knife of system calls :-) + * + */ +static int irtty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct irtty_info { char name[6]; } info; + struct sir_dev *dev; + struct sirtty_cb *priv = tty->disc_data; + int size = _IOC_SIZE(cmd); + int err = 0; + + ASSERT(priv != NULL, return -ENODEV;); + ASSERT(priv->magic == IRTTY_MAGIC, return -EBADR;); + + IRDA_DEBUG(3, "%s(cmd=0x%X)\n", __FUNCTION__, cmd); + + dev = priv->dev; + ASSERT(dev != NULL, return -1;); + + if (_IOC_DIR(cmd) & _IOC_READ) + err = verify_area(VERIFY_WRITE, (void *) arg, size); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + err = verify_area(VERIFY_READ, (void *) arg, size); + if (err) + return err; + + switch (cmd) { + case TCGETS: + case TCGETA: + err = n_tty_ioctl(tty, file, cmd, arg); + break; + + case IRTTY_IOCTDONGLE: + /* this call blocks for completion */ + err = sirdev_set_dongle(dev, (IRDA_DONGLE) arg); + break; + + case IRTTY_IOCGET: + ASSERT(dev->netdev != NULL, return -1;); + + memset(&info, 0, sizeof(info)); + strncpy(info.name, dev->netdev->name, sizeof(info.name)-1); + + if (copy_to_user((void *)arg, &info, sizeof(info))) + err = -EFAULT; + break; + default: + err = -ENOIOCTLCMD; + break; + } + return err; +} + + +/* + * Function irtty_open(tty) + * + * This function is called by the TTY module when the IrDA line + * discipline is called for. Because we are sure the tty line exists, + * we only have to link it to a free IrDA channel. + */ +static int irtty_open(struct tty_struct *tty) +{ + struct sir_dev *dev; + struct sirtty_cb *priv; + char hwname[16]; + 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; + + /* First make sure we're not already connected. */ + if (tty->disc_data != NULL) { + priv = tty->disc_data; + if (priv && priv->magic == IRTTY_MAGIC) { + ret = -EEXIST; + goto out; + } + tty->disc_data = NULL; /* ### */ + } + + /* stop the underlying driver */ + irtty_stop_receiver(tty, TRUE); + if (tty->driver.stop) + tty->driver.stop(tty); + + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + +/* from old irtty - but what is it good for? + * we _are_ the ldisc and we _dont_ implement flush_buffer! + * + * if (tty->ldisc.flush_buffer) + * tty->ldisc.flush_buffer(tty); + */ + + + /* create device name - could we use tty_name() here? */ + + if (strchr(tty->driver.name, '%')) { + sprintf(hwname, tty->driver.name, + minor(tty->device) - tty->driver.minor_start + + tty->driver.name_base); + } + else { + sprintf(hwname, "%s%d", tty->driver.name, + minor(tty->device) - tty->driver.minor_start + + tty->driver.name_base); + } + + /* apply mtt override */ + sir_tty_drv.qos_mtt_bits = qos_mtt_bits; + + /* get a sir device instance for this driver */ + dev = sirdev_get_instance(&sir_tty_drv, hwname); + if (!dev) { + ret = -ENODEV; + goto out; + } + + /* allocate private device info block */ + priv = kmalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + goto out_put; + memset(priv, 0, sizeof(*priv)); + + priv->magic = IRTTY_MAGIC; + priv->tty = tty; + priv->dev = dev; + + /* serialize with start_dev - in case we were racing with ifup */ + down(&irtty_sem); + + dev->priv = priv; + tty->disc_data = priv; + + up(&irtty_sem); + + printk(KERN_INFO "%s - done\n", __FUNCTION__); + + return 0; + +out_put: + sirdev_put_instance(dev); +out: + MOD_DEC_USE_COUNT; + return ret; +} + +/* + * Function irtty_close (tty) + * + * Close down a IrDA channel. This means flushing out any pending queues, + * and then restoring the TTY line discipline to what it was before it got + * hooked to IrDA (which usually is TTY again). + */ +static void irtty_close(struct tty_struct *tty) +{ + struct sirtty_cb *priv = tty->disc_data; + + if (!priv || priv->magic != IRTTY_MAGIC) + return; + + /* Hm, with a dongle attached the dongle driver wants + * to close the dongle - which requires the use of + * some tty write and/or termios or ioctl operations. + * Are we allowed to call those when already requested + * to shutdown the ldisc? + * If not, we should somehow mark the dev being staled. + * Question remains, how to close the dongle in this case... + * For now let's assume we are granted to issue tty driver calls + * until we return here from the ldisc close. I'm just wondering + * how this behaves with hotpluggable serial hardware like + * rs232-pcmcia card or usb-serial... + * + * priv->tty = NULL?; + */ + + /* we are dead now */ + tty->disc_data = 0; + + sirdev_put_instance(priv->dev); + + /* Stop tty */ + irtty_stop_receiver(tty, TRUE); + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + if (tty->driver.stop) + tty->driver.stop(tty); + + kfree(priv); + + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------- */ + +static struct tty_ldisc irda_ldisc = { + .magic = TTY_LDISC_MAGIC, + .name = "irda", + .flags = 0, + .open = irtty_open, + .close = irtty_close, + .read = NULL, + .write = NULL, + .ioctl = irtty_ioctl, + .poll = NULL, + .receive_buf = irtty_receive_buf, + .receive_room = irtty_receive_room, + .write_wakeup = irtty_write_wakeup, +}; + +/* ------------------------------------------------------- */ + +static int __init irtty_sir_init(void) +{ + int err; + + if ((err = tty_register_ldisc(N_IRDA, &irda_ldisc)) != 0) + ERROR("IrDA: can't register line discipline (err = %d)\n", + err); + return err; +} + +static void __exit irtty_sir_cleanup(void) +{ + int err; + + if ((err = tty_register_ldisc(N_IRDA, NULL))) { + ERROR("%s(), can't unregister line discipline (err = %d)\n", + __FUNCTION__, err); + } +} + +module_init(irtty_sir_init); +module_exit(irtty_sir_cleanup); + +MODULE_AUTHOR("Dag Brattli "); +MODULE_DESCRIPTION("IrDA TTY device driver"); +MODULE_LICENSE("GPL"); + diff -u -p --new-file linux/drivers/net/irda-d6/irtty-sir.h linux/drivers/net/irda/irtty-sir.h --- linux/drivers/net/irda-d6/irtty-sir.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/irda/irtty-sir.h Thu Oct 31 15:53:41 2002 @@ -0,0 +1,34 @@ +/********************************************************************* + * + * sir_tty.h: definitions for the irtty_sir client driver (former irtty) + * + * Copyright (c) 2002 Martin Diehl + * + * 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. + * + ********************************************************************/ + +#ifndef IRTTYSIR_H +#define IRTTYSIR_H + +#include +#include // chipio_t + +#define IRTTY_IOC_MAGIC 'e' +#define IRTTY_IOCTDONGLE _IO(IRTTY_IOC_MAGIC, 1) +#define IRTTY_IOCGET _IOR(IRTTY_IOC_MAGIC, 2, struct irtty_info) +#define IRTTY_IOC_MAXNR 2 + +struct sirtty_cb { + magic_t magic; + + struct sir_dev *dev; + struct tty_struct *tty; + + chipio_t io; /* IrDA controller information */ +}; + +#endif diff -u -p --new-file linux/drivers/net/irda-d6/sir-dev.h linux/drivers/net/irda/sir-dev.h --- linux/drivers/net/irda-d6/sir-dev.h Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/irda/sir-dev.h Thu Oct 31 15:54:20 2002 @@ -0,0 +1,203 @@ +/********************************************************************* + * + * sir.h: include file for irda-sir device abstraction layer + * + * Copyright (c) 2002 Martin Diehl + * + * 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. + * + ********************************************************************/ + +#ifndef IRDA_SIR_H +#define IRDA_SIR_H + +#include + +#include +#include // iobuff_t + +/* FIXME: unify irda_request with sir_fsm! */ + +struct irda_request { + struct list_head lh_request; + unsigned long pending; + void (*func)(void *); + void *data; + struct timer_list timer; +}; + +struct sir_fsm { + struct semaphore sem; + struct irda_request rq; + unsigned state, substate; + int param; + int result; +}; + +#define SIRDEV_STATE_WAIT_TX_COMPLETE 0x0100 + +/* substates for wait_tx_complete */ +#define SIRDEV_STATE_WAIT_XMIT 0x0101 +#define SIRDEV_STATE_WAIT_UNTIL_SENT 0x0102 +#define SIRDEV_STATE_TX_DONE 0x0103 + +#define SIRDEV_STATE_DONGLE_OPEN 0x0300 + +/* 0x0301-0x03ff reserved for individual dongle substates */ + +#define SIRDEV_STATE_DONGLE_CLOSE 0x0400 + +/* 0x0401-0x04ff reserved for individual dongle substates */ + +#define SIRDEV_STATE_SET_DTR_RTS 0x0500 + +#define SIRDEV_STATE_SET_SPEED 0x0700 +#define SIRDEV_STATE_DONGLE_CHECK 0x0800 +#define SIRDEV_STATE_DONGLE_RESET 0x0900 + +/* 0x0901-0x09ff reserved for individual dongle substates */ + +#define SIRDEV_STATE_DONGLE_SPEED 0x0a00 +/* 0x0a01-0x0aff reserved for individual dongle substates */ + +#define SIRDEV_STATE_PORT_SPEED 0x0b00 +#define SIRDEV_STATE_DONE 0x0c00 +#define SIRDEV_STATE_ERROR 0x0d00 +#define SIRDEV_STATE_COMPLETE 0x0e00 + +#define SIRDEV_STATE_DEAD 0xffff + + +struct sir_dev; + +struct dongle_driver { + + struct module *owner; + + const char *driver_name; + + IRDA_DONGLE type; + + int (*open)(struct sir_dev *dev); + int (*close)(struct sir_dev *dev); + int (*reset)(struct sir_dev *dev); + int (*set_speed)(struct sir_dev *dev, unsigned speed); + + struct list_head dongle_list; +}; + +struct sir_driver { + + struct module *owner; + + const char *driver_name; + + int qos_mtt_bits; + + int (*chars_in_buffer)(struct sir_dev *dev); + void (*wait_until_sent)(struct sir_dev *dev); + int (*set_speed)(struct sir_dev *dev, unsigned speed); + int (*set_dtr_rts)(struct sir_dev *dev, int dtr, int rts); + + int (*do_write)(struct sir_dev *dev, const unsigned char *ptr, size_t len); + + int (*start_dev)(struct sir_dev *dev); + int (*stop_dev)(struct sir_dev *dev); +}; + + +/* exported */ + +extern int irda_register_dongle(struct dongle_driver *new); +extern int irda_unregister_dongle(struct dongle_driver *drv); + +extern struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *name); +extern int sirdev_put_instance(struct sir_dev *self); + +extern int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type); +extern void sirdev_write_complete(struct sir_dev *dev); +extern int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count); + +/* not exported */ + +extern int sirdev_get_dongle(struct sir_dev *self, IRDA_DONGLE type); +extern int sirdev_put_dongle(struct sir_dev *self); + +extern int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len); +extern int sirdev_raw_read(struct sir_dev *dev, char *buf, int len); +extern void sirdev_enable_rx(struct sir_dev *dev); + +extern int sirdev_schedule_request(struct sir_dev *dev, int state, unsigned param); +extern int __init irda_thread_create(void); +extern void __exit irda_thread_join(void); + +/* inline helpers */ + +static inline int sirdev_schedule_speed(struct sir_dev *dev, unsigned speed) +{ + return sirdev_schedule_request(dev, SIRDEV_STATE_SET_SPEED, speed); +} + +static inline int sirdev_schedule_dongle_open(struct sir_dev *dev, int dongle_id) +{ + return sirdev_schedule_request(dev, SIRDEV_STATE_DONGLE_OPEN, dongle_id); +} + +static inline int sirdev_schedule_dongle_close(struct sir_dev *dev) +{ + return sirdev_schedule_request(dev, SIRDEV_STATE_DONGLE_CLOSE, 0); +} + +static inline int sirdev_schedule_dtr_rts(struct sir_dev *dev, int dtr, int rts) +{ + int dtrrts; + + dtrrts = ((dtr) ? 0x02 : 0x00) | ((rts) ? 0x01 : 0x00); + return sirdev_schedule_request(dev, SIRDEV_STATE_SET_DTR_RTS, dtrrts); +} + +#if 0 +static inline int sirdev_schedule_mode(struct sir_dev *dev, int mode) +{ + return sirdev_schedule_request(dev, SIRDEV_STATE_SET_MODE, mode); +} +#endif + + +struct sir_dev { + struct net_device *netdev; + struct net_device_stats stats; + + struct irlap_cb *irlap; + + struct qos_info qos; + + char hwname[32]; + + struct sir_fsm fsm; + atomic_t enable_rx; + spinlock_t tx_lock; + + u32 new_speed; + u32 flags; + + unsigned speed; + + iobuff_t tx_buff; /* Transmit buffer */ + iobuff_t rx_buff; /* Receive buffer */ + struct sk_buff *tx_skb; + + const struct dongle_driver * dongle_drv; + const struct sir_driver * drv; + void *priv; + + /* dongle callbacks to the SIR device */ + int (*read)(struct sir_dev *, char *buf, int len); + int (*write)(struct sir_dev *, const char *buf, int len); + int (*set_dtr_rts)(struct sir_dev *, int dtr, int rts); +}; + +#endif /* IRDA_SIR_H */ diff -u -p --new-file linux/drivers/net/irda-d6/sir_core.c linux/drivers/net/irda/sir_core.c --- linux/drivers/net/irda-d6/sir_core.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/irda/sir_core.c Thu Oct 31 16:17:07 2002 @@ -0,0 +1,52 @@ +/********************************************************************* + * + * sir_core.c: module core for irda-sir abstraction layer + * + * Copyright (c) 2002 Martin Diehl + * + * 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. + * + ********************************************************************/ + +#include +#include +#include + +#include + +#include "sir-dev.h" + +/***************************************************************************/ + +MODULE_AUTHOR("Martin Diehl "); +MODULE_DESCRIPTION("IrDA SIR core"); +MODULE_LICENSE("GPL"); + +/***************************************************************************/ + +EXPORT_SYMBOL(irda_register_dongle); +EXPORT_SYMBOL(irda_unregister_dongle); + +EXPORT_SYMBOL(sirdev_get_instance); +EXPORT_SYMBOL(sirdev_put_instance); + +EXPORT_SYMBOL(sirdev_set_dongle); +EXPORT_SYMBOL(sirdev_write_complete); +EXPORT_SYMBOL(sirdev_receive); + +static int __init sir_core_init(void) +{ + return irda_thread_create(); +} + +static void __exit sir_core_exit(void) +{ + irda_thread_join(); +} + +module_init(sir_core_init); +module_exit(sir_core_exit); + diff -u -p --new-file linux/drivers/net/irda-d6/sir_dev.c linux/drivers/net/irda/sir_dev.c --- linux/drivers/net/irda-d6/sir_dev.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/irda/sir_dev.c Thu Oct 31 17:50:08 2002 @@ -0,0 +1,673 @@ +/********************************************************************* + * + * sir_dev.c: irda sir network device + * + * Copyright (c) 2002 Martin Diehl + * + * 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. + * + ********************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include + +#include "sir-dev.h" + +/***************************************************************************/ + +void sirdev_enable_rx(struct sir_dev *dev) +{ + if (unlikely(atomic_read(&dev->enable_rx))) + return; + + /* flush rx-buffer - should also help in case of problems with echo cancelation */ + dev->rx_buff.data = dev->rx_buff.head; + dev->tx_buff.len = 0; + atomic_set(&dev->enable_rx, 1); +} + +static int sirdev_is_receiving(struct sir_dev *dev) +{ + if (!atomic_read(&dev->enable_rx)) + return 0; + + return (dev->rx_buff.state != OUTSIDE_FRAME); +} + +int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type) +{ + int err; + + IRDA_DEBUG(3, "%s : requesting dongle %d.\n", __FUNCTION__, type); + + err = sirdev_schedule_dongle_open(dev, type); + if (unlikely(err)) + return err; + down(&dev->fsm.sem); /* block until config change completed */ + err = dev->fsm.result; + up(&dev->fsm.sem); + return err; +} + +/* used by dongle drivers for dongle programming */ + +int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len) +{ + int ret; + + if (unlikely(len > dev->tx_buff.truesize)) + return -ENOSPC; + + spin_lock_bh(&dev->tx_lock); /* serialize with other tx operations */ + while (dev->tx_buff.len > 0) { /* wait until tx idle */ + spin_unlock_bh(&dev->tx_lock); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(MSECS_TO_JIFFIES(10)); + spin_lock_bh(&dev->tx_lock); + } + + dev->tx_buff.data = dev->tx_buff.head; + memcpy(dev->tx_buff.data, buf, len); + + ret = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len); + spin_unlock_bh(&dev->tx_lock); + return ret; +} + +/* seems some dongle drivers may need this */ + +int sirdev_raw_read(struct sir_dev *dev, char *buf, int len) +{ + int count; + + if (atomic_read(&dev->enable_rx)) + return -EIO; /* fail if we expect irda-frames */ + + count = (len < dev->rx_buff.len) ? len : dev->rx_buff.len; + + if (count > 0) + memcpy(buf, dev->rx_buff.head, count); + + /* forget trailing stuff */ + dev->rx_buff.data = dev->rx_buff.head; + dev->rx_buff.len = 0; + dev->rx_buff.state = OUTSIDE_FRAME; + + return count; +} + +/**********************************************************************/ + +/* called from client driver - likely with bh-context - to indicate + * it made some progress with transmission. Hence we send the next + * chunk, if any, or complete the skb otherwise + */ + +void sirdev_write_complete(struct sir_dev *dev) +{ + struct sk_buff *skb; + int actual = 0; + int err; + + spin_lock_bh(&dev->tx_lock); + + IRDA_DEBUG(3, "%s() - dev->tx_buff.len = %d\n", + __FUNCTION__, dev->tx_buff.len); + + if (likely(dev->tx_buff.len > 0)) { + /* Write data left in transmit buffer */ + actual = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len); + + if (likely(actual>0)) { + dev->tx_buff.data += actual; + dev->tx_buff.len -= actual; + } + else if (unlikely(actual<0)) { + /* could be dropped later when we have tx_timeout to recover */ + ERROR("%s: drv->do_write failed (%d)\n", __FUNCTION__, actual); + if ((skb=dev->tx_skb) != NULL) { + dev->tx_skb = NULL; + dev_kfree_skb_any(skb); + dev->stats.tx_errors++; + dev->stats.tx_dropped++; + } + dev->tx_buff.len = 0; + } + if (dev->tx_buff.len > 0) { + spin_unlock_bh(&dev->tx_lock); + return; + } + } + + /* we have finished now sending this skb. + * update statistics and free the skb. + * finally we check and trigger a pending speed change, if any. + * if not we switch to rx mode and wake the queue for further + * packets. + * note the scheduled speed request blocks until the lower + * client driver and the corresponding hardware has really + * finished sending all data (xmit fifo drained f.e.) + * before the speed change gets finally done and the queue + * re-activated. + */ + + IRDA_DEBUG(5, "%s(), finished with frame!\n", __FUNCTION__); + + if ((skb=dev->tx_skb) != NULL) { + dev->tx_skb = NULL; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + } + + if (unlikely(dev->new_speed > 0)) { + IRDA_DEBUG(5, "%s(), Changing speed!\n", __FUNCTION__); + err = sirdev_schedule_speed(dev, dev->new_speed); + if (unlikely(err)) { + /* should never happen + * forget the speed change and hope the stack recovers + */ + ERROR("%s - schedule speed change failed: %d\n", __FUNCTION__, err); + netif_wake_queue(dev->netdev); + } + /* else: success + * speed change in progress now + * on completion dev->new_speed gets cleared, + * rx-reenabled and the queue restarted + */ + } + else { + sirdev_enable_rx(dev); + netif_wake_queue(dev->netdev); + } + + spin_unlock_bh(&dev->tx_lock); +} + +/* called from client driver - likely with bh-context - to give us + * some more received bytes. We put them into the rx-buffer, + * normally unwrapping and building LAP-skb's (unless rx disabled) + */ + +int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count) +{ + if (!dev || !dev->netdev) { + IRDA_DEBUG(0, "%s(), not ready yet!\n", __FUNCTION__); + /* Use WARNING instead of IRDA_DEBUG */ + return -1; + } + + if (!dev->irlap) { + IRDA_DEBUG(0, "%s - too early: %p / %d!\n", __FUNCTION__, cp, count); + /* Use WARNING instead of IRDA_DEBUG */ + return -1; + } + + if (cp==NULL) { + /* error already at lower level receive + * just update stats and set media busy + */ + irda_device_set_media_busy(dev->netdev, TRUE); + dev->stats.rx_dropped++; + printk(KERN_INFO "%s; rx-drop: %d\n", __FUNCTION__, count); + return 0; + } + + /* Read the characters into the buffer */ + while (count--) { + if (likely(atomic_read(&dev->enable_rx))) { + /* Unwrap and destuff one byte */ + async_unwrap_char(dev->netdev, &dev->stats, + &dev->rx_buff, *cp++); + } + else { + /* rx not enabled: save the raw bytes and never + * trigger any netif_rx. The received bytes are flushed + * later when we re-enable rx but might be read meanwhile + * by the dongle driver. + */ + dev->rx_buff.data[dev->rx_buff.len++] = *cp++; + } + + /* What should we do when the buffer is full? */ + if (unlikely(dev->rx_buff.len == dev->rx_buff.truesize)) + dev->rx_buff.len = 0; + + } + + return 0; +} + +/**********************************************************************/ + +/* callbacks from network layer */ + +static struct net_device_stats *sirdev_get_stats(struct net_device *ndev) +{ + struct sir_dev *dev = ndev->priv; + + return (dev) ? &dev->stats : NULL; +} + +static int sirdev_hard_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct sir_dev *dev = ndev->priv; + int actual = 0; + int err; + s32 speed; + + ASSERT(dev != NULL, return 0;); + + netif_stop_queue(ndev); + + IRDA_DEBUG(3, "%s(), skb->len = %d\n", __FUNCTION__, skb->len); + + speed = irda_get_next_speed(skb); + if ((speed != dev->speed) && (speed != -1)) { + if (!skb->len) { + err = sirdev_schedule_speed(dev, speed); + if (unlikely(err == -EWOULDBLOCK)) { + /* Failed to initiate the speed change, likely the fsm + * is still busy (pretty unlikely, but...) + * We refuse to accept the skb and return with the queue + * stopped so the network layer will retry after the + * fsm completes and wakes the queue. + */ + return 1; + } + else if (unlikely(err)) { + /* other fatal error - forget the speed change and + * hope the stack will recover somehow + */ + netif_start_queue(ndev); + } + /* else: success + * speed change in progress now + * on completion the queue gets restarted + */ + + dev_kfree_skb_any(skb); + return 0; + } else + dev->new_speed = speed; + } + + /* Init tx buffer*/ + dev->tx_buff.data = dev->tx_buff.head; + + /* Check problems */ + if(spin_is_locked(&dev->tx_lock)) { + IRDA_DEBUG(3, "%s(), write not completed\n", __FUNCTION__); + } + + /* serialize with write completion */ + spin_lock_bh(&dev->tx_lock); + + /* Copy skb to tx_buff while wrapping, stuffing and making CRC */ + dev->tx_buff.len = async_wrap_skb(skb, dev->tx_buff.data, dev->tx_buff.truesize); + + /* transmission will start now - disable receive. + * if we are just in the middle of an incoming frame, + * treat it as collision. probably it's a good idea to + * reset the rx_buf OUTSIDE_FRAME in this case too? + */ + atomic_set(&dev->enable_rx, 0); + if (unlikely(sirdev_is_receiving(dev))) + dev->stats.collisions++; + + actual = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len); + + if (likely(actual > 0)) { + dev->tx_skb = skb; + ndev->trans_start = jiffies; + dev->tx_buff.data += actual; + dev->tx_buff.len -= actual; + } + else if (unlikely(actual < 0)) { + /* could be dropped later when we have tx_timeout to recover */ + ERROR("%s: drv->do_write failed (%d)\n", __FUNCTION__, actual); + dev_kfree_skb_any(skb); + dev->stats.tx_errors++; + dev->stats.tx_dropped++; + netif_wake_queue(ndev); + } + spin_unlock_bh(&dev->tx_lock); + + return 0; +} + +/* called from network layer with rtnl hold */ + +static int sirdev_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct sir_dev *dev = ndev->priv; + int ret = 0; + + ASSERT(dev != NULL, return -1;); + + IRDA_DEBUG(3, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__, ndev->name, cmd); + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) + ret = -EPERM; + else + ret = sirdev_schedule_speed(dev, irq->ifr_baudrate); + /* cannot sleep here for completion + * we are called from network layer with rtnl hold + */ + break; + + case SIOCSDONGLE: /* Set dongle */ + if (!capable(CAP_NET_ADMIN)) + ret = -EPERM; + else + ret = sirdev_schedule_dongle_open(dev, irq->ifr_dongle); + /* cannot sleep here for completion + * we are called from network layer with rtnl hold + */ + break; + + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) + ret = -EPERM; + else + irda_device_set_media_busy(dev->netdev, TRUE); + break; + + case SIOCGRECEIVING: /* Check if we are receiving right now */ + irq->ifr_receiving = sirdev_is_receiving(dev); + break; + + case SIOCSDTRRTS: + if (!capable(CAP_NET_ADMIN)) + ret = -EPERM; + else + ret = sirdev_schedule_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts); + /* cannot sleep here for completion + * we are called from network layer with rtnl hold + */ + break; + + case SIOCSMODE: +#if 0 + if (!capable(CAP_NET_ADMIN)) + ret = -EPERM; + else + ret = sirdev_schedule_mode(dev, irq->ifr_mode); + /* cannot sleep here for completion + * we are called from network layer with rtnl hold + */ + break; +#endif + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +/* ----------------------------------------------------------------------------- */ + +#define SIRBUF_ALLOCSIZE 4269 /* worst case size of a wrapped IrLAP frame */ + +static int sirdev_alloc_buffers(struct sir_dev *dev) +{ + dev->rx_buff.truesize = SIRBUF_ALLOCSIZE; + dev->tx_buff.truesize = SIRBUF_ALLOCSIZE; + + dev->rx_buff.head = kmalloc(dev->rx_buff.truesize, GFP_KERNEL); + if (dev->rx_buff.head == NULL) + return -ENOMEM; + memset(dev->rx_buff.head, 0, dev->rx_buff.truesize); + + dev->tx_buff.head = kmalloc(dev->tx_buff.truesize, GFP_KERNEL); + if (dev->tx_buff.head == NULL) { + kfree(dev->rx_buff.head); + dev->rx_buff.head = NULL; + return -ENOMEM; + memset(dev->tx_buff.head, 0, dev->tx_buff.truesize); + } + + dev->tx_buff.data = dev->tx_buff.head; + dev->rx_buff.data = dev->rx_buff.head; + dev->tx_buff.len = 0; + dev->rx_buff.len = 0; + + dev->rx_buff.in_frame = FALSE; + dev->rx_buff.state = OUTSIDE_FRAME; + return 0; +}; + +static void sirdev_free_buffers(struct sir_dev *dev) +{ + if (dev->rx_buff.head) + kfree(dev->rx_buff.head); + if (dev->tx_buff.head) + kfree(dev->tx_buff.head); + dev->rx_buff.head = dev->tx_buff.head = NULL; +} + +static int sirdev_open(struct net_device *ndev) +{ + struct sir_dev *dev = ndev->priv; + const struct sir_driver *drv = dev->drv; + + if (!drv) + return -ENODEV; + + lock_kernel(); /* serialize with rmmod */ + /* increase the reference count of the driver module before doing serious stuff */ + if (drv->owner && !try_inc_mod_count(drv->owner)) { + unlock_kernel(); + return -ESTALE; + } + unlock_kernel(); + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + if (sirdev_alloc_buffers(dev)) + goto errout_dec; + + if (!dev->drv->start_dev || dev->drv->start_dev(dev)) + goto errout_free; + + sirdev_enable_rx(dev); + + netif_start_queue(ndev); + dev->irlap = irlap_open(ndev, &dev->qos, dev->hwname); + if (!dev->irlap) + goto errout_stop; + + netif_wake_queue(ndev); + + printk(KERN_INFO "%s - done, speed = %d\n", __FUNCTION__, dev->speed); + + return 0; + +errout_stop: + atomic_set(&dev->enable_rx, 0); + if (dev->drv->stop_dev) + dev->drv->stop_dev(dev); +errout_free: + sirdev_free_buffers(dev); +errout_dec: + if (drv->owner) + __MOD_DEC_USE_COUNT(drv->owner); + return -EAGAIN; +} + +static int sirdev_close(struct net_device *ndev) +{ + struct sir_dev *dev = ndev->priv; + const struct sir_driver *drv; + + printk(KERN_INFO "%s\n", __FUNCTION__); + + netif_stop_queue(ndev); + + down(&dev->fsm.sem); /* block on pending config completion */ + + atomic_set(&dev->enable_rx, 0); + + if (unlikely(!dev->irlap)) + goto out; + irlap_close(dev->irlap); + dev->irlap = NULL; + + drv = dev->drv; + if (unlikely(!drv || !dev->priv)) + goto out; + + if (drv->stop_dev) + drv->stop_dev(dev); + + sirdev_free_buffers(dev); + + lock_kernel(); + if (drv->owner) + __MOD_DEC_USE_COUNT(drv->owner); + unlock_kernel(); + +out: + dev->speed = 0; + up(&dev->fsm.sem); + return 0; +} + +/* ----------------------------------------------------------------------------- */ + +static int sirdev_init(struct net_device *ndev) +{ + struct sir_dev *dev = ndev->priv; + + SET_MODULE_OWNER(ndev); + + /* Set up to be a normal IrDA network device driver */ + irda_device_setup(ndev); + + dev->flags = IFF_SIR | IFF_PIO; + + /* Override the network functions we need to use */ + ndev->hard_start_xmit = sirdev_hard_xmit; + ndev->open = sirdev_open; + ndev->stop = sirdev_close; + ndev->get_stats = sirdev_get_stats; + ndev->do_ioctl = sirdev_ioctl; + + return 0; +} + + +struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *name) +{ + struct net_device *ndev; + struct sir_dev *dev; + + printk(KERN_INFO "%s - %s\n", __FUNCTION__, name); + + /* instead of adding tests to protect against drv->do_write==NULL + * at several places we refuse to create a sir_dev instance for + * drivers which dont implement do_write. + */ + if (!drv || !drv->do_write) + return NULL; + + /* + * Allocate new instance of the device + */ + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + printk(KERN_ERR "IrDA: Can't allocate memory for " + "IrDA control block!\n"); + goto out; + } + memset(dev, 0, sizeof(*dev)); + + irda_init_max_qos_capabilies(&dev->qos); + dev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + dev->qos.min_turn_time.bits = drv->qos_mtt_bits; + irda_qos_bits_to_value(&dev->qos); + + strncpy(dev->hwname, name, sizeof(dev->hwname)-1); + + ndev = kmalloc(sizeof(*ndev), GFP_KERNEL); + if (ndev == NULL) + goto out_freedev; + memset(ndev, 0, sizeof(*ndev)); + + atomic_set(&dev->enable_rx, 0); + dev->tx_skb = NULL; + + spin_lock_init(&dev->tx_lock); + init_MUTEX(&dev->fsm.sem); + + INIT_LIST_HEAD(&dev->fsm.rq.lh_request); + dev->fsm.rq.pending = 0; + init_timer(&dev->fsm.rq.timer); + + dev->drv = drv; + dev->netdev = ndev; + + ndev->priv = (void *) dev; + ndev->init = sirdev_init; + + strcpy(ndev->name, "irda%d"); + if (register_netdev(ndev)) { + ERROR("%s(), register_netdev() failed!\n", __FUNCTION__); + goto out_freenetdev; + } + + return dev; + +out_freenetdev: + kfree(ndev); +out_freedev: + kfree(dev); +out: + return NULL; +} + +int sirdev_put_instance(struct sir_dev *dev) +{ + int err = 0; + + printk(KERN_INFO "%s\n", __FUNCTION__); + + atomic_set(&dev->enable_rx, 0); + + netif_carrier_off(dev->netdev); + netif_device_detach(dev->netdev); + + if (dev->dongle_drv) + err = sirdev_schedule_dongle_close(dev); + if (err) + ERROR("%s - error %d\n", __FUNCTION__, err); + + sirdev_close(dev->netdev); + + down(&dev->fsm.sem); + dev->fsm.state = SIRDEV_STATE_DEAD; /* mark staled */ + dev->dongle_drv = NULL; + dev->priv = NULL; + up(&dev->fsm.sem); + + /* Remove netdevice */ + if (dev->netdev) + unregister_netdev(dev->netdev); + + kfree(dev); + + return 0; +} + diff -u -p --new-file linux/drivers/net/irda-d6/sir_dongle.c linux/drivers/net/irda/sir_dongle.c --- linux/drivers/net/irda-d6/sir_dongle.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/irda/sir_dongle.c Thu Oct 31 17:05:33 2002 @@ -0,0 +1,146 @@ +/********************************************************************* + * + * sir_dongle.c: manager for serial dongle protocol drivers + * + * Copyright (c) 2002 Martin Diehl + * + * 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. + * + ********************************************************************/ + +#include +#include +#include +#include +#include + +#include + +#include "sir-dev.h" + +/************************************************************************** + * + * dongle registration and attachment + * + */ + +static LIST_HEAD(dongle_list); /* list of registered dongle drivers */ +static DECLARE_MUTEX(dongle_list_lock); /* protects the list */ + +int irda_register_dongle(struct dongle_driver *new) +{ + struct list_head *entry; + struct dongle_driver *drv; + + IRDA_DEBUG(0, "%s : registering dongle \"%s\" (%d).\n", + __FUNCTION__, new->driver_name, new->type); + + down(&dongle_list_lock); + list_for_each(entry, &dongle_list) { + drv = list_entry(entry, struct dongle_driver, dongle_list); + if (new->type == drv->type) { + up(&dongle_list_lock); + return -EEXIST; + } + } + list_add(&new->dongle_list, &dongle_list); + up(&dongle_list_lock); + return 0; +} + +int irda_unregister_dongle(struct dongle_driver *drv) +{ + down(&dongle_list_lock); + list_del(&drv->dongle_list); + up(&dongle_list_lock); + return 0; +} + +int sirdev_get_dongle(struct sir_dev *dev, IRDA_DONGLE type) +{ + struct list_head *entry; + const struct dongle_driver *drv = NULL; + int err = -EINVAL; + +#ifdef CONFIG_KMOD + char modname[30]; + + sprintf(modname, "irda-dongle-%d", type); + request_module(modname); +#endif + + if (dev->dongle_drv != NULL) + return -EBUSY; + + /* serialize access to the list of registered dongles */ + down(&dongle_list_lock); + + list_for_each(entry, &dongle_list) { + drv = list_entry(entry, struct dongle_driver, dongle_list); + if (drv->type == type) + break; + else + drv = NULL; + } + + if (!drv) { + err = -ENODEV; + goto out_unlock; /* no such dongle */ + } + + /* handling of SMP races with dongle module removal - three cases: + * 1) dongle driver was already unregistered - then we haven't found the + * requested dongle above and are already out here + * 2) the module is already marked deleted but the driver is still + * registered - then the try_inc_mod_count() below will fail + * 3) the try_inc_mod_count() below succeeds before the module is marked + * deleted - then sys_delete_module() fails and prevents the removal + * because the module is in use. + */ + + if (drv->owner && !try_inc_mod_count(drv->owner)) { + err = -ESTALE; + goto out_unlock; /* rmmod already pending */ + } + + /* Initialize dongle driver callbacks */ + dev->read = sirdev_raw_read; + dev->write = sirdev_raw_write; + dev->set_dtr_rts = dev->drv->set_dtr_rts; + + dev->dongle_drv = drv; + + if (!drv->open || (err=drv->open(dev))!=0) + goto out_reject; /* failed to open driver */ + + up(&dongle_list_lock); + return 0; + +out_reject: + dev->dongle_drv = NULL; + if (drv->owner) + __MOD_DEC_USE_COUNT(drv->owner); +out_unlock: + up(&dongle_list_lock); + return err; +} + +int sirdev_put_dongle(struct sir_dev *dev) +{ + const struct dongle_driver *drv = dev->dongle_drv; + + if (drv) { + if (drv->close) + drv->close(dev); /* close this dongle instance */ + + dev->dongle_drv = NULL; /* unlink the dongle driver */ + + if (drv->owner) + __MOD_DEC_USE_COUNT(drv->owner);/* decrement driver's module refcount */ + } + + return 0; +} diff -u -p --new-file linux/drivers/net/irda-d6/sir_kthread.c linux/drivers/net/irda/sir_kthread.c --- linux/drivers/net/irda-d6/sir_kthread.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/irda/sir_kthread.c Thu Oct 31 16:17:55 2002 @@ -0,0 +1,543 @@ +/********************************************************************* + * + * sir_kthread.c: dedicated thread to process scheduled + * sir device setup requests + * + * Copyright (c) 2002 Martin Diehl + * + * 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. + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "sir-dev.h" + +/************************************************************************** + * + * kIrDAd kernel thread and config state machine + * + */ + +struct irda_request_queue { + struct list_head request_list; + spinlock_t lock; + task_t *thread; + struct completion exit; + wait_queue_head_t kick, done; + atomic_t num_pending; +}; + +static struct irda_request_queue irda_rq_queue; + +static int irda_queue_request(struct irda_request *rq) +{ + int ret = 0; + unsigned long flags; + + if (!test_and_set_bit(0, &rq->pending)) { + spin_lock_irqsave(&irda_rq_queue.lock, flags); + list_add_tail(&rq->lh_request, &irda_rq_queue.request_list); + wake_up(&irda_rq_queue.kick); + atomic_inc(&irda_rq_queue.num_pending); + spin_unlock_irqrestore(&irda_rq_queue.lock, flags); + ret = 1; + } + return ret; +} + +static void irda_request_timer(unsigned long data) +{ + struct irda_request *rq = (struct irda_request *)data; + unsigned long flags; + + spin_lock_irqsave(&irda_rq_queue.lock, flags); + list_add_tail(&rq->lh_request, &irda_rq_queue.request_list); + wake_up(&irda_rq_queue.kick); + spin_unlock_irqrestore(&irda_rq_queue.lock, flags); +} + +static int irda_queue_delayed_request(struct irda_request *rq, unsigned long delay) +{ + int ret = 0; + struct timer_list *timer = &rq->timer; + + if (!test_and_set_bit(0, &rq->pending)) { + timer->expires = jiffies + delay; + timer->function = irda_request_timer; + timer->data = (unsigned long)rq; + atomic_inc(&irda_rq_queue.num_pending); + add_timer(timer); + ret = 1; + } + return ret; +} + +static void run_irda_queue(void) +{ + unsigned long flags; + struct list_head *entry, *tmp; + struct irda_request *rq; + + spin_lock_irqsave(&irda_rq_queue.lock, flags); + list_for_each_safe(entry, tmp, &irda_rq_queue.request_list) { + rq = list_entry(entry, struct irda_request, lh_request); + list_del_init(entry); + spin_unlock_irqrestore(&irda_rq_queue.lock, flags); + + clear_bit(0, &rq->pending); + rq->func(rq->data); + + if (atomic_dec_and_test(&irda_rq_queue.num_pending)) + wake_up(&irda_rq_queue.done); + + spin_lock_irqsave(&irda_rq_queue.lock, flags); + } + spin_unlock_irqrestore(&irda_rq_queue.lock, flags); +} + +static int irda_rt_prio = 0; /* MODULE_PARM? */ + +static int irda_thread(void *startup) +{ + DECLARE_WAITQUEUE(wait, current); + + daemonize(); + strcpy(current->comm, "kIrDAd"); + + spin_lock_irq(¤t->sig->siglock); + sigfillset(¤t->blocked); + recalc_sigpending(); + spin_unlock_irq(¤t->sig->siglock); + + set_fs(KERNEL_DS); + + if (irda_rt_prio > 0) { +#if 0 /* works but requires EXPORT_SYMBOL(setscheduler) */ + struct sched_param param; + + param.sched_priority = irda_rt_prio; + setscheduler(0, SCHED_FIFO, ¶m); +#endif + +#if 0 /* doesn't work - has some tendency to trigger instant reboot! + * looks like we would have to deactivate current on the + * runqueue - which is only possible inside of kernel/sched.h + */ + + /* runqueues are per-cpu and we are current on this cpu. Hence + * The tasklist_lock with irq-off protects our runqueue too + * and we don't have to lock it (which would be impossible, + * because it is private in kernel/sched.c) + */ + + read_lock_irq(&tasklist_lock); + current->rt_priority = (irda_rt_priopolicy = SCHED_FIFO; + current->prio = MAX_USER_RT_PRIO-1 - irda_rt_prio; + read_unlock_irq(&tasklist_lock); +#endif + } + + irda_rq_queue.thread = current; + + complete((struct completion *)startup); + + while (irda_rq_queue.thread != NULL) { + + set_task_state(current, TASK_UNINTERRUPTIBLE); + add_wait_queue(&irda_rq_queue.kick, &wait); + if (list_empty(&irda_rq_queue.request_list)) + schedule(); + else + set_task_state(current, TASK_RUNNING); + remove_wait_queue(&irda_rq_queue.kick, &wait); + + run_irda_queue(); + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,35) + reparent_to_init(); +#endif + complete_and_exit(&irda_rq_queue.exit, 0); + /* never reached */ + return 0; +} + + +static void flush_irda_queue(void) +{ + if (atomic_read(&irda_rq_queue.num_pending)) { + + DECLARE_WAITQUEUE(wait, current); + + if (!list_empty(&irda_rq_queue.request_list)) + run_irda_queue(); + + set_task_state(current, TASK_UNINTERRUPTIBLE); + add_wait_queue(&irda_rq_queue.done, &wait); + if (atomic_read(&irda_rq_queue.num_pending)) + schedule(); + else + set_task_state(current, TASK_RUNNING); + remove_wait_queue(&irda_rq_queue.done, &wait); + } +} + +/* substate handler of the config-fsm to handle the cases where we want + * to wait for transmit completion before changing the port configuration + */ + +static int irda_tx_complete_fsm(struct sir_dev *dev) +{ + struct sir_fsm *fsm = &dev->fsm; + unsigned next_state, delay; + unsigned bytes_left; + + do { + next_state = fsm->substate; /* default: stay in current substate */ + delay = 0; + + switch(fsm->substate) { + + case SIRDEV_STATE_WAIT_XMIT: + if (dev->drv->chars_in_buffer) + bytes_left = dev->drv->chars_in_buffer(dev); + else + bytes_left = 0; + if (!bytes_left) { + next_state = SIRDEV_STATE_WAIT_UNTIL_SENT; + break; + } + + if (dev->speed > 115200) + delay = (bytes_left*8*10000) / (dev->speed/100); + else if (dev->speed > 0) + delay = (bytes_left*10*10000) / (dev->speed/100); + else + delay = 0; + /* expected delay (usec) until remaining bytes are sent */ + if (delay < 100) { + udelay(delay); + delay = 0; + break; + } + /* sleep some longer delay (msec) */ + delay = (delay+999) / 1000; + break; + + case SIRDEV_STATE_WAIT_UNTIL_SENT: + /* block until underlaying hardware buffer are empty */ + if (dev->drv->wait_until_sent) + dev->drv->wait_until_sent(dev); + next_state = SIRDEV_STATE_TX_DONE; + break; + + case SIRDEV_STATE_TX_DONE: + return 0; + + default: + ERROR("%s - undefined state\n", __FUNCTION__); + return -EINVAL; + } + fsm->substate = next_state; + } while (delay == 0); + return delay; +} + +/* + * Function irda_config_fsm + * + * State machine to handle the configuration of the device (and attached dongle, if any). + * This handler is scheduled for execution in kIrDAd context, so we can sleep. + * however, kIrDAd is shared by all sir_dev devices so we better don't sleep there too + * long. Instead, for longer delays we start a timer to reschedule us later. + * On entry, fsm->sem is always locked and the netdev xmit queue stopped. + * Both must be unlocked/restarted on completion - but only on final exit. + */ + +static void irda_config_fsm(void *data) +{ + struct sir_dev *dev = data; + struct sir_fsm *fsm = &dev->fsm; + int next_state; + int ret = -1; + unsigned delay; + + IRDA_DEBUG(2, "%s(), <%ld>\n", __FUNCTION__, jiffies); + + do { + IRDA_DEBUG(3, "%s - state=0x%04x / substate=0x%04x\n", + __FUNCTION__, fsm->state, fsm->substate); + + next_state = fsm->state; + delay = 0; + + switch(fsm->state) { + + case SIRDEV_STATE_DONGLE_OPEN: + if (dev->dongle_drv != NULL) { + ret = sirdev_put_dongle(dev); + if (ret) { + fsm->result = -EINVAL; + next_state = SIRDEV_STATE_ERROR; + break; + } + } + + /* Initialize dongle */ + ret = sirdev_get_dongle(dev, fsm->param); + if (ret) { + fsm->result = ret; + next_state = SIRDEV_STATE_ERROR; + break; + } + + /* Dongles are powered through the modem control lines which + * were just set during open. Before resetting, let's wait for + * the power to stabilize. This is what some dongle drivers did + * in open before, while others didn't - should be safe anyway. + */ + + delay = 50; + fsm->substate = SIRDEV_STATE_DONGLE_RESET; + next_state = SIRDEV_STATE_DONGLE_RESET; + + fsm->param = 9600; + + break; + + case SIRDEV_STATE_DONGLE_CLOSE: + /* shouldn't we just treat this as success=? */ + if (dev->dongle_drv == NULL) { + fsm->result = -EINVAL; + next_state = SIRDEV_STATE_ERROR; + break; + } + + ret = sirdev_put_dongle(dev); + if (ret) { + fsm->result = ret; + next_state = SIRDEV_STATE_ERROR; + break; + } + next_state = SIRDEV_STATE_DONE; + break; + + case SIRDEV_STATE_SET_DTR_RTS: + if (dev->drv->set_dtr_rts) { + int dtr, rts; + + dtr = (fsm->param&0x02) ? TRUE : FALSE; + rts = (fsm->param&0x01) ? TRUE : FALSE; + ret = dev->drv->set_dtr_rts(dev,dtr,rts); + } + else + ret = -EINVAL; + next_state = SIRDEV_STATE_DONE; + break; + + case SIRDEV_STATE_SET_SPEED: + fsm->substate = SIRDEV_STATE_WAIT_XMIT; + next_state = SIRDEV_STATE_DONGLE_CHECK; + break; + + case SIRDEV_STATE_DONGLE_CHECK: + ret = irda_tx_complete_fsm(dev); + if (ret < 0) { + fsm->result = ret; + next_state = SIRDEV_STATE_ERROR; + break; + } + if ((delay=ret) != 0) + break; + + if (dev->dongle_drv) { + fsm->substate = SIRDEV_STATE_DONGLE_RESET; + next_state = SIRDEV_STATE_DONGLE_RESET; + } + else { + dev->speed = fsm->param; + next_state = SIRDEV_STATE_PORT_SPEED; + } + break; + + case SIRDEV_STATE_DONGLE_RESET: + if (dev->dongle_drv->reset) { + ret = dev->dongle_drv->reset(dev); + if (ret < 0) { + fsm->result = ret; + next_state = SIRDEV_STATE_ERROR; + break; + } + } + else + ret = 0; + if ((delay=ret) == 0) { + /* set serial port according to dongle default speed */ + if (dev->drv->set_speed) + dev->drv->set_speed(dev, dev->speed); + fsm->substate = SIRDEV_STATE_DONGLE_SPEED; + next_state = SIRDEV_STATE_DONGLE_SPEED; + } + break; + + case SIRDEV_STATE_DONGLE_SPEED: + if (dev->dongle_drv->reset) { + ret = dev->dongle_drv->set_speed(dev, fsm->param); + if (ret < 0) { + fsm->result = ret; + next_state = SIRDEV_STATE_ERROR; + break; + } + } + else + ret = 0; + if ((delay=ret) == 0) + next_state = SIRDEV_STATE_PORT_SPEED; + break; + + case SIRDEV_STATE_PORT_SPEED: + /* Finally we are ready to change the serial port speed */ + if (dev->drv->set_speed) + dev->drv->set_speed(dev, dev->speed); + dev->new_speed = 0; + next_state = SIRDEV_STATE_DONE; + break; + + case SIRDEV_STATE_DONE: + /* Signal network layer so it can send more frames */ + netif_wake_queue(dev->netdev); + next_state = SIRDEV_STATE_COMPLETE; + break; + + default: + ERROR("%s - undefined state\n", __FUNCTION__); + fsm->result = -EINVAL; + /* fall thru */ + + case SIRDEV_STATE_ERROR: + ERROR("%s - error: %d\n", __FUNCTION__, fsm->result); + +#if 0 /* don't enable this before we have netdev->tx_timeout to recover */ + netif_stop_queue(dev->netdev); +#else + netif_wake_queue(dev->netdev); +#endif + /* fall thru */ + + case SIRDEV_STATE_COMPLETE: + /* config change finished, so we are not busy any longer */ + sirdev_enable_rx(dev); + printk(KERN_INFO "%s - up\n", __FUNCTION__); + up(&fsm->sem); + return; + } + fsm->state = next_state; + } while(!delay); + + irda_queue_delayed_request(&fsm->rq, MSECS_TO_JIFFIES(delay)); +} + +/* schedule some device configuration task for execution by kIrDAd + * on behalf of the above state machine. + * can be called from process or interrupt/tasklet context. + */ + +int sirdev_schedule_request(struct sir_dev *dev, int initial_state, unsigned param) +{ + struct sir_fsm *fsm = &dev->fsm; + int xmit_was_down; + +// IRDA_DEBUG(2, "%s - state=0x%04x / param=%u\n", __FUNCTION__, initial_state, param); + + printk(KERN_INFO "%s - state=0x%04x / param=%u\n", __FUNCTION__, initial_state, param); + + if (in_interrupt()) { + if (down_trylock(&fsm->sem)) { + IRDA_DEBUG(1, "%s(), state machine busy!\n", __FUNCTION__); + return -EWOULDBLOCK; + } + } + else + down(&fsm->sem); + printk(KERN_INFO "%s - down\n", __FUNCTION__); + + if (fsm->state == SIRDEV_STATE_DEAD) { + /* race with sirdev_close should never happen */ + ERROR("%s(), instance staled!\n", __FUNCTION__); + printk(KERN_INFO "%s - up\n", __FUNCTION__); + up(&fsm->sem); + return -ESTALE; /* or better EPIPE? */ + } + + xmit_was_down = netif_queue_stopped(dev->netdev); + netif_stop_queue(dev->netdev); + atomic_set(&dev->enable_rx, 0); + + fsm->state = initial_state; + fsm->param = param; + fsm->result = 0; + + INIT_LIST_HEAD(&fsm->rq.lh_request); + fsm->rq.pending = 0; + fsm->rq.func = irda_config_fsm; + fsm->rq.data = dev; + + if (!irda_queue_request(&fsm->rq)) { /* returns 0 on error! */ + atomic_set(&dev->enable_rx, 1); + if (!xmit_was_down) + netif_wake_queue(dev->netdev); + printk(KERN_INFO "%s - up\n", __FUNCTION__); + up(&fsm->sem); + return -EAGAIN; + } + return 0; +} + +int __init irda_thread_create(void) +{ + struct completion startup; + int pid; + + spin_lock_init(&irda_rq_queue.lock); + irda_rq_queue.thread = NULL; + INIT_LIST_HEAD(&irda_rq_queue.request_list); + init_waitqueue_head(&irda_rq_queue.kick); + init_waitqueue_head(&irda_rq_queue.done); + atomic_set(&irda_rq_queue.num_pending, 0); + + init_completion(&startup); + pid = kernel_thread(irda_thread, &startup, CLONE_FS|CLONE_FILES); + if (pid <= 0) + return -EAGAIN; + else + wait_for_completion(&startup); + + return 0; +} + +void __exit irda_thread_join(void) +{ + if (irda_rq_queue.thread) { + flush_irda_queue(); + init_completion(&irda_rq_queue.exit); + irda_rq_queue.thread = NULL; + wake_up(&irda_rq_queue.kick); + wait_for_completion(&irda_rq_queue.exit); + } +} + diff -u -p --new-file linux/drivers/net/irda-d6/tekram-sir.c linux/drivers/net/irda/tekram-sir.c --- linux/drivers/net/irda-d6/tekram-sir.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/net/irda/tekram-sir.c Thu Oct 31 16:31:09 2002 @@ -0,0 +1,265 @@ +/********************************************************************* + * + * Filename: tekram.c + * Version: 1.3 + * Description: Implementation of the Tekram IrMate IR-210B dongle + * Status: Experimental. + * Author: Dag Brattli + * Created at: Wed Oct 21 20:02:35 1998 + * Modified at: Sun Oct 27 22:02:38 2002 + * Modified by: Martin Diehl + * + * Copyright (c) 1998-1999 Dag Brattli, + * Copyright (c) 2002 Martin Diehl, + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsų admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include + +#include + +#include "sir-dev.h" + +MODULE_PARM(tekram_delay, "i"); +MODULE_PARM_DESC(tekram_delay, "tekram dongle write complete delay"); +static int tekram_delay = 50; /* default is 50 ms */ + +static int tekram_open(struct sir_dev *); +static int tekram_close(struct sir_dev *); +static int tekram_change_speed(struct sir_dev *, unsigned); +static int tekram_reset(struct sir_dev *); + +#define TEKRAM_115200 0x00 +#define TEKRAM_57600 0x01 +#define TEKRAM_38400 0x02 +#define TEKRAM_19200 0x03 +#define TEKRAM_9600 0x04 + +#define TEKRAM_PW 0x10 /* Pulse select bit */ + +static struct dongle_driver tekram = { + .owner = THIS_MODULE, + .driver_name = "Tekram IR-210B", + .type = IRDA_TEKRAM_DONGLE, + .open = tekram_open, + .close = tekram_close, + .reset = tekram_reset, + .set_speed = tekram_change_speed, +}; + +int __init tekram_sir_init(void) +{ + if (tekram_delay < 1 || tekram_delay>500) + tekram_delay = 200; + return irda_register_dongle(&tekram); +} + +void __exit tekram_sir_cleanup(void) +{ + irda_unregister_dongle(&tekram); +} + +#define TEKRAM_STATE_POWERED (SIRDEV_STATE_DONGLE_OPEN + 1) + +static int tekram_open(struct sir_dev *dev) +{ + unsigned delay = 0; + unsigned next_state = dev->fsm.substate; + struct qos_info *qos = &dev->qos; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + switch(dev->fsm.substate) { + + case SIRDEV_STATE_DONGLE_OPEN: + dev->set_dtr_rts(dev, TRUE, TRUE); + next_state = TEKRAM_STATE_POWERED; + delay = 50; + break; + + case TEKRAM_STATE_POWERED: + qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */ + irda_qos_bits_to_value(qos); + return 0; + + default: + ERROR("%s - undefined state\n", __FUNCTION__); + return -EINVAL; + } + + dev->fsm.substate = next_state; + + return delay; +} + +static int tekram_close(struct sir_dev *dev) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* Power off dongle */ + dev->set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function tekram_change_speed (dev, state, speed) + * + * Set the speed for the Tekram IRMate 210 type dongle. Warning, this + * function must be called with a process context! + * + * Algorithm + * 1. clear DTR + * 2. set RTS, and wait at least 7 us + * 3. send Control Byte to the IR-210 through TXD to set new baud rate + * wait until the stop bit of Control Byte is sent (for 9600 baud rate, + * it takes about 100 msec) + * + * [oops, why 100 msec? sending 1 byte (10 bits) takes 1.05 msec + * - is this probably to compensate for delays in tty layer?] + * + * 5. clear RTS (return to NORMAL Operation) + * 6. wait at least 50 us, new setting (baud rate, etc) takes effect here + * after + */ + +#define TEKRAM_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1) + +static int tekram_change_speed(struct sir_dev *dev, unsigned speed) +{ + unsigned delay = 0; + unsigned next_state = dev->fsm.substate; + u8 byte; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + switch(dev->fsm.substate) { + + case SIRDEV_STATE_DONGLE_SPEED: + + switch (speed) { + default: + speed = 9600; + /* fall thru */ + case 9600: + byte = TEKRAM_PW|TEKRAM_9600; + break; + case 19200: + byte = TEKRAM_PW|TEKRAM_19200; + break; + case 38400: + byte = TEKRAM_PW|TEKRAM_38400; + break; + case 57600: + byte = TEKRAM_PW|TEKRAM_57600; + break; + case 115200: + byte = TEKRAM_115200; + break; + } + + /* Set DTR, Clear RTS */ + dev->set_dtr_rts(dev, TRUE, FALSE); + + /* Wait at least 7us */ + udelay(14); + + /* Write control byte */ + dev->write(dev, &byte, 1); + + dev->speed = speed; + + next_state = TEKRAM_STATE_WAIT_SPEED; + delay = tekram_delay; /* default: 50 ms */ + break; + + case TEKRAM_STATE_WAIT_SPEED: + /* Set DTR, Set RTS */ + dev->set_dtr_rts(dev, TRUE, TRUE); + + udelay(50); + + return 0; + + default: + ERROR("%s - undefined state\n", __FUNCTION__); + return -EINVAL; + } + + dev->fsm.substate = next_state; + + return delay; +} + +/* + * Function tekram_reset (driver) + * + * This function resets the tekram dongle. Warning, this function + * must be called with a process context!! + * + * Algorithm: + * 0. Clear RTS and DTR, and wait 50 ms (power off the IR-210 ) + * 1. clear RTS + * 2. set DTR, and wait at least 1 ms + * 3. clear DTR to SPACE state, wait at least 50 us for further + * operation + */ + + +#define TEKRAM_STATE_WAIT_RESET (SIRDEV_STATE_DONGLE_RESET + 1) + +static int tekram_reset(struct sir_dev *dev) +{ + unsigned delay = 0; + unsigned next_state = dev->fsm.substate; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + switch(dev->fsm.substate) { + + case SIRDEV_STATE_DONGLE_RESET: + /* Clear DTR, Set RTS */ + dev->set_dtr_rts(dev, FALSE, TRUE); + + next_state = TEKRAM_STATE_WAIT_RESET; + delay = 1; /* Should sleep 1 ms */ + break; + + case TEKRAM_STATE_WAIT_RESET: + /* Set DTR, Set RTS */ + dev->set_dtr_rts(dev, TRUE, TRUE); + + /* Wait at least 50 us */ + udelay(75); + + return 0; + + default: + ERROR("%s - undefined state\n", __FUNCTION__); + return -EINVAL; + } + + dev->fsm.substate = next_state; + + return delay; +} + +MODULE_AUTHOR("Dag Brattli "); +MODULE_DESCRIPTION("Tekram IrMate IR-210B dongle driver"); +MODULE_LICENSE("GPL"); + +module_init(tekram_sir_init); +module_exit(tekram_sir_cleanup);