diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/irda |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/net/irda')
52 files changed, 29954 insertions, 0 deletions
diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig new file mode 100644 index 000000000000..a464841da49f --- /dev/null +++ b/drivers/net/irda/Kconfig @@ -0,0 +1,404 @@ + +menu "Infrared-port device drivers" + depends on IRDA!=n + +comment "SIR device drivers" + +config IRTTY_SIR + tristate "IrTTY (uses Linux serial driver)" + depends on IRDA + help + Say Y here if you want to build support for the IrTTY line + discipline. To compile it as a module, choose M here: the module + will be called irtty-sir. 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 Y. + +comment "Dongle support" + +config DONGLE + bool "Serial dongle support" + depends on IRTTY_SIR + 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. To compile it as a module, choose M here. 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. To compile it as a module, choose M here. 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. To compile it as a module, choose M here. 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". + +config LITELINK_DONGLE + tristate "Parallax LiteLink dongle" + depends on DONGLE && IRDA + help + Say Y here if you want to build support for the Parallax Litelink + dongle. To compile it as a module, choose M here. The Parallax + dongle attaches to the normal 9-pin serial port connector, and can + currently only be used by IrTTY. To activate support for Parallax + dongles you will have to start irattach like this: + "irattach -d litelink". + +config MA600_DONGLE + tristate "Mobile Action MA600 dongle" + depends on DONGLE && IRDA && EXPERIMENTAL + help + Say Y here if you want to build support for the Mobile Action MA600 + dongle. To compile it as a module, choose M here. The MA600 dongle + attaches to the normal 9-pin serial port connector, and can + currently only be used by IrTTY. The driver should also support + the MA620 USB version of the dongle, if the integrated USB-to-RS232 + converter is supported by usbserial. To activate support for + MA600 dongle you will have to start irattach like this: + "irattach -d ma600". + +config GIRBIL_DONGLE + tristate "Greenwich GIrBIL dongle" + depends on DONGLE && IRDA && EXPERIMENTAL + help + Say Y here if you want to build support for the Greenwich GIrBIL + dongle. If you want to compile it as a module, choose M here. + The Greenwich dongle attaches to the normal 9-pin serial port + connector, and can currently only be used by IrTTY. To activate + support for Greenwich dongles you will have to start irattach + like this: "irattach -d girbil". + +config MCP2120_DONGLE + tristate "Microchip MCP2120" + depends on DONGLE && IRDA && EXPERIMENTAL + help + Say Y here if you want to build support for the Microchip MCP2120 + dongle. If you want to compile it as a module, choose M here. + The MCP2120 dongle attaches to the normal 9-pin serial port + connector, and can currently only be used by IrTTY. To activate + support for MCP2120 dongles you will have to start irattach + like this: "irattach -d mcp2120". + + You must build this dongle yourself. For more information see: + <http://www.eyetap.org/~tangf/irda_sir_linux.html> + +config OLD_BELKIN_DONGLE + tristate "Old Belkin dongle" + depends on DONGLE && IRDA && EXPERIMENTAL + 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, choose + M here. Some information is contained in the comments + at the top of <file:drivers/net/irda/old_belkin.c>. + +config ACT200L_DONGLE + tristate "ACTiSYS IR-200L dongle" + depends on DONGLE && IRDA && EXPERIMENTAL + 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, choose M here. + The ACTiSYS IR-200L dongle attaches to the normal 9-pin serial + port connector, and can currently only be used by IrTTY. + To activate support for ACTiSYS IR-200L dongle you will have to + start irattach like this: "irattach -d act200l". + +comment "Old SIR device drivers" + +config IRPORT_SIR + tristate "IrPORT (IrDA serial driver)" + depends on IRDA && BROKEN_ON_SMP + ---help--- + Say Y here if you want to build support for the IrPORT IrDA device + driver. To compile it as a module, choose M here: the module will be + called irport. IrPORT can be used instead of IrTTY and sometimes + this can be better. One example is if your IrDA port does not + have echo-canceling, which will work OK with IrPORT since this + driver is working in half-duplex mode only. You don't need to use + irattach with IrPORT, but you just insert it the same way as FIR + drivers (insmod irport io=0x3e8 irq=11). Notice that IrPORT is a + SIR device driver which means that speed is limited to 115200 bps. + + If unsure, say Y. + +comment "Old Serial dongle support" + +config DONGLE_OLD + bool "Old Serial dongle support" + depends on (IRTTY_OLD || IRPORT_SIR) && BROKEN_ON_SMP + 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_OLD + tristate "ESI JetEye PC dongle" + depends on DONGLE_OLD && IRDA + help + Say Y here if you want to build support for the Extended Systems + JetEye PC dongle. To compile it as a module, choose M here. 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_OLD + tristate "ACTiSYS IR-220L and IR220L+ dongle" + depends on DONGLE_OLD && IRDA + help + Say Y here if you want to build support for the ACTiSYS IR-220L and + IR220L+ dongles. To compile it as a module, choose M here. 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_OLD + tristate "Tekram IrMate 210B dongle" + depends on DONGLE_OLD && IRDA + help + Say Y here if you want to build support for the Tekram IrMate 210B + dongle. To compile it as a module, choose M here. 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". + +config GIRBIL_DONGLE_OLD + tristate "Greenwich GIrBIL dongle" + depends on DONGLE_OLD && IRDA + help + Say Y here if you want to build support for the Greenwich GIrBIL + dongle. To compile it as a module, choose M here. The Greenwich + dongle attaches to the normal 9-pin serial port connector, and can + currently only be used by IrTTY. To activate support for Greenwich + dongles you will have to insert "irattach -d girbil" in the + /etc/irda/drivers script. + +config LITELINK_DONGLE_OLD + tristate "Parallax LiteLink dongle" + depends on DONGLE_OLD && IRDA + help + Say Y here if you want to build support for the Parallax Litelink + dongle. To compile it as a module, choose M here. The Parallax + dongle attaches to the normal 9-pin serial port connector, and can + currently only be used by IrTTY. To activate support for Parallax + dongles you will have to start irattach like this: + "irattach -d litelink". + +config MCP2120_DONGLE_OLD + tristate "Microchip MCP2120" + depends on DONGLE_OLD && IRDA + help + Say Y here if you want to build support for the Microchip MCP2120 + dongle. To compile it as a module, choose M here. The MCP2120 dongle + attaches to the normal 9-pin serial port connector, and can + currently only be used by IrTTY. To activate support for MCP2120 + dongles you will have to insert "irattach -d mcp2120" in the + /etc/irda/drivers script. + + You must build this dongle yourself. For more information see: + <http://www.eyetap.org/~tangf/irda_sir_linux.html> + +config OLD_BELKIN_DONGLE_OLD + tristate "Old Belkin dongle" + depends on DONGLE_OLD && IRDA + help + Say Y here if you want to build support for the Adaptec Airport 1000 + and 2000 dongles. To compile it as a module, choose M here: the module + will be called old_belkin. Some information is contained in the + comments at the top of <file:drivers/net/irda/old_belkin.c>. + +config ACT200L_DONGLE_OLD + tristate "ACTiSYS IR-200L dongle (EXPERIMENTAL)" + depends on DONGLE_OLD && EXPERIMENTAL && IRDA + help + Say Y here if you want to build support for the ACTiSYS IR-200L + dongle. To compile it as a module, choose M here. The ACTiSYS + IR-200L dongle attaches to the normal 9-pin serial port connector, + and can currently only be used by IrTTY. To activate support for + ACTiSYS IR-200L dongles you will have to start irattach like this: + "irattach -d act200l". + +config MA600_DONGLE_OLD + tristate "Mobile Action MA600 dongle (EXPERIMENTAL)" + depends on DONGLE_OLD && EXPERIMENTAL && IRDA + ---help--- + Say Y here if you want to build support for the Mobile Action MA600 + dongle. To compile it as a module, choose M here. The MA600 dongle + attaches to the normal 9-pin serial port connector, and can + currently only be tested on IrCOMM. To activate support for MA600 + dongles you will have to insert "irattach -d ma600" in the + /etc/irda/drivers script. Note: irutils 0.9.15 requires no + modification. irutils 0.9.9 needs modification. For more + information, download the following tar gzip file. + + There is a pre-compiled module on + <http://engsvr.ust.hk/~eetwl95/ma600.html> + +config EP7211_IR + tristate "EP7211 I/R support" + depends on DONGLE_OLD && ARCH_EP7211 && IRDA + +comment "FIR device drivers" + +config USB_IRDA + tristate "IrDA USB dongles" + depends on IRDA && USB + ---help--- + Say Y here if you want to build support for the USB IrDA FIR Dongle + device driver. To compile it as a module, choose M here: the module + will be called irda-usb. IrDA-USB support the various IrDA USB + dongles available and most of their pecularities. Those dongles + plug in the USB port of your computer, are plug and play, and + support SIR and FIR (4Mbps) speeds. On the other hand, those + dongles tend to be less efficient than a FIR chipset. + + Please note that the driver is still experimental. And of course, + you will need both USB and IrDA support in your kernel... + +config SIGMATEL_FIR + tristate "SigmaTel STIr4200 bridge (EXPERIMENTAL)" + depends on IRDA && USB && EXPERIMENTAL + select CRC32 + ---help--- + Say Y here if you want to build support for the SigmaTel STIr4200 + USB IrDA FIR bridge device driver. + + USB bridge based on the SigmaTel STIr4200 don't conform to the + IrDA-USB device class specification, and therefore need their + own specific driver. Those dongles support SIR and FIR (4Mbps) + speeds. + + To compile it as a module, choose M here: the module will be called + stir4200. + +config NSC_FIR + tristate "NSC PC87108/PC87338" + depends on IRDA && ISA + help + Say Y here if you want to build support for the NSC PC87108 and + PC87338 IrDA chipsets. This driver supports SIR, + MIR and FIR (4Mbps) speeds. + + To compile it as a module, choose M here: the module will be called + nsc-ircc. + +config WINBOND_FIR + tristate "Winbond W83977AF (IR)" + depends on IRDA && ISA + help + Say Y here if you want to build IrDA support for the Winbond + W83977AF super-io chipset. This driver should be used for the IrDA + chipset in the Corel NetWinder. The driver supports SIR, MIR and + FIR (4Mbps) speeds. + + To compile it as a module, choose M here: the module will be called + w83977af_ir. + +config TOSHIBA_FIR + tristate "Toshiba Type-O IR Port" + depends on IRDA && PCI && !64BIT + help + Say Y here if you want to build support for the Toshiba Type-O IR + and Donau oboe chipsets. These chipsets are used by the Toshiba + Libretto 100/110CT, Tecra 8100, Portege 7020 and many more laptops. + To compile it as a module, choose M here: the module will be called + donauboe. + +config AU1000_FIR + tristate "Alchemy Au1000 SIR/FIR" + depends on MIPS_AU1000 && IRDA + +config SMC_IRCC_FIR + tristate "SMSC IrCC (EXPERIMENTAL)" + depends on EXPERIMENTAL && IRDA && ISA + 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). + To compile it as a module, choose M here: the module will be called + smsc-ircc2.o. + +config ALI_FIR + tristate "ALi M5123 FIR (EXPERIMENTAL)" + depends on EXPERIMENTAL && IRDA && ISA + help + Say Y here if you want to build support for the ALi M5123 FIR + Controller. The ALi M5123 FIR Controller is embedded in ALi M1543C, + M1535, M1535D, M1535+, M1535D Sourth Bridge. This driver supports + SIR, MIR and FIR (4Mbps) speeds. + + To compile it as a module, choose M here: the module will be called + ali-ircc. + +config VLSI_FIR + tristate "VLSI 82C147 SIR/MIR/FIR (EXPERIMENTAL)" + depends on EXPERIMENTAL && IRDA && PCI + help + Say Y here if you want to build support for the VLSI 82C147 + PCI-IrDA Controller. This controller is used by the HP OmniBook 800 + and 5500 notebooks. The driver provides support for SIR, MIR and + FIR (4Mbps) speeds. + + To compile it as a module, choose M here: the module will be called + vlsi_ir. + +config SA1100_FIR + tristate "SA1100 Internal IR" + depends on ARCH_SA1100 && IRDA + +config VIA_FIR + tristate "VIA VT8231/VT1211 SIR/MIR/FIR" + depends on IRDA && ISA && PCI + help + Say Y here if you want to build support for the VIA VT8231 + and VIA VT1211 IrDA controllers, found on the motherboards using + those those VIA chipsets. To use this controller, you will need + to plug a specific 5 pins FIR IrDA dongle in the specific + motherboard connector. The driver provides support for SIR, MIR + and FIR (4Mbps) speeds. + + You will need to specify the 'dongle_id' module parameter to + indicate the FIR dongle attached to the controller. + + To compile it as a module, choose M here: the module will be called + via-ircc. + +endmenu + diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile new file mode 100644 index 000000000000..29a8bd812b21 --- /dev/null +++ b/drivers/net/irda/Makefile @@ -0,0 +1,47 @@ +# +# Makefile for the Linux IrDA infrared port device drivers. +# +# 9 Aug 2000, Christoph Hellwig <hch@infradead.org> +# Rewritten to use lists instead of if-statements. +# + +# Old SIR drivers +obj-$(CONFIG_IRPORT_SIR) += irport.o +# FIR drivers +obj-$(CONFIG_USB_IRDA) += irda-usb.o +obj-$(CONFIG_SIGMATEL_FIR) += stir4200.o +obj-$(CONFIG_NSC_FIR) += nsc-ircc.o +obj-$(CONFIG_WINBOND_FIR) += w83977af_ir.o +obj-$(CONFIG_SA1100_FIR) += sa1100_ir.o +obj-$(CONFIG_TOSHIBA_FIR) += donauboe.o +obj-$(CONFIG_SMC_IRCC_FIR) += smsc-ircc2.o +obj-$(CONFIG_ALI_FIR) += ali-ircc.o +obj-$(CONFIG_VLSI_FIR) += vlsi_ir.o +obj-$(CONFIG_VIA_FIR) += via-ircc.o +# Old dongle drivers for old SIR drivers +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_OLD) += girbil.o +obj-$(CONFIG_LITELINK_DONGLE_OLD) += litelink.o +obj-$(CONFIG_OLD_BELKIN_DONGLE_OLD) += old_belkin.o +obj-$(CONFIG_MCP2120_DONGLE_OLD) += mcp2120.o +obj-$(CONFIG_ACT200L_DONGLE_OLD) += act200l.o +obj-$(CONFIG_MA600_DONGLE_OLD) += ma600.o +obj-$(CONFIG_EP7211_IR) += ep7211_ir.o +obj-$(CONFIG_AU1000_FIR) += au1k_ir.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 +obj-$(CONFIG_LITELINK_DONGLE) += litelink-sir.o +obj-$(CONFIG_GIRBIL_DONGLE) += girbil-sir.o +obj-$(CONFIG_OLD_BELKIN_DONGLE) += old_belkin-sir.o +obj-$(CONFIG_MCP2120_DONGLE) += mcp2120-sir.o +obj-$(CONFIG_ACT200L_DONGLE) += act200l-sir.o +obj-$(CONFIG_MA600_DONGLE) += ma600-sir.o + +# The SIR helper module +sir-dev-objs := sir_core.o sir_dev.o sir_dongle.o sir_kthread.o diff --git a/drivers/net/irda/act200l-sir.c b/drivers/net/irda/act200l-sir.c new file mode 100644 index 000000000000..d8b89c74aabd --- /dev/null +++ b/drivers/net/irda/act200l-sir.c @@ -0,0 +1,257 @@ +/********************************************************************* + * + * Filename: act200l.c + * Version: 0.8 + * Description: Implementation for the ACTiSYS ACT-IR200L dongle + * Status: Experimental. + * Author: SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp> + * Created at: Fri Aug 3 17:35:42 2001 + * Modified at: Fri Aug 17 10:22:40 2001 + * Modified by: SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp> + * + * Copyright (c) 2001 SHIMIZU Takuya, 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. + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> + +#include <net/irda/irda.h> + +#include "sir-dev.h" + +static int act200l_reset(struct sir_dev *dev); +static int act200l_open(struct sir_dev *dev); +static int act200l_close(struct sir_dev *dev); +static int act200l_change_speed(struct sir_dev *dev, unsigned speed); + +/* Regsiter 0: Control register #1 */ +#define ACT200L_REG0 0x00 +#define ACT200L_TXEN 0x01 /* Enable transmitter */ +#define ACT200L_RXEN 0x02 /* Enable receiver */ + +/* Register 1: Control register #2 */ +#define ACT200L_REG1 0x10 +#define ACT200L_LODB 0x01 /* Load new baud rate count value */ +#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */ + +/* Register 4: Output Power register */ +#define ACT200L_REG4 0x40 +#define ACT200L_OP0 0x01 /* Enable LED1C output */ +#define ACT200L_OP1 0x02 /* Enable LED2C output */ +#define ACT200L_BLKR 0x04 + +/* Register 5: Receive Mode register */ +#define ACT200L_REG5 0x50 +#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */ + +/* Register 6: Receive Sensitivity register #1 */ +#define ACT200L_REG6 0x60 +#define ACT200L_RS0 0x01 /* receive threshold bit 0 */ +#define ACT200L_RS1 0x02 /* receive threshold bit 1 */ + +/* Register 7: Receive Sensitivity register #2 */ +#define ACT200L_REG7 0x70 +#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */ + +/* Register 8,9: Baud Rate Dvider register #1,#2 */ +#define ACT200L_REG8 0x80 +#define ACT200L_REG9 0x90 + +#define ACT200L_2400 0x5f +#define ACT200L_9600 0x17 +#define ACT200L_19200 0x0b +#define ACT200L_38400 0x05 +#define ACT200L_57600 0x03 +#define ACT200L_115200 0x01 + +/* Register 13: Control register #3 */ +#define ACT200L_REG13 0xd0 +#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */ + +/* Register 15: Status register */ +#define ACT200L_REG15 0xf0 + +/* Register 21: Control register #4 */ +#define ACT200L_REG21 0x50 +#define ACT200L_EXCK 0x02 /* Disable clock output driver */ +#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */ + +static struct dongle_driver act200l = { + .owner = THIS_MODULE, + .driver_name = "ACTiSYS ACT-IR200L", + .type = IRDA_ACT200L_DONGLE, + .open = act200l_open, + .close = act200l_close, + .reset = act200l_reset, + .set_speed = act200l_change_speed, +}; + +static int __init act200l_sir_init(void) +{ + return irda_register_dongle(&act200l); +} + +static void __exit act200l_sir_cleanup(void) +{ + irda_unregister_dongle(&act200l); +} + +static int act200l_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); + + /* Power on the dongle */ + sirdev_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; + qos->min_turn_time.bits = 0x03; + irda_qos_bits_to_value(qos); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int act200l_close(struct sir_dev *dev) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); + + /* Power off the dongle */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function act200l_change_speed (dev, speed) + * + * Set the speed for the ACTiSYS ACT-IR200L type dongle. + * + */ +static int act200l_change_speed(struct sir_dev *dev, unsigned speed) +{ + u8 control[3]; + int ret = 0; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); + + /* Clear DTR and set RTS to enter command mode */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + + switch (speed) { + default: + ret = -EINVAL; + /* fall through */ + case 9600: + control[0] = ACT200L_REG8 | (ACT200L_9600 & 0x0f); + control[1] = ACT200L_REG9 | ((ACT200L_9600 >> 4) & 0x0f); + break; + case 19200: + control[0] = ACT200L_REG8 | (ACT200L_19200 & 0x0f); + control[1] = ACT200L_REG9 | ((ACT200L_19200 >> 4) & 0x0f); + break; + case 38400: + control[0] = ACT200L_REG8 | (ACT200L_38400 & 0x0f); + control[1] = ACT200L_REG9 | ((ACT200L_38400 >> 4) & 0x0f); + break; + case 57600: + control[0] = ACT200L_REG8 | (ACT200L_57600 & 0x0f); + control[1] = ACT200L_REG9 | ((ACT200L_57600 >> 4) & 0x0f); + break; + case 115200: + control[0] = ACT200L_REG8 | (ACT200L_115200 & 0x0f); + control[1] = ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f); + break; + } + control[2] = ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE; + + /* Write control bytes */ + sirdev_raw_write(dev, control, 3); + msleep(5); + + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + dev->speed = speed; + return ret; +} + +/* + * Function act200l_reset (driver) + * + * Reset the ACTiSYS ACT-IR200L type dongle. + */ + +#define ACT200L_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET+1) +#define ACT200L_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET+2) + +static int act200l_reset(struct sir_dev *dev) +{ + unsigned state = dev->fsm.substate; + unsigned delay = 0; + u8 control[9] = { + ACT200L_REG15, + ACT200L_REG13 | ACT200L_SHDW, + ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL, + ACT200L_REG13, + ACT200L_REG7 | ACT200L_ENPOS, + ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1, + ACT200L_REG5 | ACT200L_RWIDL, + ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR, + ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN + }; + int ret = 0; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); + + switch (state) { + case SIRDEV_STATE_DONGLE_RESET: + /* Reset the dongle : set RTS low for 25 ms */ + sirdev_set_dtr_rts(dev, TRUE, FALSE); + state = ACT200L_STATE_WAIT1_RESET; + delay = 50; + break; + + case ACT200L_STATE_WAIT1_RESET: + /* Clear DTR and set RTS to enter command mode */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + + udelay(25); /* better wait for some short while */ + + /* Write control bytes */ + sirdev_raw_write(dev, control, sizeof(control)); + state = ACT200L_STATE_WAIT2_RESET; + delay = 15; + break; + + case ACT200L_STATE_WAIT2_RESET: + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + dev->speed = 9600; + break; + default: + IRDA_ERROR("%s(), unknown state %d\n", __FUNCTION__, state); + ret = -1; + break; + } + dev->fsm.substate = state; + return (delay > 0) ? delay : ret; +} + +MODULE_AUTHOR("SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp>"); +MODULE_DESCRIPTION("ACTiSYS ACT-IR200L dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-10"); /* IRDA_ACT200L_DONGLE */ + +module_init(act200l_sir_init); +module_exit(act200l_sir_cleanup); diff --git a/drivers/net/irda/act200l.c b/drivers/net/irda/act200l.c new file mode 100644 index 000000000000..756cd44e857a --- /dev/null +++ b/drivers/net/irda/act200l.c @@ -0,0 +1,297 @@ +/********************************************************************* + * + * Filename: act200l.c + * Version: 0.8 + * Description: Implementation for the ACTiSYS ACT-IR200L dongle + * Status: Experimental. + * Author: SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp> + * Created at: Fri Aug 3 17:35:42 2001 + * Modified at: Fri Aug 17 10:22:40 2001 + * Modified by: SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp> + * + * Copyright (c) 2001 SHIMIZU Takuya, 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. + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/init.h> + +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> + +static int act200l_reset(struct irda_task *task); +static void act200l_open(dongle_t *self, struct qos_info *qos); +static void act200l_close(dongle_t *self); +static int act200l_change_speed(struct irda_task *task); + +/* Regsiter 0: Control register #1 */ +#define ACT200L_REG0 0x00 +#define ACT200L_TXEN 0x01 /* Enable transmitter */ +#define ACT200L_RXEN 0x02 /* Enable receiver */ + +/* Register 1: Control register #2 */ +#define ACT200L_REG1 0x10 +#define ACT200L_LODB 0x01 /* Load new baud rate count value */ +#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */ + +/* Register 4: Output Power register */ +#define ACT200L_REG4 0x40 +#define ACT200L_OP0 0x01 /* Enable LED1C output */ +#define ACT200L_OP1 0x02 /* Enable LED2C output */ +#define ACT200L_BLKR 0x04 + +/* Register 5: Receive Mode register */ +#define ACT200L_REG5 0x50 +#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */ + +/* Register 6: Receive Sensitivity register #1 */ +#define ACT200L_REG6 0x60 +#define ACT200L_RS0 0x01 /* receive threshold bit 0 */ +#define ACT200L_RS1 0x02 /* receive threshold bit 1 */ + +/* Register 7: Receive Sensitivity register #2 */ +#define ACT200L_REG7 0x70 +#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */ + +/* Register 8,9: Baud Rate Dvider register #1,#2 */ +#define ACT200L_REG8 0x80 +#define ACT200L_REG9 0x90 + +#define ACT200L_2400 0x5f +#define ACT200L_9600 0x17 +#define ACT200L_19200 0x0b +#define ACT200L_38400 0x05 +#define ACT200L_57600 0x03 +#define ACT200L_115200 0x01 + +/* Register 13: Control register #3 */ +#define ACT200L_REG13 0xd0 +#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */ + +/* Register 15: Status register */ +#define ACT200L_REG15 0xf0 + +/* Register 21: Control register #4 */ +#define ACT200L_REG21 0x50 +#define ACT200L_EXCK 0x02 /* Disable clock output driver */ +#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */ + +static struct dongle_reg dongle = { + .type = IRDA_ACT200L_DONGLE, + .open = act200l_open, + .close = act200l_close, + .reset = act200l_reset, + .change_speed = act200l_change_speed, + .owner = THIS_MODULE, +}; + +static int __init act200l_init(void) +{ + return irda_device_register_dongle(&dongle); +} + +static void __exit act200l_cleanup(void) +{ + irda_device_unregister_dongle(&dongle); +} + +static void act200l_open(dongle_t *self, struct qos_info *qos) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); + + /* Power on the dongle */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + + /* Set the speeds we can accept */ + qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + qos->min_turn_time.bits = 0x03; +} + +static void act200l_close(dongle_t *self) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); + + /* Power off the dongle */ + self->set_dtr_rts(self->dev, FALSE, FALSE); +} + +/* + * Function act200l_change_speed (dev, speed) + * + * Set the speed for the ACTiSYS ACT-IR200L type dongle. + * + */ +static int act200l_change_speed(struct irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + __u32 speed = (__u32) task->param; + __u8 control[3]; + int ret = 0; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); + + self->speed_task = task; + + switch (task->state) { + case IRDA_TASK_INIT: + if (irda_task_execute(self, act200l_reset, NULL, task, + (void *) speed)) + { + /* Dongle need more time to reset */ + irda_task_next_state(task, IRDA_TASK_CHILD_WAIT); + + /* Give reset 1 sec to finish */ + ret = msecs_to_jiffies(1000); + } + break; + case IRDA_TASK_CHILD_WAIT: + IRDA_WARNING("%s(), resetting dongle timed out!\n", + __FUNCTION__); + ret = -1; + break; + case IRDA_TASK_CHILD_DONE: + /* Clear DTR and set RTS to enter command mode */ + self->set_dtr_rts(self->dev, FALSE, TRUE); + + switch (speed) { + case 9600: + default: + control[0] = ACT200L_REG8 | (ACT200L_9600 & 0x0f); + control[1] = ACT200L_REG9 | ((ACT200L_9600 >> 4) & 0x0f); + break; + case 19200: + control[0] = ACT200L_REG8 | (ACT200L_19200 & 0x0f); + control[1] = ACT200L_REG9 | ((ACT200L_19200 >> 4) & 0x0f); + break; + case 38400: + control[0] = ACT200L_REG8 | (ACT200L_38400 & 0x0f); + control[1] = ACT200L_REG9 | ((ACT200L_38400 >> 4) & 0x0f); + break; + case 57600: + control[0] = ACT200L_REG8 | (ACT200L_57600 & 0x0f); + control[1] = ACT200L_REG9 | ((ACT200L_57600 >> 4) & 0x0f); + break; + case 115200: + control[0] = ACT200L_REG8 | (ACT200L_115200 & 0x0f); + control[1] = ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f); + break; + } + control[2] = ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE; + + /* Write control bytes */ + self->write(self->dev, control, 3); + irda_task_next_state(task, IRDA_TASK_WAIT); + ret = msecs_to_jiffies(5); + break; + case IRDA_TASK_WAIT: + /* Go back to normal mode */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + + irda_task_next_state(task, IRDA_TASK_DONE); + self->speed_task = NULL; + break; + default: + IRDA_ERROR("%s(), unknown state %d\n", + __FUNCTION__, task->state); + irda_task_next_state(task, IRDA_TASK_DONE); + self->speed_task = NULL; + ret = -1; + break; + } + return ret; +} + +/* + * Function act200l_reset (driver) + * + * Reset the ACTiSYS ACT-IR200L type dongle. + */ +static int act200l_reset(struct irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + __u8 control[9] = { + ACT200L_REG15, + ACT200L_REG13 | ACT200L_SHDW, + ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL, + ACT200L_REG13, + ACT200L_REG7 | ACT200L_ENPOS, + ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1, + ACT200L_REG5 | ACT200L_RWIDL, + ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR, + ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN + }; + int ret = 0; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); + + self->reset_task = task; + + switch (task->state) { + case IRDA_TASK_INIT: + /* Power on the dongle */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + + irda_task_next_state(task, IRDA_TASK_WAIT1); + ret = msecs_to_jiffies(50); + break; + case IRDA_TASK_WAIT1: + /* Reset the dongle : set RTS low for 25 ms */ + self->set_dtr_rts(self->dev, TRUE, FALSE); + + irda_task_next_state(task, IRDA_TASK_WAIT2); + ret = msecs_to_jiffies(50); + break; + case IRDA_TASK_WAIT2: + /* Clear DTR and set RTS to enter command mode */ + self->set_dtr_rts(self->dev, FALSE, TRUE); + + /* Write control bytes */ + self->write(self->dev, control, 9); + irda_task_next_state(task, IRDA_TASK_WAIT3); + ret = msecs_to_jiffies(15); + break; + case IRDA_TASK_WAIT3: + /* Go back to normal mode */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + + irda_task_next_state(task, IRDA_TASK_DONE); + self->reset_task = NULL; + break; + default: + IRDA_ERROR("%s(), unknown state %d\n", + __FUNCTION__, task->state); + irda_task_next_state(task, IRDA_TASK_DONE); + self->reset_task = NULL; + ret = -1; + break; + } + return ret; +} + +MODULE_AUTHOR("SHIMIZU Takuya <tshimizu@ga2.so-net.ne.jp>"); +MODULE_DESCRIPTION("ACTiSYS ACT-IR200L dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-10"); /* IRDA_ACT200L_DONGLE */ + +/* + * Function init_module (void) + * + * Initialize ACTiSYS ACT-IR200L module + * + */ +module_init(act200l_init); + +/* + * Function cleanup_module (void) + * + * Cleanup ACTiSYS ACT-IR200L module + * + */ +module_exit(act200l_cleanup); diff --git a/drivers/net/irda/actisys-sir.c b/drivers/net/irda/actisys-sir.c new file mode 100644 index 000000000000..9715ab5572e9 --- /dev/null +++ b/drivers/net/irda/actisys-sir.c @@ -0,0 +1,246 @@ +/********************************************************************* + * + * Filename: actisys.c + * Version: 1.1 + * Description: Implementation for the ACTiSYS IR-220L and IR-220L+ + * dongles + * Status: Beta. + * Authors: Dag Brattli <dagb@cs.uit.no> (initially) + * Jean Tourrilhes <jt@hpl.hp.com> (new version) + * Martin Diehl <mad@mdiehl.de> (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 <mad@mdiehl.de> + * + * 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 <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> + +#include <net/irda/irda.h> + +#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 unsigned 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, +}; + +static 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; +} + +static 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; + + sirdev_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); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int actisys_close(struct sir_dev *dev) +{ + /* Power off the dongle */ + sirdev_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; i < MAX_SPEEDS; i++) { + if (speed == baud_rates[i]) { + dev->speed = speed; + break; + } + /* Set RTS low for 10 us */ + sirdev_set_dtr_rts(dev, TRUE, FALSE); + udelay(MIN_DELAY); + + /* Set RTS high for 10 us */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + udelay(MIN_DELAY); + } + + /* Check if life is sweet... */ + if (i >= MAX_SPEEDS) { + actisys_reset(dev); + ret = -EINVAL; /* 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 accommodate 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... + * <Martin : move above comment to irda_config_fsm> + * 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 */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + udelay(MIN_DELAY); + + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + dev->speed = 9600; /* That's the default */ + + return 0; +} + +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no> - Jean Tourrilhes <jt@hpl.hp.com>"); +MODULE_DESCRIPTION("ACTiSYS IR-220L and IR-220L+ dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-2"); /* IRDA_ACTISYS_DONGLE */ +MODULE_ALIAS("irda-dongle-3"); /* IRDA_ACTISYS_PLUS_DONGLE */ + +module_init(actisys_sir_init); +module_exit(actisys_sir_cleanup); diff --git a/drivers/net/irda/actisys.c b/drivers/net/irda/actisys.c new file mode 100644 index 000000000000..b2e31f4a384c --- /dev/null +++ b/drivers/net/irda/actisys.c @@ -0,0 +1,288 @@ +/********************************************************************* + * + * Filename: actisys.c + * Version: 1.0 + * Description: Implementation for the ACTiSYS IR-220L and IR-220L+ + * dongles + * Status: Beta. + * Authors: Dag Brattli <dagb@cs.uit.no> (initially) + * Jean Tourrilhes <jt@hpl.hp.com> (new version) + * Created at: Wed Oct 21 20:02:35 1998 + * Modified at: Fri Dec 17 09:10:43 1999 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. + * Copyright (c) 1999 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 + * 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... + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/init.h> + +#include <net/irda/irda.h> +#include <net/irda/irda_device.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_change_speed(struct irda_task *task); +static int actisys_reset(struct irda_task *task); +static void actisys_open(dongle_t *self, struct qos_info *qos); +static void actisys_close(dongle_t *self); + +/* 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 5 + +static struct dongle_reg dongle = { + .type = IRDA_ACTISYS_DONGLE, + .open = actisys_open, + .close = actisys_close, + .reset = actisys_reset, + .change_speed = actisys_change_speed, + .owner = THIS_MODULE, +}; + +static struct dongle_reg dongle_plus = { + .type = IRDA_ACTISYS_PLUS_DONGLE, + .open = actisys_open, + .close = actisys_close, + .reset = actisys_reset, + .change_speed = actisys_change_speed, + .owner = THIS_MODULE, +}; + +/* + * Function actisys_change_speed (task) + * + * There is two model of Actisys dongle we are dealing with, + * the 220L and 220L+. At this point, only irattach knows with + * kind the user has requested (it was an argument on irattach + * command line). + * So, we register a dongle of each sort and let irattach + * pick the right one... + */ +static int __init actisys_init(void) +{ + int ret; + + /* First, register an Actisys 220L dongle */ + ret = irda_device_register_dongle(&dongle); + if (ret < 0) + return ret; + /* Now, register an Actisys 220L+ dongle */ + ret = irda_device_register_dongle(&dongle_plus); + if (ret < 0) { + irda_device_unregister_dongle(&dongle); + return ret; + } + return 0; +} + +static void __exit actisys_cleanup(void) +{ + /* We have to remove both dongles */ + irda_device_unregister_dongle(&dongle); + irda_device_unregister_dongle(&dongle_plus); +} + +static void actisys_open(dongle_t *self, struct qos_info *qos) +{ + /* Power on the dongle */ + self->set_dtr_rts(self->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 (self->issue->type == IRDA_ACTISYS_DONGLE) + qos->baud_rate.bits &= ~IR_38400; + + qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */ +} + +static void actisys_close(dongle_t *self) +{ + /* Power off the dongle */ + self->set_dtr_rts(self->dev, FALSE, FALSE); +} + +/* + * 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 irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + __u32 speed = (__u32) task->param; /* Target speed */ + int ret = 0; + int i = 0; + + IRDA_DEBUG(4, "%s(), speed=%d (was %d)\n", __FUNCTION__, speed, + self->speed); + + /* Go to a known state by reseting the dongle */ + + /* Reset the dongle : set DTR low for 10 us */ + self->set_dtr_rts(self->dev, FALSE, TRUE); + udelay(MIN_DELAY); + + /* Go back to normal mode (we are now at 9600 b/s) */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + + /* + * Now, we can set the speed requested. Send RTS pulses until we + * reach the target speed + */ + for (i=0; i<MAX_SPEEDS; i++) { + if (speed == baud_rates[i]) { + self->speed = baud_rates[i]; + break; + } + /* Make sure previous pulse is finished */ + udelay(MIN_DELAY); + + /* Set RTS low for 10 us */ + self->set_dtr_rts(self->dev, TRUE, FALSE); + udelay(MIN_DELAY); + + /* Set RTS high for 10 us */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + } + + /* Check if life is sweet... */ + if (i >= MAX_SPEEDS) + ret = -1; /* This should not happen */ + + /* Basta lavoro, on se casse d'ici... */ + irda_task_next_state(task, IRDA_TASK_DONE); + + 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 accommodate 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 irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + int ret = 0; + + IRDA_ASSERT(task != NULL, return -1;); + + self->reset_task = task; + + switch (task->state) { + case IRDA_TASK_INIT: + /* Set both DTR & RTS to power up the dongle */ + /* In theory redundant with power up in actisys_open() */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + + /* Sleep 50 ms to make sure capacitor is charged */ + ret = msecs_to_jiffies(50); + irda_task_next_state(task, IRDA_TASK_WAIT); + break; + case IRDA_TASK_WAIT: + /* Reset the dongle : set DTR low for 10 us */ + self->set_dtr_rts(self->dev, FALSE, TRUE); + udelay(MIN_DELAY); + + /* Go back to normal mode */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + + irda_task_next_state(task, IRDA_TASK_DONE); + self->reset_task = NULL; + self->speed = 9600; /* That's the default */ + break; + default: + IRDA_ERROR("%s(), unknown state %d\n", + __FUNCTION__, task->state); + irda_task_next_state(task, IRDA_TASK_DONE); + self->reset_task = NULL; + ret = -1; + break; + } + return ret; +} + +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no> - Jean Tourrilhes <jt@hpl.hp.com>"); +MODULE_DESCRIPTION("ACTiSYS IR-220L and IR-220L+ dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-2"); /* IRDA_ACTISYS_DONGLE */ +MODULE_ALIAS("irda-dongle-3"); /* IRDA_ACTISYS_PLUS_DONGLE */ + + +/* + * Function init_module (void) + * + * Initialize Actisys module + * + */ +module_init(actisys_init); + +/* + * Function cleanup_module (void) + * + * Cleanup Actisys module + * + */ +module_exit(actisys_cleanup); diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c new file mode 100644 index 000000000000..9bf34681d3df --- /dev/null +++ b/drivers/net/irda/ali-ircc.c @@ -0,0 +1,2277 @@ +/********************************************************************* + * + * Filename: ali-ircc.h + * Version: 0.5 + * Description: Driver for the ALI M1535D and M1543C FIR Controller + * Status: Experimental. + * Author: Benjamin Kong <benjamin_kong@ali.com.tw> + * Created at: 2000/10/16 03:46PM + * Modified at: 2001/1/3 02:55PM + * Modified by: Benjamin Kong <benjamin_kong@ali.com.tw> + * Modified at: 2003/11/6 and support for ALi south-bridge chipsets M1563 + * Modified by: Clear Zhang <clear_zhang@ali.com.tw> + * + * Copyright (c) 2000 Benjamin Kong <benjamin_kong@ali.com.tw> + * 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. + * + ********************************************************************/ + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/rtnetlink.h> +#include <linux/serial_reg.h> +#include <linux/dma-mapping.h> + +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/byteorder.h> + +#include <linux/pm.h> + +#include <net/irda/wrapper.h> +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> + +#include "ali-ircc.h" + +#define CHIP_IO_EXTENT 8 +#define BROKEN_DONGLE_ID + +static char *driver_name = "ali-ircc"; + +/* Module parameters */ +static int qos_mtt_bits = 0x07; /* 1 ms or more */ + +/* Use BIOS settions by default, but user may supply module parameters */ +static unsigned int io[] = { ~0, ~0, ~0, ~0 }; +static unsigned int irq[] = { 0, 0, 0, 0 }; +static unsigned int dma[] = { 0, 0, 0, 0 }; + +static int ali_ircc_probe_53(ali_chip_t *chip, chipio_t *info); +static int ali_ircc_init_43(ali_chip_t *chip, chipio_t *info); +static int ali_ircc_init_53(ali_chip_t *chip, chipio_t *info); + +/* These are the currently known ALi sourth-bridge chipsets, the only one difference + * is that M1543C doesn't support HP HDSL-3600 + */ +static ali_chip_t chips[] = +{ + { "M1543", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x43, ali_ircc_probe_53, ali_ircc_init_43 }, + { "M1535", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x53, ali_ircc_probe_53, ali_ircc_init_53 }, + { "M1563", { 0x3f0, 0x370 }, 0x51, 0x23, 0x20, 0x63, ali_ircc_probe_53, ali_ircc_init_53 }, + { NULL } +}; + +/* Max 4 instances for now */ +static struct ali_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL }; + +/* Dongle Types */ +static char *dongle_types[] = { + "TFDS6000", + "HP HSDL-3600", + "HP HSDL-1100", + "No dongle connected", +}; + +/* Some prototypes */ +static int ali_ircc_open(int i, chipio_t *info); + +static int ali_ircc_close(struct ali_ircc_cb *self); + +static int ali_ircc_setup(chipio_t *info); +static int ali_ircc_is_receiving(struct ali_ircc_cb *self); +static int ali_ircc_net_open(struct net_device *dev); +static int ali_ircc_net_close(struct net_device *dev); +static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static int ali_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data); +static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud); +static void ali_ircc_suspend(struct ali_ircc_cb *self); +static void ali_ircc_wakeup(struct ali_ircc_cb *self); +static struct net_device_stats *ali_ircc_net_get_stats(struct net_device *dev); + +/* SIR function */ +static int ali_ircc_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t ali_ircc_sir_interrupt(struct ali_ircc_cb *self); +static void ali_ircc_sir_receive(struct ali_ircc_cb *self); +static void ali_ircc_sir_write_wakeup(struct ali_ircc_cb *self); +static int ali_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len); +static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed); + +/* FIR function */ +static int ali_ircc_fir_hard_xmit(struct sk_buff *skb, struct net_device *dev); +static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 speed); +static irqreturn_t ali_ircc_fir_interrupt(struct ali_ircc_cb *self); +static int ali_ircc_dma_receive(struct ali_ircc_cb *self); +static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self); +static int ali_ircc_dma_xmit_complete(struct ali_ircc_cb *self); +static void ali_ircc_dma_xmit(struct ali_ircc_cb *self); + +/* My Function */ +static int ali_ircc_read_dongle_id (int i, chipio_t *info); +static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed); + +/* ALi chip function */ +static void SIR2FIR(int iobase); +static void FIR2SIR(int iobase); +static void SetCOMInterrupts(struct ali_ircc_cb *self , unsigned char enable); + +/* + * Function ali_ircc_init () + * + * Initialize chip. Find out whay kinds of chips we are dealing with + * and their configuation registers address + */ +static int __init ali_ircc_init(void) +{ + ali_chip_t *chip; + chipio_t info; + int ret = -ENODEV; + int cfg, cfg_base; + int reg, revision; + int i = 0; + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); + + /* Probe for all the ALi chipsets we know about */ + for (chip= chips; chip->name; chip++, i++) + { + IRDA_DEBUG(2, "%s(), Probing for %s ...\n", __FUNCTION__, chip->name); + + /* Try all config registers for this chip */ + for (cfg=0; cfg<2; cfg++) + { + cfg_base = chip->cfg[cfg]; + if (!cfg_base) + continue; + + memset(&info, 0, sizeof(chipio_t)); + info.cfg_base = cfg_base; + info.fir_base = io[i]; + info.dma = dma[i]; + info.irq = irq[i]; + + + /* Enter Configuration */ + outb(chip->entr1, cfg_base); + outb(chip->entr2, cfg_base); + + /* Select Logical Device 5 Registers (UART2) */ + outb(0x07, cfg_base); + outb(0x05, cfg_base+1); + + /* Read Chip Identification Register */ + outb(chip->cid_index, cfg_base); + reg = inb(cfg_base+1); + + if (reg == chip->cid_value) + { + IRDA_DEBUG(2, "%s(), Chip found at 0x%03x\n", __FUNCTION__, cfg_base); + + outb(0x1F, cfg_base); + revision = inb(cfg_base+1); + IRDA_DEBUG(2, "%s(), Found %s chip, revision=%d\n", __FUNCTION__, + chip->name, revision); + + /* + * If the user supplies the base address, then + * we init the chip, if not we probe the values + * set by the BIOS + */ + if (io[i] < 2000) + { + chip->init(chip, &info); + } + else + { + chip->probe(chip, &info); + } + + if (ali_ircc_open(i, &info) == 0) + ret = 0; + i++; + } + else + { + IRDA_DEBUG(2, "%s(), No %s chip at 0x%03x\n", __FUNCTION__, chip->name, cfg_base); + } + /* Exit configuration */ + outb(0xbb, cfg_base); + } + } + + IRDA_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__); + return ret; +} + +/* + * Function ali_ircc_cleanup () + * + * Close all configured chips + * + */ +static void __exit ali_ircc_cleanup(void) +{ + int i; + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); + + pm_unregister_all(ali_ircc_pmproc); + + for (i=0; i < 4; i++) { + if (dev_self[i]) + ali_ircc_close(dev_self[i]); + } + + IRDA_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__); +} + +/* + * Function ali_ircc_open (int i, chipio_t *inf) + * + * Open driver instance + * + */ +static int ali_ircc_open(int i, chipio_t *info) +{ + struct net_device *dev; + struct ali_ircc_cb *self; + struct pm_dev *pmdev; + int dongle_id; + int err; + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); + + /* Set FIR FIFO and DMA Threshold */ + if ((ali_ircc_setup(info)) == -1) + return -1; + + dev = alloc_irdadev(sizeof(*self)); + if (dev == NULL) { + IRDA_ERROR("%s(), can't allocate memory for control block!\n", + __FUNCTION__); + return -ENOMEM; + } + + self = dev->priv; + self->netdev = dev; + spin_lock_init(&self->lock); + + /* Need to store self somewhere */ + dev_self[i] = self; + self->index = i; + + /* Initialize IO */ + self->io.cfg_base = info->cfg_base; /* In ali_ircc_probe_53 assign */ + self->io.fir_base = info->fir_base; /* info->sir_base = info->fir_base */ + self->io.sir_base = info->sir_base; /* ALi SIR and FIR use the same address */ + self->io.irq = info->irq; + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.dma = info->dma; + self->io.fifo_size = 16; /* SIR: 16, FIR: 32 Benjamin 2000/11/1 */ + + /* Reserve the ioports that we need */ + if (!request_region(self->io.fir_base, self->io.fir_ext, driver_name)) { + IRDA_WARNING("%s(), can't get iobase of 0x%03x\n", __FUNCTION__, + self->io.fir_base); + err = -ENODEV; + goto err_out1; + } + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + /* The only value we must override it the baudrate */ + self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| + IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); // benjamin 2000/11/8 05:27PM + + self->qos.min_turn_time.bits = qos_mtt_bits; + + irda_qos_bits_to_value(&self->qos); + + /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ + self->rx_buff.truesize = 14384; + self->tx_buff.truesize = 14384; + + /* Allocate memory if needed */ + self->rx_buff.head = + dma_alloc_coherent(NULL, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); + if (self->rx_buff.head == NULL) { + err = -ENOMEM; + goto err_out2; + } + memset(self->rx_buff.head, 0, self->rx_buff.truesize); + + self->tx_buff.head = + dma_alloc_coherent(NULL, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); + if (self->tx_buff.head == NULL) { + err = -ENOMEM; + goto err_out3; + } + 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; + + /* Reset Tx queue info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + + + /* Keep track of module usage */ + SET_MODULE_OWNER(dev); + + /* Override the network functions we need to use */ + dev->hard_start_xmit = ali_ircc_sir_hard_xmit; + dev->open = ali_ircc_net_open; + dev->stop = ali_ircc_net_close; + dev->do_ioctl = ali_ircc_net_ioctl; + dev->get_stats = ali_ircc_net_get_stats; + + err = register_netdev(dev); + if (err) { + IRDA_ERROR("%s(), register_netdev() failed!\n", __FUNCTION__); + goto err_out4; + } + IRDA_MESSAGE("IrDA: Registered device %s\n", dev->name); + + /* Check dongle id */ + dongle_id = ali_ircc_read_dongle_id(i, info); + IRDA_MESSAGE("%s(), %s, Found dongle: %s\n", __FUNCTION__, driver_name, dongle_types[dongle_id]); + + self->io.dongle_id = dongle_id; + + pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, ali_ircc_pmproc); + if (pmdev) + pmdev->data = self; + + IRDA_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__); + + return 0; + + err_out4: + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + err_out3: + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + err_out2: + release_region(self->io.fir_base, self->io.fir_ext); + err_out1: + dev_self[i] = NULL; + free_netdev(dev); + return err; +} + + +/* + * Function ali_ircc_close (self) + * + * Close driver instance + * + */ +static int __exit ali_ircc_close(struct ali_ircc_cb *self) +{ + int iobase; + + IRDA_DEBUG(4, "%s(), ---------------- Start ----------------\n", __FUNCTION__); + + IRDA_ASSERT(self != NULL, return -1;); + + iobase = self->io.fir_base; + + /* Remove netdevice */ + unregister_netdev(self->netdev); + + /* Release the PORT that this driver is using */ + IRDA_DEBUG(4, "%s(), Releasing Region %03x\n", __FUNCTION__, self->io.fir_base); + release_region(self->io.fir_base, self->io.fir_ext); + + if (self->tx_buff.head) + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + + if (self->rx_buff.head) + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + + dev_self[self->index] = NULL; + free_netdev(self->netdev); + + IRDA_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__); + + return 0; +} + +/* + * Function ali_ircc_init_43 (chip, info) + * + * Initialize the ALi M1543 chip. + */ +static int ali_ircc_init_43(ali_chip_t *chip, chipio_t *info) +{ + /* All controller information like I/O address, DMA channel, IRQ + * are set by BIOS + */ + + return 0; +} + +/* + * Function ali_ircc_init_53 (chip, info) + * + * Initialize the ALi M1535 chip. + */ +static int ali_ircc_init_53(ali_chip_t *chip, chipio_t *info) +{ + /* All controller information like I/O address, DMA channel, IRQ + * are set by BIOS + */ + + return 0; +} + +/* + * Function ali_ircc_probe_53 (chip, info) + * + * Probes for the ALi M1535D or M1535 + */ +static int ali_ircc_probe_53(ali_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + int hi, low, reg; + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); + + /* Enter Configuration */ + outb(chip->entr1, cfg_base); + outb(chip->entr2, cfg_base); + + /* Select Logical Device 5 Registers (UART2) */ + outb(0x07, cfg_base); + outb(0x05, cfg_base+1); + + /* Read address control register */ + outb(0x60, cfg_base); + hi = inb(cfg_base+1); + outb(0x61, cfg_base); + low = inb(cfg_base+1); + info->fir_base = (hi<<8) + low; + + info->sir_base = info->fir_base; + + IRDA_DEBUG(2, "%s(), probing fir_base=0x%03x\n", __FUNCTION__, info->fir_base); + + /* Read IRQ control register */ + outb(0x70, cfg_base); + reg = inb(cfg_base+1); + info->irq = reg & 0x0f; + IRDA_DEBUG(2, "%s(), probing irq=%d\n", __FUNCTION__, info->irq); + + /* Read DMA channel */ + outb(0x74, cfg_base); + reg = inb(cfg_base+1); + info->dma = reg & 0x07; + + if(info->dma == 0x04) + IRDA_WARNING("%s(), No DMA channel assigned !\n", __FUNCTION__); + else + IRDA_DEBUG(2, "%s(), probing dma=%d\n", __FUNCTION__, info->dma); + + /* Read Enabled Status */ + outb(0x30, cfg_base); + reg = inb(cfg_base+1); + info->enabled = (reg & 0x80) && (reg & 0x01); + IRDA_DEBUG(2, "%s(), probing enabled=%d\n", __FUNCTION__, info->enabled); + + /* Read Power Status */ + outb(0x22, cfg_base); + reg = inb(cfg_base+1); + info->suspended = (reg & 0x20); + IRDA_DEBUG(2, "%s(), probing suspended=%d\n", __FUNCTION__, info->suspended); + + /* Exit configuration */ + outb(0xbb, cfg_base); + + IRDA_DEBUG(2, "%s(), ----------------- End -----------------\n", __FUNCTION__); + + return 0; +} + +/* + * Function ali_ircc_setup (info) + * + * Set FIR FIFO and DMA Threshold + * Returns non-negative on success. + * + */ +static int ali_ircc_setup(chipio_t *info) +{ + unsigned char tmp; + int version; + int iobase = info->fir_base; + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); + + /* Locking comments : + * Most operations here need to be protected. We are called before + * the device instance is created in ali_ircc_open(), therefore + * nobody can bother us - Jean II */ + + /* Switch to FIR space */ + SIR2FIR(iobase); + + /* Master Reset */ + outb(0x40, iobase+FIR_MCR); // benjamin 2000/11/30 11:45AM + + /* Read FIR ID Version Register */ + switch_bank(iobase, BANK3); + version = inb(iobase+FIR_ID_VR); + + /* Should be 0x00 in the M1535/M1535D */ + if(version != 0x00) + { + IRDA_ERROR("%s, Wrong chip version %02x\n", driver_name, version); + return -1; + } + + // IRDA_MESSAGE("%s, Found chip at base=0x%03x\n", driver_name, info->cfg_base); + + /* Set FIR FIFO Threshold Register */ + switch_bank(iobase, BANK1); + outb(RX_FIFO_Threshold, iobase+FIR_FIFO_TR); + + /* Set FIR DMA Threshold Register */ + outb(RX_DMA_Threshold, iobase+FIR_DMA_TR); + + /* CRC enable */ + switch_bank(iobase, BANK2); + outb(inb(iobase+FIR_IRDA_CR) | IRDA_CR_CRC, iobase+FIR_IRDA_CR); + + /* NDIS driver set TX Length here BANK2 Alias 3, Alias4*/ + + /* Switch to Bank 0 */ + switch_bank(iobase, BANK0); + + tmp = inb(iobase+FIR_LCR_B); + tmp &=~0x20; // disable SIP + tmp |= 0x80; // these two steps make RX mode + tmp &= 0xbf; + outb(tmp, iobase+FIR_LCR_B); + + /* Disable Interrupt */ + outb(0x00, iobase+FIR_IER); + + + /* Switch to SIR space */ + FIR2SIR(iobase); + + IRDA_MESSAGE("%s, driver loaded (Benjamin Kong)\n", driver_name); + + /* Enable receive interrupts */ + // outb(UART_IER_RDI, iobase+UART_IER); //benjamin 2000/11/23 01:25PM + // Turn on the interrupts in ali_ircc_net_open + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__); + + return 0; +} + +/* + * Function ali_ircc_read_dongle_id (int index, info) + * + * Try to read dongle indentification. This procedure needs to be executed + * once after power-on/reset. It also needs to be used whenever you suspect + * that the user may have plugged/unplugged the IrDA Dongle. + */ +static int ali_ircc_read_dongle_id (int i, chipio_t *info) +{ + int dongle_id, reg; + int cfg_base = info->cfg_base; + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); + + /* Enter Configuration */ + outb(chips[i].entr1, cfg_base); + outb(chips[i].entr2, cfg_base); + + /* Select Logical Device 5 Registers (UART2) */ + outb(0x07, cfg_base); + outb(0x05, cfg_base+1); + + /* Read Dongle ID */ + outb(0xf0, cfg_base); + reg = inb(cfg_base+1); + dongle_id = ((reg>>6)&0x02) | ((reg>>5)&0x01); + IRDA_DEBUG(2, "%s(), probing dongle_id=%d, dongle_types=%s\n", __FUNCTION__, + dongle_id, dongle_types[dongle_id]); + + /* Exit configuration */ + outb(0xbb, cfg_base); + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__); + + return dongle_id; +} + +/* + * Function ali_ircc_interrupt (irq, dev_id, regs) + * + * An interrupt from the chip has arrived. Time to do some work + * + */ +static irqreturn_t ali_ircc_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct ali_ircc_cb *self; + int ret; + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); + + if (!dev) { + IRDA_WARNING("%s: irq %d for unknown device.\n", driver_name, irq); + return IRQ_NONE; + } + + self = (struct ali_ircc_cb *) dev->priv; + + spin_lock(&self->lock); + + /* Dispatch interrupt handler for the current speed */ + if (self->io.speed > 115200) + ret = ali_ircc_fir_interrupt(self); + else + ret = ali_ircc_sir_interrupt(self); + + spin_unlock(&self->lock); + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__); + return ret; +} +/* + * Function ali_ircc_fir_interrupt(irq, struct ali_ircc_cb *self) + * + * Handle MIR/FIR interrupt + * + */ +static irqreturn_t ali_ircc_fir_interrupt(struct ali_ircc_cb *self) +{ + __u8 eir, OldMessageCount; + int iobase, tmp; + + IRDA_DEBUG(1, "%s(), ---------------- Start ----------------\n", __FUNCTION__); + + iobase = self->io.fir_base; + + switch_bank(iobase, BANK0); + self->InterruptID = inb(iobase+FIR_IIR); + self->BusStatus = inb(iobase+FIR_BSR); + + OldMessageCount = (self->LineStatus + 1) & 0x07; + self->LineStatus = inb(iobase+FIR_LSR); + //self->ier = inb(iobase+FIR_IER); 2000/12/1 04:32PM + eir = self->InterruptID & self->ier; /* Mask out the interesting ones */ + + IRDA_DEBUG(1, "%s(), self->InterruptID = %x\n", __FUNCTION__,self->InterruptID); + IRDA_DEBUG(1, "%s(), self->LineStatus = %x\n", __FUNCTION__,self->LineStatus); + IRDA_DEBUG(1, "%s(), self->ier = %x\n", __FUNCTION__,self->ier); + IRDA_DEBUG(1, "%s(), eir = %x\n", __FUNCTION__,eir); + + /* Disable interrupts */ + SetCOMInterrupts(self, FALSE); + + /* Tx or Rx Interrupt */ + + if (eir & IIR_EOM) + { + if (self->io.direction == IO_XMIT) /* TX */ + { + IRDA_DEBUG(1, "%s(), ******* IIR_EOM (Tx) *******\n", __FUNCTION__); + + if(ali_ircc_dma_xmit_complete(self)) + { + if (irda_device_txqueue_empty(self->netdev)) + { + /* Prepare for receive */ + ali_ircc_dma_receive(self); + self->ier = IER_EOM; + } + } + else + { + self->ier = IER_EOM; + } + + } + else /* RX */ + { + IRDA_DEBUG(1, "%s(), ******* IIR_EOM (Rx) *******\n", __FUNCTION__); + + if(OldMessageCount > ((self->LineStatus+1) & 0x07)) + { + self->rcvFramesOverflow = TRUE; + IRDA_DEBUG(1, "%s(), ******* self->rcvFramesOverflow = TRUE ******** \n", __FUNCTION__); + } + + if (ali_ircc_dma_receive_complete(self)) + { + IRDA_DEBUG(1, "%s(), ******* receive complete ******** \n", __FUNCTION__); + + self->ier = IER_EOM; + } + else + { + IRDA_DEBUG(1, "%s(), ******* Not receive complete ******** \n", __FUNCTION__); + + self->ier = IER_EOM | IER_TIMER; + } + + } + } + /* Timer Interrupt */ + else if (eir & IIR_TIMER) + { + if(OldMessageCount > ((self->LineStatus+1) & 0x07)) + { + self->rcvFramesOverflow = TRUE; + IRDA_DEBUG(1, "%s(), ******* self->rcvFramesOverflow = TRUE ******* \n", __FUNCTION__); + } + /* Disable Timer */ + switch_bank(iobase, BANK1); + tmp = inb(iobase+FIR_CR); + outb( tmp& ~CR_TIMER_EN, iobase+FIR_CR); + + /* Check if this is a Tx timer interrupt */ + if (self->io.direction == IO_XMIT) + { + ali_ircc_dma_xmit(self); + + /* Interrupt on EOM */ + self->ier = IER_EOM; + + } + else /* Rx */ + { + if(ali_ircc_dma_receive_complete(self)) + { + self->ier = IER_EOM; + } + else + { + self->ier = IER_EOM | IER_TIMER; + } + } + } + + /* Restore Interrupt */ + SetCOMInterrupts(self, TRUE); + + IRDA_DEBUG(1, "%s(), ----------------- End ---------------\n", __FUNCTION__); + return IRQ_RETVAL(eir); +} + +/* + * Function ali_ircc_sir_interrupt (irq, self, eir) + * + * Handle SIR interrupt + * + */ +static irqreturn_t ali_ircc_sir_interrupt(struct ali_ircc_cb *self) +{ + int iobase; + int iir, lsr; + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); + + iobase = self->io.sir_base; + + iir = inb(iobase+UART_IIR) & UART_IIR_ID; + if (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 */ + ali_ircc_sir_receive(self); + break; + case UART_IIR_THRI: + if (lsr & UART_LSR_THRE) + { + /* Transmitter ready for data */ + ali_ircc_sir_write_wakeup(self); + } + break; + default: + IRDA_DEBUG(0, "%s(), unhandled IIR=%#x\n", __FUNCTION__, iir); + break; + } + + } + + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__); + + return IRQ_RETVAL(iir); +} + + +/* + * Function ali_ircc_sir_receive (self) + * + * Receive one frame from the infrared port + * + */ +static void ali_ircc_sir_receive(struct ali_ircc_cb *self) +{ + int boguscount = 0; + int iobase; + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__); + IRDA_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 too long */ + if (boguscount++ > 32) { + IRDA_DEBUG(2,"%s(), breaking!\n", __FUNCTION__); + break; + } + } while (inb(iobase+UART_LSR) & UART_LSR_DR); + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); +} + +/* + * Function ali_ircc_sir_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 ali_ircc_sir_write_wakeup(struct ali_ircc_cb *self) +{ + int actual = 0; + int iobase; + + IRDA_ASSERT(self != NULL, return;); + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + iobase = self->io.sir_base; + + /* Finished with frame? */ + if (self->tx_buff.len > 0) + { + /* Write data left in transmit buffer */ + actual = ali_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->new_speed) + { + /* We must wait until all data are gone */ + while(!(inb(iobase+UART_LSR) & UART_LSR_TEMT)) + IRDA_DEBUG(1, "%s(), UART_LSR_THRE\n", __FUNCTION__ ); + + IRDA_DEBUG(1, "%s(), Changing speed! self->new_speed = %d\n", __FUNCTION__ , self->new_speed); + ali_ircc_change_speed(self, self->new_speed); + self->new_speed = 0; + + // benjamin 2000/11/10 06:32PM + if (self->io.speed > 115200) + { + IRDA_DEBUG(2, "%s(), ali_ircc_change_speed from UART_LSR_TEMT \n", __FUNCTION__ ); + + self->ier = IER_EOM; + // SetCOMInterrupts(self, TRUE); + return; + } + } + else + { + netif_wake_queue(self->netdev); + } + + self->stats.tx_packets++; + + /* Turn on receive interrupts */ + outb(UART_IER_RDI, iobase+UART_IER); + } + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); +} + +static void ali_ircc_change_speed(struct ali_ircc_cb *self, __u32 baud) +{ + struct net_device *dev = self->netdev; + int iobase; + + IRDA_DEBUG(1, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + IRDA_DEBUG(2, "%s(), setting speed = %d \n", __FUNCTION__ , baud); + + /* This function *must* be called with irq off and spin-lock. + * - Jean II */ + + iobase = self->io.fir_base; + + SetCOMInterrupts(self, FALSE); // 2000/11/24 11:43AM + + /* Go to MIR, FIR Speed */ + if (baud > 115200) + { + + + ali_ircc_fir_change_speed(self, baud); + + /* Install FIR xmit handler*/ + dev->hard_start_xmit = ali_ircc_fir_hard_xmit; + + /* Enable Interuupt */ + self->ier = IER_EOM; // benjamin 2000/11/20 07:24PM + + /* Be ready for incomming frames */ + ali_ircc_dma_receive(self); // benajmin 2000/11/8 07:46PM not complete + } + /* Go to SIR Speed */ + else + { + ali_ircc_sir_change_speed(self, baud); + + /* Install SIR xmit handler*/ + dev->hard_start_xmit = ali_ircc_sir_hard_xmit; + } + + + SetCOMInterrupts(self, TRUE); // 2000/11/24 11:43AM + + netif_wake_queue(self->netdev); + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); +} + +static void ali_ircc_fir_change_speed(struct ali_ircc_cb *priv, __u32 baud) +{ + + int iobase; + struct ali_ircc_cb *self = (struct ali_ircc_cb *) priv; + struct net_device *dev; + + IRDA_DEBUG(1, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + IRDA_ASSERT(self != NULL, return;); + + dev = self->netdev; + iobase = self->io.fir_base; + + IRDA_DEBUG(1, "%s(), self->io.speed = %d, change to speed = %d\n", __FUNCTION__ ,self->io.speed,baud); + + /* Come from SIR speed */ + if(self->io.speed <=115200) + { + SIR2FIR(iobase); + } + + /* Update accounting for new speed */ + self->io.speed = baud; + + // Set Dongle Speed mode + ali_ircc_change_dongle_speed(self, baud); + + IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); +} + +/* + * Function ali_sir_change_speed (self, speed) + * + * Set speed of IrDA port to specified baudrate + * + */ +static void ali_ircc_sir_change_speed(struct ali_ircc_cb *priv, __u32 speed) +{ + struct ali_ircc_cb *self = (struct ali_ircc_cb *) priv; + unsigned long flags; + int iobase; + int fcr; /* FIFO control reg */ + int lcr; /* Line control reg */ + int divisor; + + IRDA_DEBUG(1, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + IRDA_DEBUG(1, "%s(), Setting speed to: %d\n", __FUNCTION__ , speed); + + IRDA_ASSERT(self != NULL, return;); + + iobase = self->io.sir_base; + + /* Come from MIR or FIR speed */ + if(self->io.speed >115200) + { + // Set Dongle Speed mode first + ali_ircc_change_dongle_speed(self, speed); + + FIR2SIR(iobase); + } + + // Clear Line and Auxiluary status registers 2000/11/24 11:47AM + + inb(iobase+UART_LSR); + inb(iobase+UART_SCR); + + /* Update accounting for new speed */ + self->io.speed = speed; + + spin_lock_irqsave(&self->lock, flags); + + divisor = 115200/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 */ + + /* without this, the conection will be broken after come back from FIR speed, + but with this, the SIR connection is harder to established */ + outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase+UART_MCR); + + spin_unlock_irqrestore(&self->lock, flags); + + IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); +} + +static void ali_ircc_change_dongle_speed(struct ali_ircc_cb *priv, int speed) +{ + + struct ali_ircc_cb *self = (struct ali_ircc_cb *) priv; + int iobase,dongle_id; + int tmp = 0; + + IRDA_DEBUG(1, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + iobase = self->io.fir_base; /* or iobase = self->io.sir_base; */ + dongle_id = self->io.dongle_id; + + /* We are already locked, no need to do it again */ + + IRDA_DEBUG(1, "%s(), Set Speed for %s , Speed = %d\n", __FUNCTION__ , dongle_types[dongle_id], speed); + + switch_bank(iobase, BANK2); + tmp = inb(iobase+FIR_IRDA_CR); + + /* IBM type dongle */ + if(dongle_id == 0) + { + if(speed == 4000000) + { + // __ __ + // SD/MODE __| |__ __ + // __ __ + // IRTX __ __| |__ + // T1 T2 T3 T4 T5 + + tmp &= ~IRDA_CR_HDLC; // HDLC=0 + tmp |= IRDA_CR_CRC; // CRC=1 + + switch_bank(iobase, BANK2); + outb(tmp, iobase+FIR_IRDA_CR); + + // T1 -> SD/MODE:0 IRTX:0 + tmp &= ~0x09; + tmp |= 0x02; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // T2 -> SD/MODE:1 IRTX:0 + tmp &= ~0x01; + tmp |= 0x0a; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // T3 -> SD/MODE:1 IRTX:1 + tmp |= 0x0b; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // T4 -> SD/MODE:0 IRTX:1 + tmp &= ~0x08; + tmp |= 0x03; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // T5 -> SD/MODE:0 IRTX:0 + tmp &= ~0x09; + tmp |= 0x02; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // reset -> Normal TX output Signal + outb(tmp & ~0x02, iobase+FIR_IRDA_CR); + } + else /* speed <=1152000 */ + { + // __ + // SD/MODE __| |__ + // + // IRTX ________ + // T1 T2 T3 + + /* MIR 115200, 57600 */ + if (speed==1152000) + { + tmp |= 0xA0; //HDLC=1, 1.152Mbps=1 + } + else + { + tmp &=~0x80; //HDLC 0.576Mbps + tmp |= 0x20; //HDLC=1, + } + + tmp |= IRDA_CR_CRC; // CRC=1 + + switch_bank(iobase, BANK2); + outb(tmp, iobase+FIR_IRDA_CR); + + /* MIR 115200, 57600 */ + + //switch_bank(iobase, BANK2); + // T1 -> SD/MODE:0 IRTX:0 + tmp &= ~0x09; + tmp |= 0x02; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // T2 -> SD/MODE:1 IRTX:0 + tmp &= ~0x01; + tmp |= 0x0a; + outb(tmp, iobase+FIR_IRDA_CR); + + // T3 -> SD/MODE:0 IRTX:0 + tmp &= ~0x09; + tmp |= 0x02; + outb(tmp, iobase+FIR_IRDA_CR); + udelay(2); + + // reset -> Normal TX output Signal + outb(tmp & ~0x02, iobase+FIR_IRDA_CR); + } + } + else if (dongle_id == 1) /* HP HDSL-3600 */ + { + switch(speed) + { + case 4000000: + tmp &= ~IRDA_CR_HDLC; // HDLC=0 + break; + + case 1152000: + tmp |= 0xA0; // HDLC=1, 1.152Mbps=1 + break; + + case 576000: + tmp &=~0x80; // HDLC 0.576Mbps + tmp |= 0x20; // HDLC=1, + break; + } + + tmp |= IRDA_CR_CRC; // CRC=1 + + switch_bank(iobase, BANK2); + outb(tmp, iobase+FIR_IRDA_CR); + } + else /* HP HDSL-1100 */ + { + if(speed <= 115200) /* SIR */ + { + + tmp &= ~IRDA_CR_FIR_SIN; // HP sin select = 0 + + switch_bank(iobase, BANK2); + outb(tmp, iobase+FIR_IRDA_CR); + } + else /* MIR FIR */ + { + + switch(speed) + { + case 4000000: + tmp &= ~IRDA_CR_HDLC; // HDLC=0 + break; + + case 1152000: + tmp |= 0xA0; // HDLC=1, 1.152Mbps=1 + break; + + case 576000: + tmp &=~0x80; // HDLC 0.576Mbps + tmp |= 0x20; // HDLC=1, + break; + } + + tmp |= IRDA_CR_CRC; // CRC=1 + tmp |= IRDA_CR_FIR_SIN; // HP sin select = 1 + + switch_bank(iobase, BANK2); + outb(tmp, iobase+FIR_IRDA_CR); + } + } + + switch_bank(iobase, BANK0); + + IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); +} + +/* + * Function ali_ircc_sir_write (driver) + * + * Fill Tx FIFO with transmit data + * + */ +static int ali_ircc_sir_write(int iobase, int fifo_size, __u8 *buf, int len) +{ + int actual = 0; + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + /* 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++; + } + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); + return actual; +} + +/* + * Function ali_ircc_net_open (dev) + * + * Start the device + * + */ +static int ali_ircc_net_open(struct net_device *dev) +{ + struct ali_ircc_cb *self; + int iobase; + char hwname[32]; + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + IRDA_ASSERT(dev != NULL, return -1;); + + self = (struct ali_ircc_cb *) dev->priv; + + IRDA_ASSERT(self != NULL, return 0;); + + iobase = self->io.fir_base; + + /* Request IRQ and install Interrupt Handler */ + if (request_irq(self->io.irq, ali_ircc_interrupt, 0, dev->name, dev)) + { + IRDA_WARNING("%s, unable to allocate irq=%d\n", driver_name, + self->io.irq); + return -EAGAIN; + } + + /* + * Always allocate the DMA channel after the IRQ, and clean up on + * failure. + */ + if (request_dma(self->io.dma, dev->name)) { + IRDA_WARNING("%s, unable to allocate dma=%d\n", driver_name, + self->io.dma); + free_irq(self->io.irq, self); + return -EAGAIN; + } + + /* Turn on interrups */ + outb(UART_IER_RDI , iobase+UART_IER); + + /* Ready to play! */ + netif_start_queue(dev); //benjamin by irport + + /* Give self a hardware name */ + sprintf(hwname, "ALI-FIR @ 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); + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); + + return 0; +} + +/* + * Function ali_ircc_net_close (dev) + * + * Stop the device + * + */ +static int ali_ircc_net_close(struct net_device *dev) +{ + + struct ali_ircc_cb *self; + //int iobase; + + IRDA_DEBUG(4, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + IRDA_ASSERT(dev != NULL, return -1;); + + self = (struct ali_ircc_cb *) dev->priv; + IRDA_ASSERT(self != NULL, return 0;); + + /* Stop device */ + netif_stop_queue(dev); + + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close(self->irlap); + self->irlap = NULL; + + disable_dma(self->io.dma); + + /* Disable interrupts */ + SetCOMInterrupts(self, FALSE); + + free_irq(self->io.irq, dev); + free_dma(self->io.dma); + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); + + return 0; +} + +/* + * Function ali_ircc_fir_hard_xmit (skb, dev) + * + * Transmit the frame + * + */ +static int ali_ircc_fir_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ali_ircc_cb *self; + unsigned long flags; + int iobase; + __u32 speed; + int mtt, diff; + + IRDA_DEBUG(1, "%s(), ---------------- Start -----------------\n", __FUNCTION__ ); + + self = (struct ali_ircc_cb *) dev->priv; + iobase = self->io.fir_base; + + netif_stop_queue(dev); + + /* Make sure tests *& speed change are atomic */ + spin_lock_irqsave(&self->lock, flags); + + /* 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 */ + + /* 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) { + ali_ircc_change_speed(self, speed); + dev->trans_start = jiffies; + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + return 0; + } else + self->new_speed = speed; + } + + /* Register and copy this frame to DMA memory */ + self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail; + self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; + self->tx_fifo.tail += skb->len; + + self->stats.tx_bytes += skb->len; + + memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, + skb->len); + + self->tx_fifo.len++; + self->tx_fifo.free++; + + /* Start transmit only if there is currently no transmit going on */ + if (self->tx_fifo.len == 1) + { + /* Check if we must wait the min turn time or not */ + mtt = irda_get_mtt(skb); + + if (mtt) + { + /* Check how much time we have used already */ + do_gettimeofday(&self->now); + + diff = self->now.tv_usec - self->stamp.tv_usec; + /* self->stamp is set from ali_ircc_dma_receive_complete() */ + + IRDA_DEBUG(1, "%s(), ******* diff = %d ******* \n", __FUNCTION__ , diff); + + if (diff < 0) + diff += 1000000; + + /* Check if the mtt is larger than the time we have + * already used by all the protocol processing + */ + if (mtt > diff) + { + mtt -= diff; + + /* + * Use timer if delay larger than 1000 us, and + * use udelay for smaller values which should + * be acceptable + */ + if (mtt > 500) + { + /* Adjust for timer resolution */ + mtt = (mtt+250) / 500; /* 4 discard, 5 get advanced, Let's round off */ + + IRDA_DEBUG(1, "%s(), ************** mtt = %d ***********\n", __FUNCTION__ , mtt); + + /* Setup timer */ + if (mtt == 1) /* 500 us */ + { + switch_bank(iobase, BANK1); + outb(TIMER_IIR_500, iobase+FIR_TIMER_IIR); + } + else if (mtt == 2) /* 1 ms */ + { + switch_bank(iobase, BANK1); + outb(TIMER_IIR_1ms, iobase+FIR_TIMER_IIR); + } + else /* > 2ms -> 4ms */ + { + switch_bank(iobase, BANK1); + outb(TIMER_IIR_2ms, iobase+FIR_TIMER_IIR); + } + + + /* Start timer */ + outb(inb(iobase+FIR_CR) | CR_TIMER_EN, iobase+FIR_CR); + self->io.direction = IO_XMIT; + + /* Enable timer interrupt */ + self->ier = IER_TIMER; + SetCOMInterrupts(self, TRUE); + + /* Timer will take care of the rest */ + goto out; + } + else + udelay(mtt); + } // if (if (mtt > diff) + }// if (mtt) + + /* Enable EOM interrupt */ + self->ier = IER_EOM; + SetCOMInterrupts(self, TRUE); + + /* Transmit frame */ + ali_ircc_dma_xmit(self); + } // if (self->tx_fifo.len == 1) + + out: + + /* Not busy transmitting anymore if window is not full */ + if (self->tx_fifo.free < MAX_TX_WINDOW) + netif_wake_queue(self->netdev); + + /* Restore bank register */ + switch_bank(iobase, BANK0); + + dev->trans_start = jiffies; + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + + IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); + return 0; +} + + +static void ali_ircc_dma_xmit(struct ali_ircc_cb *self) +{ + int iobase, tmp; + unsigned char FIFO_OPTI, Hi, Lo; + + + IRDA_DEBUG(1, "%s(), ---------------- Start -----------------\n", __FUNCTION__ ); + + iobase = self->io.fir_base; + + /* FIFO threshold , this method comes from NDIS5 code */ + + if(self->tx_fifo.queue[self->tx_fifo.ptr].len < TX_FIFO_Threshold) + FIFO_OPTI = self->tx_fifo.queue[self->tx_fifo.ptr].len-1; + else + FIFO_OPTI = TX_FIFO_Threshold; + + /* Disable DMA */ + switch_bank(iobase, BANK1); + outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR); + + self->io.direction = IO_XMIT; + + irda_setup_dma(self->io.dma, + ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start - + self->tx_buff.head) + self->tx_buff_dma, + self->tx_fifo.queue[self->tx_fifo.ptr].len, + DMA_TX_MODE); + + /* Reset Tx FIFO */ + switch_bank(iobase, BANK0); + outb(LCR_A_FIFO_RESET, iobase+FIR_LCR_A); + + /* Set Tx FIFO threshold */ + if (self->fifo_opti_buf!=FIFO_OPTI) + { + switch_bank(iobase, BANK1); + outb(FIFO_OPTI, iobase+FIR_FIFO_TR) ; + self->fifo_opti_buf=FIFO_OPTI; + } + + /* Set Tx DMA threshold */ + switch_bank(iobase, BANK1); + outb(TX_DMA_Threshold, iobase+FIR_DMA_TR); + + /* Set max Tx frame size */ + Hi = (self->tx_fifo.queue[self->tx_fifo.ptr].len >> 8) & 0x0f; + Lo = self->tx_fifo.queue[self->tx_fifo.ptr].len & 0xff; + switch_bank(iobase, BANK2); + outb(Hi, iobase+FIR_TX_DSR_HI); + outb(Lo, iobase+FIR_TX_DSR_LO); + + /* Disable SIP , Disable Brick Wall (we don't support in TX mode), Change to TX mode */ + switch_bank(iobase, BANK0); + tmp = inb(iobase+FIR_LCR_B); + tmp &= ~0x20; // Disable SIP + outb(((unsigned char)(tmp & 0x3f) | LCR_B_TX_MODE) & ~LCR_B_BW, iobase+FIR_LCR_B); + IRDA_DEBUG(1, "%s(), ******* Change to TX mode: FIR_LCR_B = 0x%x ******* \n", __FUNCTION__ , inb(iobase+FIR_LCR_B)); + + outb(0, iobase+FIR_LSR); + + /* Enable DMA and Burst Mode */ + switch_bank(iobase, BANK1); + outb(inb(iobase+FIR_CR) | CR_DMA_EN | CR_DMA_BURST, iobase+FIR_CR); + + switch_bank(iobase, BANK0); + + IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); +} + +static int ali_ircc_dma_xmit_complete(struct ali_ircc_cb *self) +{ + int iobase; + int ret = TRUE; + + IRDA_DEBUG(1, "%s(), ---------------- Start -----------------\n", __FUNCTION__ ); + + iobase = self->io.fir_base; + + /* Disable DMA */ + switch_bank(iobase, BANK1); + outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR); + + /* Check for underrun! */ + switch_bank(iobase, BANK0); + if((inb(iobase+FIR_LSR) & LSR_FRAME_ABORT) == LSR_FRAME_ABORT) + + { + IRDA_ERROR("%s(), ********* LSR_FRAME_ABORT *********\n", __FUNCTION__); + self->stats.tx_errors++; + self->stats.tx_fifo_errors++; + } + else + { + self->stats.tx_packets++; + } + + /* Check if we need to change the speed */ + if (self->new_speed) + { + ali_ircc_change_speed(self, self->new_speed); + self->new_speed = 0; + } + + /* Finished with this frame, so prepare for next */ + self->tx_fifo.ptr++; + self->tx_fifo.len--; + + /* Any frames to be sent back-to-back? */ + if (self->tx_fifo.len) + { + ali_ircc_dma_xmit(self); + + /* Not finished yet! */ + ret = FALSE; + } + else + { /* Reset Tx FIFO info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + } + + /* Make sure we have room for more frames */ + if (self->tx_fifo.free < MAX_TX_WINDOW) { + /* Not busy transmitting anymore */ + /* Tell the network layer, that we can accept more frames */ + netif_wake_queue(self->netdev); + } + + switch_bank(iobase, BANK0); + + IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); + return ret; +} + +/* + * Function ali_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 ali_ircc_dma_receive(struct ali_ircc_cb *self) +{ + int iobase, tmp; + + IRDA_DEBUG(1, "%s(), ---------------- Start -----------------\n", __FUNCTION__ ); + + iobase = self->io.fir_base; + + /* Reset Tx FIFO info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + + /* Disable DMA */ + switch_bank(iobase, BANK1); + outb(inb(iobase+FIR_CR) & ~CR_DMA_EN, iobase+FIR_CR); + + /* Reset Message Count */ + switch_bank(iobase, BANK0); + outb(0x07, iobase+FIR_LSR); + + self->rcvFramesOverflow = FALSE; + + self->LineStatus = inb(iobase+FIR_LSR) ; + + /* Reset Rx FIFO info */ + self->io.direction = IO_RECV; + self->rx_buff.data = self->rx_buff.head; + + /* Reset Rx FIFO */ + // switch_bank(iobase, BANK0); + outb(LCR_A_FIFO_RESET, iobase+FIR_LCR_A); + + self->st_fifo.len = self->st_fifo.pending_bytes = 0; + self->st_fifo.tail = self->st_fifo.head = 0; + + irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, + DMA_RX_MODE); + + /* Set Receive Mode,Brick Wall */ + //switch_bank(iobase, BANK0); + tmp = inb(iobase+FIR_LCR_B); + outb((unsigned char)(tmp &0x3f) | LCR_B_RX_MODE | LCR_B_BW , iobase + FIR_LCR_B); // 2000/12/1 05:16PM + IRDA_DEBUG(1, "%s(), *** Change To RX mode: FIR_LCR_B = 0x%x *** \n", __FUNCTION__ , inb(iobase+FIR_LCR_B)); + + /* Set Rx Threshold */ + switch_bank(iobase, BANK1); + outb(RX_FIFO_Threshold, iobase+FIR_FIFO_TR); + outb(RX_DMA_Threshold, iobase+FIR_DMA_TR); + + /* Enable DMA and Burst Mode */ + // switch_bank(iobase, BANK1); + outb(CR_DMA_EN | CR_DMA_BURST, iobase+FIR_CR); + + switch_bank(iobase, BANK0); + IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); + return 0; +} + +static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self) +{ + struct st_fifo *st_fifo; + struct sk_buff *skb; + __u8 status, MessageCount; + int len, i, iobase, val; + + IRDA_DEBUG(1, "%s(), ---------------- Start -----------------\n", __FUNCTION__ ); + + st_fifo = &self->st_fifo; + iobase = self->io.fir_base; + + switch_bank(iobase, BANK0); + MessageCount = inb(iobase+ FIR_LSR)&0x07; + + if (MessageCount > 0) + IRDA_DEBUG(0, "%s(), Messsage count = %d,\n", __FUNCTION__ , MessageCount); + + for (i=0; i<=MessageCount; i++) + { + /* Bank 0 */ + switch_bank(iobase, BANK0); + status = inb(iobase+FIR_LSR); + + switch_bank(iobase, BANK2); + len = inb(iobase+FIR_RX_DSR_HI) & 0x0f; + len = len << 8; + len |= inb(iobase+FIR_RX_DSR_LO); + + IRDA_DEBUG(1, "%s(), RX Length = 0x%.2x,\n", __FUNCTION__ , len); + IRDA_DEBUG(1, "%s(), RX Status = 0x%.2x,\n", __FUNCTION__ , status); + + if (st_fifo->tail >= MAX_RX_WINDOW) { + IRDA_DEBUG(0, "%s(), window is full!\n", __FUNCTION__ ); + continue; + } + + st_fifo->entries[st_fifo->tail].status = status; + st_fifo->entries[st_fifo->tail].len = len; + st_fifo->pending_bytes += len; + st_fifo->tail++; + st_fifo->len++; + } + + for (i=0; i<=MessageCount; i++) + { + /* Get first entry */ + status = st_fifo->entries[st_fifo->head].status; + len = st_fifo->entries[st_fifo->head].len; + st_fifo->pending_bytes -= len; + st_fifo->head++; + st_fifo->len--; + + /* Check for errors */ + if ((status & 0xd8) || self->rcvFramesOverflow || (len==0)) + { + IRDA_DEBUG(0,"%s(), ************* RX Errors ************ \n", __FUNCTION__ ); + + /* Skip frame */ + self->stats.rx_errors++; + + self->rx_buff.data += len; + + if (status & LSR_FIFO_UR) + { + self->stats.rx_frame_errors++; + IRDA_DEBUG(0,"%s(), ************* FIFO Errors ************ \n", __FUNCTION__ ); + } + if (status & LSR_FRAME_ERROR) + { + self->stats.rx_frame_errors++; + IRDA_DEBUG(0,"%s(), ************* FRAME Errors ************ \n", __FUNCTION__ ); + } + + if (status & LSR_CRC_ERROR) + { + self->stats.rx_crc_errors++; + IRDA_DEBUG(0,"%s(), ************* CRC Errors ************ \n", __FUNCTION__ ); + } + + if(self->rcvFramesOverflow) + { + self->stats.rx_frame_errors++; + IRDA_DEBUG(0,"%s(), ************* Overran DMA buffer ************ \n", __FUNCTION__ ); + } + if(len == 0) + { + self->stats.rx_frame_errors++; + IRDA_DEBUG(0,"%s(), ********** Receive Frame Size = 0 ********* \n", __FUNCTION__ ); + } + } + else + { + + if (st_fifo->pending_bytes < 32) + { + switch_bank(iobase, BANK0); + val = inb(iobase+FIR_BSR); + if ((val& BSR_FIFO_NOT_EMPTY)== 0x80) + { + IRDA_DEBUG(0, "%s(), ************* BSR_FIFO_NOT_EMPTY ************ \n", __FUNCTION__ ); + + /* Put this entry back in fifo */ + st_fifo->head--; + st_fifo->len++; + st_fifo->pending_bytes += len; + st_fifo->entries[st_fifo->head].status = status; + st_fifo->entries[st_fifo->head].len = len; + + /* + * DMA not finished yet, so try again + * later, set timer value, resolution + * 500 us + */ + + switch_bank(iobase, BANK1); + outb(TIMER_IIR_500, iobase+FIR_TIMER_IIR); // 2001/1/2 05:07PM + + /* Enable Timer */ + outb(inb(iobase+FIR_CR) | CR_TIMER_EN, iobase+FIR_CR); + + return FALSE; /* I'll be back! */ + } + } + + /* + * Remember the time we received this frame, so we can + * reduce the min turn time a bit since we will know + * how much time we have used for protocol processing + */ + do_gettimeofday(&self->stamp); + + skb = dev_alloc_skb(len+1); + if (skb == NULL) + { + IRDA_WARNING("%s(), memory squeeze, " + "dropping frame.\n", + __FUNCTION__); + self->stats.rx_dropped++; + + return FALSE; + } + + /* Make sure IP header gets aligned */ + skb_reserve(skb, 1); + + /* Copy frame without CRC, CRC is removed by hardware*/ + skb_put(skb, len); + memcpy(skb->data, self->rx_buff.data, len); + + /* Move to next frame */ + self->rx_buff.data += len; + self->stats.rx_bytes += len; + self->stats.rx_packets++; + + skb->dev = self->netdev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + self->netdev->last_rx = jiffies; + } + } + + switch_bank(iobase, BANK0); + + IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); + return TRUE; +} + + + +/* + * Function ali_ircc_sir_hard_xmit (skb, dev) + * + * Transmit the frame! + * + */ +static int ali_ircc_sir_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ali_ircc_cb *self; + unsigned long flags; + int iobase; + __u32 speed; + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + IRDA_ASSERT(dev != NULL, return 0;); + + self = (struct ali_ircc_cb *) dev->priv; + IRDA_ASSERT(self != NULL, return 0;); + + iobase = self->io.sir_base; + + netif_stop_queue(dev); + + /* Make sure tests *& speed change are atomic */ + spin_lock_irqsave(&self->lock, flags); + + /* 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 */ + + /* 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) { + ali_ircc_change_speed(self, speed); + dev->trans_start = jiffies; + 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); + + dev->trans_start = jiffies; + spin_unlock_irqrestore(&self->lock, flags); + + dev_kfree_skb(skb); + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); + + return 0; +} + + +/* + * Function ali_ircc_net_ioctl (dev, rq, cmd) + * + * Process IOCTL commands for this device + * + */ +static int ali_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct ali_ircc_cb *self; + unsigned long flags; + int ret = 0; + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + IRDA_ASSERT(dev != NULL, return -1;); + + self = dev->priv; + + IRDA_ASSERT(self != NULL, return -1;); + + IRDA_DEBUG(2, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__ , dev->name, cmd); + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + IRDA_DEBUG(1, "%s(), SIOCSBANDWIDTH\n", __FUNCTION__ ); + /* + * This function will also be used by IrLAP to change the + * speed, so we still must allow for speed change within + * interrupt context. + */ + if (!in_interrupt() && !capable(CAP_NET_ADMIN)) + return -EPERM; + + spin_lock_irqsave(&self->lock, flags); + ali_ircc_change_speed(self, irq->ifr_baudrate); + spin_unlock_irqrestore(&self->lock, flags); + break; + case SIOCSMEDIABUSY: /* Set media busy */ + IRDA_DEBUG(1, "%s(), SIOCSMEDIABUSY\n", __FUNCTION__ ); + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + irda_device_set_media_busy(self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + IRDA_DEBUG(2, "%s(), SIOCGRECEIVING\n", __FUNCTION__ ); + /* This is protected */ + irq->ifr_receiving = ali_ircc_is_receiving(self); + break; + default: + ret = -EOPNOTSUPP; + } + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); + + return ret; +} + +/* + * Function ali_ircc_is_receiving (self) + * + * Return TRUE is we are currently receiving a frame + * + */ +static int ali_ircc_is_receiving(struct ali_ircc_cb *self) +{ + unsigned long flags; + int status = FALSE; + int iobase; + + IRDA_DEBUG(2, "%s(), ---------------- Start -----------------\n", __FUNCTION__ ); + + IRDA_ASSERT(self != NULL, return FALSE;); + + spin_lock_irqsave(&self->lock, flags); + + if (self->io.speed > 115200) + { + iobase = self->io.fir_base; + + switch_bank(iobase, BANK1); + if((inb(iobase+FIR_FIFO_FR) & 0x3f) != 0) + { + /* We are receiving something */ + IRDA_DEBUG(1, "%s(), We are receiving something\n", __FUNCTION__ ); + status = TRUE; + } + switch_bank(iobase, BANK0); + } + else + { + status = (self->rx_buff.state != OUTSIDE_FRAME); + } + + spin_unlock_irqrestore(&self->lock, flags); + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); + + return status; +} + +static struct net_device_stats *ali_ircc_net_get_stats(struct net_device *dev) +{ + struct ali_ircc_cb *self = (struct ali_ircc_cb *) dev->priv; + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); + + return &self->stats; +} + +static void ali_ircc_suspend(struct ali_ircc_cb *self) +{ + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + IRDA_MESSAGE("%s, Suspending\n", driver_name); + + if (self->io.suspended) + return; + + ali_ircc_net_close(self->netdev); + + self->io.suspended = 1; + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); +} + +static void ali_ircc_wakeup(struct ali_ircc_cb *self) +{ + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + if (!self->io.suspended) + return; + + ali_ircc_net_open(self->netdev); + + IRDA_MESSAGE("%s, Waking up\n", driver_name); + + self->io.suspended = 0; + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); +} + +static int ali_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct ali_ircc_cb *self = (struct ali_ircc_cb*) dev->data; + + IRDA_DEBUG(2, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + if (self) { + switch (rqst) { + case PM_SUSPEND: + ali_ircc_suspend(self); + break; + case PM_RESUME: + ali_ircc_wakeup(self); + break; + } + } + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); + + return 0; +} + + +/* ALi Chip Function */ + +static void SetCOMInterrupts(struct ali_ircc_cb *self , unsigned char enable) +{ + + unsigned char newMask; + + int iobase = self->io.fir_base; /* or sir_base */ + + IRDA_DEBUG(2, "%s(), -------- Start -------- ( Enable = %d )\n", __FUNCTION__ , enable); + + /* Enable the interrupt which we wish to */ + if (enable){ + if (self->io.direction == IO_XMIT) + { + if (self->io.speed > 115200) /* FIR, MIR */ + { + newMask = self->ier; + } + else /* SIR */ + { + newMask = UART_IER_THRI | UART_IER_RDI; + } + } + else { + if (self->io.speed > 115200) /* FIR, MIR */ + { + newMask = self->ier; + } + else /* SIR */ + { + newMask = UART_IER_RDI; + } + } + } + else /* Disable all the interrupts */ + { + newMask = 0x00; + + } + + //SIR and FIR has different registers + if (self->io.speed > 115200) + { + switch_bank(iobase, BANK0); + outb(newMask, iobase+FIR_IER); + } + else + outb(newMask, iobase+UART_IER); + + IRDA_DEBUG(2, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); +} + +static void SIR2FIR(int iobase) +{ + //unsigned char tmp; + + IRDA_DEBUG(1, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + /* Already protected (change_speed() or setup()), no need to lock. + * Jean II */ + + outb(0x28, iobase+UART_MCR); + outb(0x68, iobase+UART_MCR); + outb(0x88, iobase+UART_MCR); + + outb(0x60, iobase+FIR_MCR); /* Master Reset */ + outb(0x20, iobase+FIR_MCR); /* Master Interrupt Enable */ + + //tmp = inb(iobase+FIR_LCR_B); /* SIP enable */ + //tmp |= 0x20; + //outb(tmp, iobase+FIR_LCR_B); + + IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); +} + +static void FIR2SIR(int iobase) +{ + unsigned char val; + + IRDA_DEBUG(1, "%s(), ---------------- Start ----------------\n", __FUNCTION__ ); + + /* Already protected (change_speed() or setup()), no need to lock. + * Jean II */ + + outb(0x20, iobase+FIR_MCR); /* IRQ to low */ + outb(0x00, iobase+UART_IER); + + outb(0xA0, iobase+FIR_MCR); /* Don't set master reset */ + outb(0x00, iobase+UART_FCR); + outb(0x07, iobase+UART_FCR); + + val = inb(iobase+UART_RX); + val = inb(iobase+UART_LSR); + val = inb(iobase+UART_MSR); + + IRDA_DEBUG(1, "%s(), ----------------- End ------------------\n", __FUNCTION__ ); +} + +MODULE_AUTHOR("Benjamin Kong <benjamin_kong@ali.com.tw>"); +MODULE_DESCRIPTION("ALi FIR Controller Driver"); +MODULE_LICENSE("GPL"); + + +module_param_array(io, int, NULL, 0); +MODULE_PARM_DESC(io, "Base I/O addresses"); +module_param_array(irq, int, NULL, 0); +MODULE_PARM_DESC(irq, "IRQ lines"); +module_param_array(dma, int, NULL, 0); +MODULE_PARM_DESC(dma, "DMA channels"); + +module_init(ali_ircc_init); +module_exit(ali_ircc_cleanup); diff --git a/drivers/net/irda/ali-ircc.h b/drivers/net/irda/ali-ircc.h new file mode 100644 index 000000000000..e489c6661ee8 --- /dev/null +++ b/drivers/net/irda/ali-ircc.h @@ -0,0 +1,231 @@ +/********************************************************************* + * + * Filename: ali-ircc.h + * Version: 0.5 + * Description: Driver for the ALI M1535D and M1543C FIR Controller + * Status: Experimental. + * Author: Benjamin Kong <benjamin_kong@ali.com.tw> + * Created at: 2000/10/16 03:46PM + * Modified at: 2001/1/3 02:56PM + * Modified by: Benjamin Kong <benjamin_kong@ali.com.tw> + * + * Copyright (c) 2000 Benjamin Kong <benjamin_kong@ali.com.tw> + * 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. + * + ********************************************************************/ + +#ifndef ALI_IRCC_H +#define ALI_IRCC_H + +#include <linux/time.h> + +#include <linux/spinlock.h> +#include <linux/pm.h> +#include <linux/types.h> +#include <asm/io.h> + +/* SIR Register */ +/* Usr definition of linux/serial_reg.h */ + +/* FIR Register */ +#define BANK0 0x20 +#define BANK1 0x21 +#define BANK2 0x22 +#define BANK3 0x23 + +#define FIR_MCR 0x07 /* Master Control Register */ + +/* Bank 0 */ +#define FIR_DR 0x00 /* Alias 0, FIR Data Register (R/W) */ +#define FIR_IER 0x01 /* Alias 1, FIR Interrupt Enable Register (R/W) */ +#define FIR_IIR 0x02 /* Alias 2, FIR Interrupt Identification Register (Read only) */ +#define FIR_LCR_A 0x03 /* Alias 3, FIR Line Control Register A (R/W) */ +#define FIR_LCR_B 0x04 /* Alias 4, FIR Line Control Register B (R/W) */ +#define FIR_LSR 0x05 /* Alias 5, FIR Line Status Register (R/W) */ +#define FIR_BSR 0x06 /* Alias 6, FIR Bus Status Register (Read only) */ + + + /* Alias 1 */ + #define IER_FIFO 0x10 /* FIR FIFO Interrupt Enable */ + #define IER_TIMER 0x20 /* Timer Interrupt Enable */ + #define IER_EOM 0x40 /* End of Message Interrupt Enable */ + #define IER_ACT 0x80 /* Active Frame Interrupt Enable */ + + /* Alias 2 */ + #define IIR_FIFO 0x10 /* FIR FIFO Interrupt */ + #define IIR_TIMER 0x20 /* Timer Interrupt */ + #define IIR_EOM 0x40 /* End of Message Interrupt */ + #define IIR_ACT 0x80 /* Active Frame Interrupt */ + + /* Alias 3 */ + #define LCR_A_FIFO_RESET 0x80 /* FIFO Reset */ + + /* Alias 4 */ + #define LCR_B_BW 0x10 /* Brick Wall */ + #define LCR_B_SIP 0x20 /* SIP Enable */ + #define LCR_B_TX_MODE 0x40 /* Transmit Mode */ + #define LCR_B_RX_MODE 0x80 /* Receive Mode */ + + /* Alias 5 */ + #define LSR_FIR_LSA 0x00 /* FIR Line Status Address */ + #define LSR_FRAME_ABORT 0x08 /* Frame Abort */ + #define LSR_CRC_ERROR 0x10 /* CRC Error */ + #define LSR_SIZE_ERROR 0x20 /* Size Error */ + #define LSR_FRAME_ERROR 0x40 /* Frame Error */ + #define LSR_FIFO_UR 0x80 /* FIFO Underrun */ + #define LSR_FIFO_OR 0x80 /* FIFO Overrun */ + + /* Alias 6 */ + #define BSR_FIFO_NOT_EMPTY 0x80 /* FIFO Not Empty */ + +/* Bank 1 */ +#define FIR_CR 0x00 /* Alias 0, FIR Configuration Register (R/W) */ +#define FIR_FIFO_TR 0x01 /* Alias 1, FIR FIFO Threshold Register (R/W) */ +#define FIR_DMA_TR 0x02 /* Alias 2, FIR DMA Threshold Register (R/W) */ +#define FIR_TIMER_IIR 0x03 /* Alias 3, FIR Timer interrupt interval register (W/O) */ +#define FIR_FIFO_FR 0x03 /* Alias 3, FIR FIFO Flag register (R/O) */ +#define FIR_FIFO_RAR 0x04 /* Alias 4, FIR FIFO Read Address register (R/O) */ +#define FIR_FIFO_WAR 0x05 /* Alias 5, FIR FIFO Write Address register (R/O) */ +#define FIR_TR 0x06 /* Alias 6, Test REgister (W/O) */ + + /* Alias 0 */ + #define CR_DMA_EN 0x01 /* DMA Enable */ + #define CR_DMA_BURST 0x02 /* DMA Burst Mode */ + #define CR_TIMER_EN 0x08 /* Timer Enable */ + + /* Alias 3 */ + #define TIMER_IIR_500 0x00 /* 500 us */ + #define TIMER_IIR_1ms 0x01 /* 1 ms */ + #define TIMER_IIR_2ms 0x02 /* 2 ms */ + #define TIMER_IIR_4ms 0x03 /* 4 ms */ + +/* Bank 2 */ +#define FIR_IRDA_CR 0x00 /* Alias 0, IrDA Control Register (R/W) */ +#define FIR_BOF_CR 0x01 /* Alias 1, BOF Count Register (R/W) */ +#define FIR_BW_CR 0x02 /* Alias 2, Brick Wall Count Register (R/W) */ +#define FIR_TX_DSR_HI 0x03 /* Alias 3, TX Data Size Register (high) (R/W) */ +#define FIR_TX_DSR_LO 0x04 /* Alias 4, TX Data Size Register (low) (R/W) */ +#define FIR_RX_DSR_HI 0x05 /* Alias 5, RX Data Size Register (high) (R/W) */ +#define FIR_RX_DSR_LO 0x06 /* Alias 6, RX Data Size Register (low) (R/W) */ + + /* Alias 0 */ + #define IRDA_CR_HDLC1152 0x80 /* 1.152Mbps HDLC Select */ + #define IRDA_CR_CRC 0X40 /* CRC Select. */ + #define IRDA_CR_HDLC 0x20 /* HDLC select. */ + #define IRDA_CR_HP_MODE 0x10 /* HP mode (read only) */ + #define IRDA_CR_SD_ST 0x08 /* SD/MODE State. */ + #define IRDA_CR_FIR_SIN 0x04 /* FIR SIN Select. */ + #define IRDA_CR_ITTX_0 0x02 /* SOUT State. IRTX force to 0 */ + #define IRDA_CR_ITTX_1 0x03 /* SOUT State. IRTX force to 1 */ + +/* Bank 3 */ +#define FIR_ID_VR 0x00 /* Alias 0, FIR ID Version Register (R/O) */ +#define FIR_MODULE_CR 0x01 /* Alias 1, FIR Module Control Register (R/W) */ +#define FIR_IO_BASE_HI 0x02 /* Alias 2, FIR Higher I/O Base Address Register (R/O) */ +#define FIR_IO_BASE_LO 0x03 /* Alias 3, FIR Lower I/O Base Address Register (R/O) */ +#define FIR_IRQ_CR 0x04 /* Alias 4, FIR IRQ Channel Register (R/O) */ +#define FIR_DMA_CR 0x05 /* Alias 5, FIR DMA Channel Register (R/O) */ + +struct ali_chip { + char *name; + int cfg[2]; + unsigned char entr1; + unsigned char entr2; + unsigned char cid_index; + unsigned char cid_value; + int (*probe)(struct ali_chip *chip, chipio_t *info); + int (*init)(struct ali_chip *chip, chipio_t *info); +}; +typedef struct ali_chip ali_chip_t; + + +/* DMA modes needed */ +#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */ +#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */ + +#define MAX_TX_WINDOW 7 +#define MAX_RX_WINDOW 7 + +#define TX_FIFO_Threshold 8 +#define RX_FIFO_Threshold 1 +#define TX_DMA_Threshold 1 +#define RX_DMA_Threshold 1 + +/* For storing entries in the status FIFO */ + +struct st_fifo_entry { + int status; + int len; +}; + +struct st_fifo { + struct st_fifo_entry entries[MAX_RX_WINDOW]; + int pending_bytes; + int head; + int tail; + int len; +}; + +struct frame_cb { + void *start; /* Start of frame in DMA mem */ + int len; /* Lenght of frame in DMA mem */ +}; + +struct tx_fifo { + struct frame_cb queue[MAX_TX_WINDOW]; /* Info about frames in queue */ + int ptr; /* Currently being sent */ + int len; /* Lenght of queue */ + int free; /* Next free slot */ + void *tail; /* Next free start in DMA mem */ +}; + +/* Private data for each instance */ +struct ali_ircc_cb { + + struct st_fifo st_fifo; /* Info about received frames */ + struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ + + 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 */ + struct qos_info qos; /* QoS capabilities for this device */ + + chipio_t io; /* IrDA controller information */ + iobuff_t tx_buff; /* Transmit buffer */ + iobuff_t rx_buff; /* Receive buffer */ + dma_addr_t tx_buff_dma; + dma_addr_t rx_buff_dma; + + __u8 ier; /* Interrupt enable register */ + + __u8 InterruptID; /* Interrupt ID */ + __u8 BusStatus; /* Bus Status */ + __u8 LineStatus; /* Line Status */ + + unsigned char rcvFramesOverflow; + + struct timeval stamp; + struct timeval now; + + spinlock_t lock; /* For serializing operations */ + + __u32 new_speed; + int index; /* Instance index */ + + unsigned char fifo_opti_buf; + + struct pm_dev *dev; +}; + +static inline void switch_bank(int iobase, int bank) +{ + outb(bank, iobase+FIR_MCR); +} + +#endif /* ALI_IRCC_H */ diff --git a/drivers/net/irda/au1000_ircc.h b/drivers/net/irda/au1000_ircc.h new file mode 100644 index 000000000000..7a31d4659ed6 --- /dev/null +++ b/drivers/net/irda/au1000_ircc.h @@ -0,0 +1,127 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * Au1000 IrDA driver. + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef AU1000_IRCC_H +#define AU1000_IRCC_H + +#include <linux/time.h> + +#include <linux/spinlock.h> +#include <linux/pm.h> +#include <asm/io.h> + +#define NUM_IR_IFF 1 +#define NUM_IR_DESC 64 +#define RING_SIZE_4 0x0 +#define RING_SIZE_16 0x3 +#define RING_SIZE_64 0xF +#define MAX_NUM_IR_DESC 64 +#define MAX_BUF_SIZE 2048 + +#define BPS_115200 0 +#define BPS_57600 1 +#define BPS_38400 2 +#define BPS_19200 5 +#define BPS_9600 11 +#define BPS_2400 47 + +/* Ring descriptor flags */ +#define AU_OWN (1<<7) /* tx,rx */ + +#define IR_DIS_CRC (1<<6) /* tx */ +#define IR_BAD_CRC (1<<5) /* tx */ +#define IR_NEED_PULSE (1<<4) /* tx */ +#define IR_FORCE_UNDER (1<<3) /* tx */ +#define IR_DISABLE_TX (1<<2) /* tx */ +#define IR_HW_UNDER (1<<0) /* tx */ +#define IR_TX_ERROR (IR_DIS_CRC|IR_BAD_CRC|IR_HW_UNDER) + +#define IR_PHY_ERROR (1<<6) /* rx */ +#define IR_CRC_ERROR (1<<5) /* rx */ +#define IR_MAX_LEN (1<<4) /* rx */ +#define IR_FIFO_OVER (1<<3) /* rx */ +#define IR_SIR_ERROR (1<<2) /* rx */ +#define IR_RX_ERROR (IR_PHY_ERROR|IR_CRC_ERROR| \ + IR_MAX_LEN|IR_FIFO_OVER|IR_SIR_ERROR) + +typedef struct db_dest { + struct db_dest *pnext; + volatile u32 *vaddr; + dma_addr_t dma_addr; +} db_dest_t; + + +typedef struct ring_desc { + u8 count_0; /* 7:0 */ + u8 count_1; /* 12:8 */ + u8 reserved; + u8 flags; + u8 addr_0; /* 7:0 */ + u8 addr_1; /* 15:8 */ + u8 addr_2; /* 23:16 */ + u8 addr_3; /* 31:24 */ +} ring_dest_t; + + +/* Private data for each instance */ +struct au1k_private { + + db_dest_t *pDBfree; + db_dest_t db[2*NUM_IR_DESC]; + volatile ring_dest_t *rx_ring[NUM_IR_DESC]; + volatile ring_dest_t *tx_ring[NUM_IR_DESC]; + db_dest_t *rx_db_inuse[NUM_IR_DESC]; + db_dest_t *tx_db_inuse[NUM_IR_DESC]; + u32 rx_head; + u32 tx_head; + u32 tx_tail; + u32 tx_full; + + iobuff_t rx_buff; + + struct net_device *netdev; + struct net_device_stats stats; + + struct timeval stamp; + struct timeval now; + struct qos_info qos; + struct irlap_cb *irlap; + + u8 open; + u32 speed; + u32 newspeed; + + u32 intr_work_done; /* number of Rx and Tx pkts processed in the isr */ + struct timer_list timer; + + spinlock_t lock; /* For serializing operations */ + struct pm_dev *dev; +}; +#endif /* AU1000_IRCC_H */ diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c new file mode 100644 index 000000000000..e6b1985767c2 --- /dev/null +++ b/drivers/net/irda/au1k_ir.c @@ -0,0 +1,851 @@ +/* + * Alchemy Semi Au1000 IrDA driver + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ppopov@mvista.com or source@mvista.com + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/slab.h> +#include <linux/rtnetlink.h> +#include <linux/interrupt.h> +#include <linux/pm.h> +#include <linux/bitops.h> + +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/au1000.h> +#if defined(CONFIG_MIPS_PB1000) || defined(CONFIG_MIPS_PB1100) +#include <asm/pb1000.h> +#elif defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) +#include <asm/db1x00.h> +#else +#error au1k_ir: unsupported board +#endif + +#include <net/irda/irda.h> +#include <net/irda/irmod.h> +#include <net/irda/wrapper.h> +#include <net/irda/irda_device.h> +#include "au1000_ircc.h" + +static int au1k_irda_net_init(struct net_device *); +static int au1k_irda_start(struct net_device *); +static int au1k_irda_stop(struct net_device *dev); +static int au1k_irda_hard_xmit(struct sk_buff *, struct net_device *); +static int au1k_irda_rx(struct net_device *); +static void au1k_irda_interrupt(int, void *, struct pt_regs *); +static void au1k_tx_timeout(struct net_device *); +static struct net_device_stats *au1k_irda_stats(struct net_device *); +static int au1k_irda_ioctl(struct net_device *, struct ifreq *, int); +static int au1k_irda_set_speed(struct net_device *dev, int speed); + +static void *dma_alloc(size_t, dma_addr_t *); +static void dma_free(void *, size_t); + +static int qos_mtt_bits = 0x07; /* 1 ms or more */ +static struct net_device *ir_devs[NUM_IR_IFF]; +static char version[] __devinitdata = + "au1k_ircc:1.2 ppopov@mvista.com\n"; + +#define RUN_AT(x) (jiffies + (x)) + +#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) +static BCSR * const bcsr = (BCSR *)0xAE000000; +#endif + +static DEFINE_SPINLOCK(ir_lock); + +/* + * IrDA peripheral bug. You have to read the register + * twice to get the right value. + */ +u32 read_ir_reg(u32 addr) +{ + readl(addr); + return readl(addr); +} + + +/* + * Buffer allocation/deallocation routines. The buffer descriptor returned + * has the virtual and dma address of a buffer suitable for + * both, receive and transmit operations. + */ +static db_dest_t *GetFreeDB(struct au1k_private *aup) +{ + db_dest_t *pDB; + pDB = aup->pDBfree; + + if (pDB) { + aup->pDBfree = pDB->pnext; + } + return pDB; +} + +static void ReleaseDB(struct au1k_private *aup, db_dest_t *pDB) +{ + db_dest_t *pDBfree = aup->pDBfree; + if (pDBfree) + pDBfree->pnext = pDB; + aup->pDBfree = pDB; +} + + +/* + DMA memory allocation, derived from pci_alloc_consistent. + However, the Au1000 data cache is coherent (when programmed + so), therefore we return KSEG0 address, not KSEG1. +*/ +static void *dma_alloc(size_t size, dma_addr_t * dma_handle) +{ + void *ret; + int gfp = GFP_ATOMIC | GFP_DMA; + + ret = (void *) __get_free_pages(gfp, get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_bus(ret); + ret = (void *)KSEG0ADDR(ret); + } + return ret; +} + + +static void dma_free(void *vaddr, size_t size) +{ + vaddr = (void *)KSEG0ADDR(vaddr); + free_pages((unsigned long) vaddr, get_order(size)); +} + + +static void +setup_hw_rings(struct au1k_private *aup, u32 rx_base, u32 tx_base) +{ + int i; + for (i=0; i<NUM_IR_DESC; i++) { + aup->rx_ring[i] = (volatile ring_dest_t *) + (rx_base + sizeof(ring_dest_t)*i); + } + for (i=0; i<NUM_IR_DESC; i++) { + aup->tx_ring[i] = (volatile ring_dest_t *) + (tx_base + sizeof(ring_dest_t)*i); + } +} + +static int au1k_irda_init(void) +{ + static unsigned version_printed = 0; + struct au1k_private *aup; + struct net_device *dev; + int err; + + if (version_printed++ == 0) printk(version); + + dev = alloc_irdadev(sizeof(struct au1k_private)); + if (!dev) + return -ENOMEM; + + dev->irq = AU1000_IRDA_RX_INT; /* TX has its own interrupt */ + err = au1k_irda_net_init(dev); + if (err) + goto out; + err = register_netdev(dev); + if (err) + goto out1; + ir_devs[0] = dev; + printk(KERN_INFO "IrDA: Registered device %s\n", dev->name); + return 0; + +out1: + aup = netdev_priv(dev); + dma_free((void *)aup->db[0].vaddr, + MAX_BUF_SIZE * 2*NUM_IR_DESC); + dma_free((void *)aup->rx_ring[0], + 2 * MAX_NUM_IR_DESC*(sizeof(ring_dest_t))); + kfree(aup->rx_buff.head); +out: + free_netdev(dev); + return err; +} + +static int au1k_irda_init_iobuf(iobuff_t *io, int size) +{ + io->head = kmalloc(size, GFP_KERNEL); + if (io->head != NULL) { + io->truesize = size; + io->in_frame = FALSE; + io->state = OUTSIDE_FRAME; + io->data = io->head; + } + return io->head ? 0 : -ENOMEM; +} + +static int au1k_irda_net_init(struct net_device *dev) +{ + struct au1k_private *aup = netdev_priv(dev); + int i, retval = 0, err; + db_dest_t *pDB, *pDBfree; + dma_addr_t temp; + + err = au1k_irda_init_iobuf(&aup->rx_buff, 14384); + if (err) + goto out1; + + dev->open = au1k_irda_start; + dev->hard_start_xmit = au1k_irda_hard_xmit; + dev->stop = au1k_irda_stop; + dev->get_stats = au1k_irda_stats; + dev->do_ioctl = au1k_irda_ioctl; + dev->tx_timeout = au1k_tx_timeout; + + irda_init_max_qos_capabilies(&aup->qos); + + /* The only value we must override it the baudrate */ + aup->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| + IR_115200|IR_576000 |(IR_4000000 << 8); + + aup->qos.min_turn_time.bits = qos_mtt_bits; + irda_qos_bits_to_value(&aup->qos); + + retval = -ENOMEM; + + /* Tx ring follows rx ring + 512 bytes */ + /* we need a 1k aligned buffer */ + aup->rx_ring[0] = (ring_dest_t *) + dma_alloc(2*MAX_NUM_IR_DESC*(sizeof(ring_dest_t)), &temp); + if (!aup->rx_ring[0]) + goto out2; + + /* allocate the data buffers */ + aup->db[0].vaddr = + (void *)dma_alloc(MAX_BUF_SIZE * 2*NUM_IR_DESC, &temp); + if (!aup->db[0].vaddr) + goto out3; + + setup_hw_rings(aup, (u32)aup->rx_ring[0], (u32)aup->rx_ring[0] + 512); + + pDBfree = NULL; + pDB = aup->db; + for (i=0; i<(2*NUM_IR_DESC); i++) { + pDB->pnext = pDBfree; + pDBfree = pDB; + pDB->vaddr = + (u32 *)((unsigned)aup->db[0].vaddr + MAX_BUF_SIZE*i); + pDB->dma_addr = (dma_addr_t)virt_to_bus(pDB->vaddr); + pDB++; + } + aup->pDBfree = pDBfree; + + /* attach a data buffer to each descriptor */ + for (i=0; i<NUM_IR_DESC; i++) { + pDB = GetFreeDB(aup); + if (!pDB) goto out; + aup->rx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff); + aup->rx_ring[i]->addr_1 = (u8)((pDB->dma_addr>>8) & 0xff); + aup->rx_ring[i]->addr_2 = (u8)((pDB->dma_addr>>16) & 0xff); + aup->rx_ring[i]->addr_3 = (u8)((pDB->dma_addr>>24) & 0xff); + aup->rx_db_inuse[i] = pDB; + } + for (i=0; i<NUM_IR_DESC; i++) { + pDB = GetFreeDB(aup); + if (!pDB) goto out; + aup->tx_ring[i]->addr_0 = (u8)(pDB->dma_addr & 0xff); + aup->tx_ring[i]->addr_1 = (u8)((pDB->dma_addr>>8) & 0xff); + aup->tx_ring[i]->addr_2 = (u8)((pDB->dma_addr>>16) & 0xff); + aup->tx_ring[i]->addr_3 = (u8)((pDB->dma_addr>>24) & 0xff); + aup->tx_ring[i]->count_0 = 0; + aup->tx_ring[i]->count_1 = 0; + aup->tx_ring[i]->flags = 0; + aup->tx_db_inuse[i] = pDB; + } + +#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) + /* power on */ + bcsr->resets &= ~BCSR_RESETS_IRDA_MODE_MASK; + bcsr->resets |= BCSR_RESETS_IRDA_MODE_FULL; + au_sync(); +#endif + + return 0; + +out3: + dma_free((void *)aup->rx_ring[0], + 2 * MAX_NUM_IR_DESC*(sizeof(ring_dest_t))); +out2: + kfree(aup->rx_buff.head); +out1: + printk(KERN_ERR "au1k_init_module failed. Returns %d\n", retval); + return retval; +} + + +static int au1k_init(struct net_device *dev) +{ + struct au1k_private *aup = netdev_priv(dev); + int i; + u32 control; + u32 ring_address; + + /* bring the device out of reset */ + control = 0xe; /* coherent, clock enable, one half system clock */ + +#ifndef CONFIG_CPU_LITTLE_ENDIAN + control |= 1; +#endif + aup->tx_head = 0; + aup->tx_tail = 0; + aup->rx_head = 0; + + for (i=0; i<NUM_IR_DESC; i++) { + aup->rx_ring[i]->flags = AU_OWN; + } + + writel(control, IR_INTERFACE_CONFIG); + au_sync_delay(10); + + writel(read_ir_reg(IR_ENABLE) & ~0x8000, IR_ENABLE); /* disable PHY */ + au_sync_delay(1); + + writel(MAX_BUF_SIZE, IR_MAX_PKT_LEN); + + ring_address = (u32)virt_to_phys((void *)aup->rx_ring[0]); + writel(ring_address >> 26, IR_RING_BASE_ADDR_H); + writel((ring_address >> 10) & 0xffff, IR_RING_BASE_ADDR_L); + + writel(RING_SIZE_64<<8 | RING_SIZE_64<<12, IR_RING_SIZE); + + writel(1<<2 | IR_ONE_PIN, IR_CONFIG_2); /* 48MHz */ + writel(0, IR_RING_ADDR_CMPR); + + au1k_irda_set_speed(dev, 9600); + return 0; +} + +static int au1k_irda_start(struct net_device *dev) +{ + int retval; + char hwname[32]; + struct au1k_private *aup = netdev_priv(dev); + + if ((retval = au1k_init(dev))) { + printk(KERN_ERR "%s: error in au1k_init\n", dev->name); + return retval; + } + + if ((retval = request_irq(AU1000_IRDA_TX_INT, &au1k_irda_interrupt, + 0, dev->name, dev))) { + printk(KERN_ERR "%s: unable to get IRQ %d\n", + dev->name, dev->irq); + return retval; + } + if ((retval = request_irq(AU1000_IRDA_RX_INT, &au1k_irda_interrupt, + 0, dev->name, dev))) { + free_irq(AU1000_IRDA_TX_INT, dev); + printk(KERN_ERR "%s: unable to get IRQ %d\n", + dev->name, dev->irq); + return retval; + } + + /* Give self a hardware name */ + sprintf(hwname, "Au1000 SIR/FIR"); + aup->irlap = irlap_open(dev, &aup->qos, hwname); + netif_start_queue(dev); + + writel(read_ir_reg(IR_CONFIG_2) | 1<<8, IR_CONFIG_2); /* int enable */ + + aup->timer.expires = RUN_AT((3*HZ)); + aup->timer.data = (unsigned long)dev; + return 0; +} + +static int au1k_irda_stop(struct net_device *dev) +{ + struct au1k_private *aup = netdev_priv(dev); + + /* disable interrupts */ + writel(read_ir_reg(IR_CONFIG_2) & ~(1<<8), IR_CONFIG_2); + writel(0, IR_CONFIG_1); + writel(0, IR_INTERFACE_CONFIG); /* disable clock */ + au_sync(); + + if (aup->irlap) { + irlap_close(aup->irlap); + aup->irlap = NULL; + } + + netif_stop_queue(dev); + del_timer(&aup->timer); + + /* disable the interrupt */ + free_irq(AU1000_IRDA_TX_INT, dev); + free_irq(AU1000_IRDA_RX_INT, dev); + return 0; +} + +static void __exit au1k_irda_exit(void) +{ + struct net_device *dev = ir_devs[0]; + struct au1k_private *aup = netdev_priv(dev); + + unregister_netdev(dev); + + dma_free((void *)aup->db[0].vaddr, + MAX_BUF_SIZE * 2*NUM_IR_DESC); + dma_free((void *)aup->rx_ring[0], + 2 * MAX_NUM_IR_DESC*(sizeof(ring_dest_t))); + kfree(aup->rx_buff.head); + free_netdev(dev); +} + + +static inline void +update_tx_stats(struct net_device *dev, u32 status, u32 pkt_len) +{ + struct au1k_private *aup = netdev_priv(dev); + struct net_device_stats *ps = &aup->stats; + + ps->tx_packets++; + ps->tx_bytes += pkt_len; + + if (status & IR_TX_ERROR) { + ps->tx_errors++; + ps->tx_aborted_errors++; + } +} + + +static void au1k_tx_ack(struct net_device *dev) +{ + struct au1k_private *aup = netdev_priv(dev); + volatile ring_dest_t *ptxd; + + ptxd = aup->tx_ring[aup->tx_tail]; + while (!(ptxd->flags & AU_OWN) && (aup->tx_tail != aup->tx_head)) { + update_tx_stats(dev, ptxd->flags, + ptxd->count_1<<8 | ptxd->count_0); + ptxd->count_0 = 0; + ptxd->count_1 = 0; + au_sync(); + + aup->tx_tail = (aup->tx_tail + 1) & (NUM_IR_DESC - 1); + ptxd = aup->tx_ring[aup->tx_tail]; + + if (aup->tx_full) { + aup->tx_full = 0; + netif_wake_queue(dev); + } + } + + if (aup->tx_tail == aup->tx_head) { + if (aup->newspeed) { + au1k_irda_set_speed(dev, aup->newspeed); + aup->newspeed = 0; + } + else { + writel(read_ir_reg(IR_CONFIG_1) & ~IR_TX_ENABLE, + IR_CONFIG_1); + au_sync(); + writel(read_ir_reg(IR_CONFIG_1) | IR_RX_ENABLE, + IR_CONFIG_1); + writel(0, IR_RING_PROMPT); + au_sync(); + } + } +} + + +/* + * Au1000 transmit routine. + */ +static int au1k_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct au1k_private *aup = netdev_priv(dev); + int speed = irda_get_next_speed(skb); + volatile ring_dest_t *ptxd; + u32 len; + + u32 flags; + db_dest_t *pDB; + + if (speed != aup->speed && speed != -1) { + aup->newspeed = speed; + } + + if ((skb->len == 0) && (aup->newspeed)) { + if (aup->tx_tail == aup->tx_head) { + au1k_irda_set_speed(dev, speed); + aup->newspeed = 0; + } + dev_kfree_skb(skb); + return 0; + } + + ptxd = aup->tx_ring[aup->tx_head]; + flags = ptxd->flags; + + if (flags & AU_OWN) { + printk(KERN_DEBUG "%s: tx_full\n", dev->name); + netif_stop_queue(dev); + aup->tx_full = 1; + return 1; + } + else if (((aup->tx_head + 1) & (NUM_IR_DESC - 1)) == aup->tx_tail) { + printk(KERN_DEBUG "%s: tx_full\n", dev->name); + netif_stop_queue(dev); + aup->tx_full = 1; + return 1; + } + + pDB = aup->tx_db_inuse[aup->tx_head]; + +#if 0 + if (read_ir_reg(IR_RX_BYTE_CNT) != 0) { + printk("tx warning: rx byte cnt %x\n", + read_ir_reg(IR_RX_BYTE_CNT)); + } +#endif + + if (aup->speed == 4000000) { + /* FIR */ + memcpy((void *)pDB->vaddr, skb->data, skb->len); + ptxd->count_0 = skb->len & 0xff; + ptxd->count_1 = (skb->len >> 8) & 0xff; + + } + else { + /* SIR */ + len = async_wrap_skb(skb, (u8 *)pDB->vaddr, MAX_BUF_SIZE); + ptxd->count_0 = len & 0xff; + ptxd->count_1 = (len >> 8) & 0xff; + ptxd->flags |= IR_DIS_CRC; + au_writel(au_readl(0xae00000c) & ~(1<<13), 0xae00000c); + } + ptxd->flags |= AU_OWN; + au_sync(); + + writel(read_ir_reg(IR_CONFIG_1) | IR_TX_ENABLE, IR_CONFIG_1); + writel(0, IR_RING_PROMPT); + au_sync(); + + dev_kfree_skb(skb); + aup->tx_head = (aup->tx_head + 1) & (NUM_IR_DESC - 1); + dev->trans_start = jiffies; + return 0; +} + + +static inline void +update_rx_stats(struct net_device *dev, u32 status, u32 count) +{ + struct au1k_private *aup = netdev_priv(dev); + struct net_device_stats *ps = &aup->stats; + + ps->rx_packets++; + + if (status & IR_RX_ERROR) { + ps->rx_errors++; + if (status & (IR_PHY_ERROR|IR_FIFO_OVER)) + ps->rx_missed_errors++; + if (status & IR_MAX_LEN) + ps->rx_length_errors++; + if (status & IR_CRC_ERROR) + ps->rx_crc_errors++; + } + else + ps->rx_bytes += count; +} + +/* + * Au1000 receive routine. + */ +static int au1k_irda_rx(struct net_device *dev) +{ + struct au1k_private *aup = netdev_priv(dev); + struct sk_buff *skb; + volatile ring_dest_t *prxd; + u32 flags, count; + db_dest_t *pDB; + + prxd = aup->rx_ring[aup->rx_head]; + flags = prxd->flags; + + while (!(flags & AU_OWN)) { + pDB = aup->rx_db_inuse[aup->rx_head]; + count = prxd->count_1<<8 | prxd->count_0; + if (!(flags & IR_RX_ERROR)) { + /* good frame */ + update_rx_stats(dev, flags, count); + skb=alloc_skb(count+1,GFP_ATOMIC); + if (skb == NULL) { + aup->stats.rx_dropped++; + continue; + } + skb_reserve(skb, 1); + if (aup->speed == 4000000) + skb_put(skb, count); + else + skb_put(skb, count-2); + memcpy(skb->data, (void *)pDB->vaddr, count-2); + skb->dev = dev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + prxd->count_0 = 0; + prxd->count_1 = 0; + } + prxd->flags |= AU_OWN; + aup->rx_head = (aup->rx_head + 1) & (NUM_IR_DESC - 1); + writel(0, IR_RING_PROMPT); + au_sync(); + + /* next descriptor */ + prxd = aup->rx_ring[aup->rx_head]; + flags = prxd->flags; + dev->last_rx = jiffies; + + } + return 0; +} + + +void au1k_irda_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + + if (dev == NULL) { + printk(KERN_ERR "%s: isr: null dev ptr\n", dev->name); + return; + } + + writel(0, IR_INT_CLEAR); /* ack irda interrupts */ + + au1k_irda_rx(dev); + au1k_tx_ack(dev); +} + + +/* + * The Tx ring has been full longer than the watchdog timeout + * value. The transmitter must be hung? + */ +static void au1k_tx_timeout(struct net_device *dev) +{ + u32 speed; + struct au1k_private *aup = netdev_priv(dev); + + printk(KERN_ERR "%s: tx timeout\n", dev->name); + speed = aup->speed; + aup->speed = 0; + au1k_irda_set_speed(dev, speed); + aup->tx_full = 0; + netif_wake_queue(dev); +} + + +/* + * Set the IrDA communications speed. + */ +static int +au1k_irda_set_speed(struct net_device *dev, int speed) +{ + unsigned long flags; + struct au1k_private *aup = netdev_priv(dev); + u32 control; + int ret = 0, timeout = 10, i; + volatile ring_dest_t *ptxd; +#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) + unsigned long irda_resets; +#endif + + if (speed == aup->speed) + return ret; + + spin_lock_irqsave(&ir_lock, flags); + + /* disable PHY first */ + writel(read_ir_reg(IR_ENABLE) & ~0x8000, IR_ENABLE); + + /* disable RX/TX */ + writel(read_ir_reg(IR_CONFIG_1) & ~(IR_RX_ENABLE|IR_TX_ENABLE), + IR_CONFIG_1); + au_sync_delay(1); + while (read_ir_reg(IR_ENABLE) & (IR_RX_STATUS | IR_TX_STATUS)) { + mdelay(1); + if (!timeout--) { + printk(KERN_ERR "%s: rx/tx disable timeout\n", + dev->name); + break; + } + } + + /* disable DMA */ + writel(read_ir_reg(IR_CONFIG_1) & ~IR_DMA_ENABLE, IR_CONFIG_1); + au_sync_delay(1); + + /* + * After we disable tx/rx. the index pointers + * go back to zero. + */ + aup->tx_head = aup->tx_tail = aup->rx_head = 0; + for (i=0; i<NUM_IR_DESC; i++) { + ptxd = aup->tx_ring[i]; + ptxd->flags = 0; + ptxd->count_0 = 0; + ptxd->count_1 = 0; + } + + for (i=0; i<NUM_IR_DESC; i++) { + ptxd = aup->rx_ring[i]; + ptxd->count_0 = 0; + ptxd->count_1 = 0; + ptxd->flags = AU_OWN; + } + + if (speed == 4000000) { +#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) + bcsr->resets |= BCSR_RESETS_FIR_SEL; +#else /* Pb1000 and Pb1100 */ + writel(1<<13, CPLD_AUX1); +#endif + } + else { +#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) + bcsr->resets &= ~BCSR_RESETS_FIR_SEL; +#else /* Pb1000 and Pb1100 */ + writel(readl(CPLD_AUX1) & ~(1<<13), CPLD_AUX1); +#endif + } + + switch (speed) { + case 9600: + writel(11<<10 | 12<<5, IR_WRITE_PHY_CONFIG); + writel(IR_SIR_MODE, IR_CONFIG_1); + break; + case 19200: + writel(5<<10 | 12<<5, IR_WRITE_PHY_CONFIG); + writel(IR_SIR_MODE, IR_CONFIG_1); + break; + case 38400: + writel(2<<10 | 12<<5, IR_WRITE_PHY_CONFIG); + writel(IR_SIR_MODE, IR_CONFIG_1); + break; + case 57600: + writel(1<<10 | 12<<5, IR_WRITE_PHY_CONFIG); + writel(IR_SIR_MODE, IR_CONFIG_1); + break; + case 115200: + writel(12<<5, IR_WRITE_PHY_CONFIG); + writel(IR_SIR_MODE, IR_CONFIG_1); + break; + case 4000000: + writel(0xF, IR_WRITE_PHY_CONFIG); + writel(IR_FIR|IR_DMA_ENABLE|IR_RX_ENABLE, IR_CONFIG_1); + break; + default: + printk(KERN_ERR "%s unsupported speed %x\n", dev->name, speed); + ret = -EINVAL; + break; + } + + aup->speed = speed; + writel(read_ir_reg(IR_ENABLE) | 0x8000, IR_ENABLE); + au_sync(); + + control = read_ir_reg(IR_ENABLE); + writel(0, IR_RING_PROMPT); + au_sync(); + + if (control & (1<<14)) { + printk(KERN_ERR "%s: configuration error\n", dev->name); + } + else { + if (control & (1<<11)) + printk(KERN_DEBUG "%s Valid SIR config\n", dev->name); + if (control & (1<<12)) + printk(KERN_DEBUG "%s Valid MIR config\n", dev->name); + if (control & (1<<13)) + printk(KERN_DEBUG "%s Valid FIR config\n", dev->name); + if (control & (1<<10)) + printk(KERN_DEBUG "%s TX enabled\n", dev->name); + if (control & (1<<9)) + printk(KERN_DEBUG "%s RX enabled\n", dev->name); + } + + spin_unlock_irqrestore(&ir_lock, flags); + return ret; +} + +static int +au1k_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) +{ + struct if_irda_req *rq = (struct if_irda_req *)ifreq; + struct au1k_private *aup = netdev_priv(dev); + int ret = -EOPNOTSUPP; + + switch (cmd) { + case SIOCSBANDWIDTH: + if (capable(CAP_NET_ADMIN)) { + /* + * We are unable to set the speed if the + * device is not running. + */ + if (aup->open) + ret = au1k_irda_set_speed(dev, + rq->ifr_baudrate); + else { + printk(KERN_ERR "%s ioctl: !netif_running\n", + dev->name); + ret = 0; + } + } + break; + + case SIOCSMEDIABUSY: + ret = -EPERM; + if (capable(CAP_NET_ADMIN)) { + irda_device_set_media_busy(dev, TRUE); + ret = 0; + } + break; + + case SIOCGRECEIVING: + rq->ifr_receiving = 0; + break; + default: + break; + } + return ret; +} + + +static struct net_device_stats *au1k_irda_stats(struct net_device *dev) +{ + struct au1k_private *aup = netdev_priv(dev); + return &aup->stats; +} + +MODULE_AUTHOR("Pete Popov <ppopov@mvista.com>"); +MODULE_DESCRIPTION("Au1000 IrDA Device Driver"); + +module_init(au1k_irda_init); +module_exit(au1k_irda_exit); diff --git a/drivers/net/irda/donauboe.c b/drivers/net/irda/donauboe.c new file mode 100644 index 000000000000..0a08c539c051 --- /dev/null +++ b/drivers/net/irda/donauboe.c @@ -0,0 +1,1789 @@ +/***************************************************************** + * + * Filename: donauboe.c + * Version: 2.17 + * Description: Driver for the Toshiba OBOE (or type-O or 701) + * FIR Chipset, also supports the DONAUOBOE (type-DO + * or d01) FIR chipset which as far as I know is + * register compatible. + * Documentation: http://libxg.free.fr/irda/lib-irda.html + * Status: Experimental. + * Author: James McKenzie <james@fishsoup.dhs.org> + * Created at: Sat May 8 12:35:27 1999 + * Modified: Paul Bristow <paul.bristow@technologist.com> + * Modified: Mon Nov 11 19:10:05 1999 + * Modified: James McKenzie <james@fishsoup.dhs.org> + * Modified: Thu Mar 16 12:49:00 2000 (Substantial rewrite) + * Modified: Sat Apr 29 00:23:03 2000 (Added DONAUOBOE support) + * Modified: Wed May 24 23:45:02 2000 (Fixed chipio_t structure) + * Modified: 2.13 Christian Gennerat <christian.gennerat@polytechnique.org> + * Modified: 2.13 dim jan 07 21:57:39 2001 (tested with kernel 2.4 & irnet/ppp) + * Modified: 2.14 Christian Gennerat <christian.gennerat@polytechnique.org> + * Modified: 2.14 lun fev 05 17:55:59 2001 (adapted to patch-2.4.1-pre8-irda1) + * Modified: 2.15 Martin Lucina <mato@kotelna.sk> + * Modified: 2.15 Fri Jun 21 20:40:59 2002 (sync with 2.4.18, substantial fixes) + * Modified: 2.16 Martin Lucina <mato@kotelna.sk> + * Modified: 2.16 Sat Jun 22 18:54:29 2002 (fix freeregion, default to verbose) + * Modified: 2.17 Christian Gennerat <christian.gennerat@polytechnique.org> + * Modified: 2.17 jeu sep 12 08:50:20 2002 (save_flags();cli(); replaced by spinlocks) + * Modified: 2.18 Christian Gennerat <christian.gennerat@polytechnique.org> + * Modified: 2.18 ven jan 10 03:14:16 2003 Change probe default options + * + * Copyright (c) 1999 James McKenzie, 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 James McKenzie nor Cambridge University admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + * Applicable Models : Libretto 100/110CT and many more. + * Toshiba refers to this chip as the type-O IR port, + * or the type-DO IR port. + * + ********************************************************************/ + +/* Look at toshoboe.h (currently in include/net/irda) for details of */ +/* Where to get documentation on the chip */ + + +static char *rcsid = + "$Id: donauboe.c V2.18 ven jan 10 03:14:16 2003$"; + +/* See below for a description of the logic in this driver */ + +/* User servicable parts */ +/* USE_PROBE Create the code which probes the chip and does a few tests */ +/* do_probe module parameter Enable this code */ +/* Probe code is very useful for understanding how the hardware works */ +/* Use it with various combinations of TT_LEN, RX_LEN */ +/* Strongly recomended, disable if the probe fails on your machine */ +/* and send me <james@fishsoup.dhs.org> the output of dmesg */ +#define USE_PROBE 1 +#undef USE_PROBE + +/* Trace Transmit ring, interrupts, Receive ring or not ? */ +#define PROBE_VERBOSE 1 + +/* Debug option, examine sent and received raw data */ +/* Irdadump is better, but does not see all packets. enable it if you want. */ +#undef DUMP_PACKETS + +/* MIR mode has not been tested. Some behaviour is different */ +/* Seems to work against an Ericsson R520 for me. -Martin */ +#define USE_MIR + +/* Schedule back to back hardware transmits wherever possible, otherwise */ +/* we need an interrupt for every frame, unset if oboe works for a bit and */ +/* then hangs */ +#define OPTIMIZE_TX + +/* Set the number of slots in the rings */ +/* If you get rx/tx fifo overflows at high bitrates, you can try increasing */ +/* these */ + +#define RING_SIZE (OBOE_RING_SIZE_RX8 | OBOE_RING_SIZE_TX8) +#define TX_SLOTS 8 +#define RX_SLOTS 8 + + +/* Less user servicable parts below here */ + +/* Test, Transmit and receive buffer sizes, adjust at your peril */ +/* remarks: nfs usually needs 1k blocks */ +/* remarks: in SIR mode, CRC is received, -> RX_LEN=TX_LEN+2 */ +/* remarks: test accepts large blocks. Standard is 0x80 */ +/* When TT_LEN > RX_LEN (SIR mode) data is stored in successive slots. */ +/* When 3 or more slots are needed for each test packet, */ +/* data received in the first slots is overwritten, even */ +/* if OBOE_CTL_RX_HW_OWNS is not set, without any error! */ +#define TT_LEN 0x80 +#define TX_LEN 0xc00 +#define RX_LEN 0xc04 +/* Real transmitted length (SIR mode) is about 14+(2%*TX_LEN) more */ +/* long than user-defined length (see async_wrap_skb) and is less then 4K */ +/* Real received length is (max RX_LEN) differs from user-defined */ +/* length only b the CRC (2 or 4 bytes) */ +#define BUF_SAFETY 0x7a +#define RX_BUF_SZ (RX_LEN) +#define TX_BUF_SZ (TX_LEN+BUF_SAFETY) + + +/* Logic of the netdev part of this driver */ + +/* The RX ring is filled with buffers, when a packet arrives */ +/* it is DMA'd into the buffer which is marked used and RxDone called */ +/* RxDone forms an skb (and checks the CRC if in SIR mode) and ships */ +/* the packet off upstairs */ + +/* The transmitter on the oboe chip can work in one of two modes */ +/* for each ring->tx[] the transmitter can either */ +/* a) transmit the packet, leave the trasmitter enabled and proceed to */ +/* the next ring */ +/* OR */ +/* b) transmit the packet, switch off the transmitter and issue TxDone */ + +/* All packets are entered into the ring in mode b), if the ring was */ +/* empty the transmitter is started. */ + +/* If OPTIMIZE_TX is defined then in TxDone if the ring contains */ +/* more than one packet, all but the last are set to mode a) [HOWEVER */ +/* the hardware may not notice this, this is why we start in mode b) ] */ +/* then restart the transmitter */ + +/* If OPTIMIZE_TX is not defined then we just restart the transmitter */ +/* if the ring isn't empty */ + +/* Speed changes are delayed until the TxRing is empty */ +/* mtt is handled by generating packets with bad CRCs, before the data */ + +/* TODO: */ +/* check the mtt works ok */ +/* finish the watchdog */ + +/* No user servicable parts below here */ + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/rtnetlink.h> + +#include <asm/system.h> +#include <asm/io.h> + +#include <net/irda/wrapper.h> +#include <net/irda/irda.h> +//#include <net/irda/irmod.h> +//#include <net/irda/irlap_frame.h> +#include <net/irda/irda_device.h> +#include <net/irda/crc.h> + +#include "donauboe.h" + +#define INB(port) inb_p(port) +#define OUTB(val,port) outb_p(val,port) +#define OUTBP(val,port) outb_p(val,port) + +#define PROMPT OUTB(OBOE_PROMPT_BIT,OBOE_PROMPT); + +#if PROBE_VERBOSE +#define PROBE_DEBUG(args...) (printk (args)) +#else +#define PROBE_DEBUG(args...) ; +#endif + +/* Set the DMA to be byte at a time */ +#define CONFIG0H_DMA_OFF OBOE_CONFIG0H_RCVANY +#define CONFIG0H_DMA_ON_NORX CONFIG0H_DMA_OFF| OBOE_CONFIG0H_ENDMAC +#define CONFIG0H_DMA_ON CONFIG0H_DMA_ON_NORX | OBOE_CONFIG0H_ENRX + +static struct pci_device_id toshoboe_pci_tbl[] = { + { PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIR701, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIRD01, PCI_ANY_ID, PCI_ANY_ID, }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, toshoboe_pci_tbl); + +#define DRIVER_NAME "toshoboe" +static char *driver_name = DRIVER_NAME; + +static int max_baud = 4000000; +#ifdef USE_PROBE +static int do_probe = 0; +#endif + + +/**********************************************************************/ +static int +toshoboe_checkfcs (unsigned char *buf, int len) +{ + int i; + union + { + __u16 value; + __u8 bytes[2]; + } + fcs; + + fcs.value = INIT_FCS; + + for (i = 0; i < len; ++i) + fcs.value = irda_fcs (fcs.value, *(buf++)); + + return (fcs.value == GOOD_FCS); +} + +/***********************************************************************/ +/* Generic chip handling code */ +#ifdef DUMP_PACKETS +static unsigned char dump[50]; +static void +_dumpbufs (unsigned char *data, int len, char tete) +{ +int i,j; +char head=tete; +for (i=0;i<len;i+=16) { + for (j=0;j<16 && i+j<len;j++) { sprintf(&dump[3*j],"%02x.",data[i+j]); } + dump [3*j]=0; + IRDA_DEBUG (2, "%c%s\n",head , dump); + head='+'; + } +} +#endif + +#ifdef USE_PROBE +/* Dump the registers */ +static void +toshoboe_dumpregs (struct toshoboe_cb *self) +{ + __u32 ringbase; + + IRDA_DEBUG (4, "%s()\n", __FUNCTION__); + + ringbase = INB (OBOE_RING_BASE0) << 10; + ringbase |= INB (OBOE_RING_BASE1) << 18; + ringbase |= INB (OBOE_RING_BASE2) << 26; + + printk (KERN_ERR DRIVER_NAME ": Register dump:\n"); + printk (KERN_ERR "Interrupts: Tx:%d Rx:%d TxUnder:%d RxOver:%d Sip:%d\n", + self->int_tx, self->int_rx, self->int_txunder, self->int_rxover, + self->int_sip); + printk (KERN_ERR "RX %02x TX %02x RingBase %08x\n", + INB (OBOE_RXSLOT), INB (OBOE_TXSLOT), ringbase); + printk (KERN_ERR "RING_SIZE %02x IER %02x ISR %02x\n", + INB (OBOE_RING_SIZE), INB (OBOE_IER), INB (OBOE_ISR)); + printk (KERN_ERR "CONFIG1 %02x STATUS %02x\n", + INB (OBOE_CONFIG1), INB (OBOE_STATUS)); + printk (KERN_ERR "CONFIG0 %02x%02x ENABLE %02x%02x\n", + INB (OBOE_CONFIG0H), INB (OBOE_CONFIG0L), + INB (OBOE_ENABLEH), INB (OBOE_ENABLEL)); + printk (KERN_ERR "NEW_PCONFIG %02x%02x CURR_PCONFIG %02x%02x\n", + INB (OBOE_NEW_PCONFIGH), INB (OBOE_NEW_PCONFIGL), + INB (OBOE_CURR_PCONFIGH), INB (OBOE_CURR_PCONFIGL)); + printk (KERN_ERR "MAXLEN %02x%02x RXCOUNT %02x%02x\n", + INB (OBOE_MAXLENH), INB (OBOE_MAXLENL), + INB (OBOE_RXCOUNTL), INB (OBOE_RXCOUNTH)); + + if (self->ring) + { + int i; + ringbase = virt_to_bus (self->ring); + printk (KERN_ERR "Ring at %08x:\n", ringbase); + printk (KERN_ERR "RX:"); + for (i = 0; i < RX_SLOTS; ++i) + printk (" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control); + printk ("\n"); + printk (KERN_ERR "TX:"); + for (i = 0; i < RX_SLOTS; ++i) + printk (" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control); + printk ("\n"); + } +} +#endif + +/*Don't let the chip look at memory */ +static void +toshoboe_disablebm (struct toshoboe_cb *self) +{ + __u8 command; + IRDA_DEBUG (4, "%s()\n", __FUNCTION__); + + pci_read_config_byte (self->pdev, PCI_COMMAND, &command); + command &= ~PCI_COMMAND_MASTER; + pci_write_config_byte (self->pdev, PCI_COMMAND, command); + +} + +/* Shutdown the chip and point the taskfile reg somewhere else */ +static void +toshoboe_stopchip (struct toshoboe_cb *self) +{ + IRDA_DEBUG (4, "%s()\n", __FUNCTION__); + + /*Disable interrupts */ + OUTB (0x0, OBOE_IER); + /*Disable DMA, Disable Rx, Disable Tx */ + OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H); + /*Disable SIR MIR FIR, Tx and Rx */ + OUTB (0x00, OBOE_ENABLEH); + /*Point the ring somewhere safe */ + OUTB (0x3f, OBOE_RING_BASE2); + OUTB (0xff, OBOE_RING_BASE1); + OUTB (0xff, OBOE_RING_BASE0); + + OUTB (RX_LEN >> 8, OBOE_MAXLENH); + OUTB (RX_LEN & 0xff, OBOE_MAXLENL); + + /*Acknoledge any pending interrupts */ + OUTB (0xff, OBOE_ISR); + + /*Why */ + OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); + + /*switch it off */ + OUTB (OBOE_CONFIG1_OFF, OBOE_CONFIG1); + + toshoboe_disablebm (self); +} + +/* Transmitter initialization */ +static void +toshoboe_start_DMA (struct toshoboe_cb *self, int opts) +{ + OUTB (0x0, OBOE_ENABLEH); + OUTB (CONFIG0H_DMA_ON | opts, OBOE_CONFIG0H); + OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); + PROMPT; +} + +/*Set the baud rate */ +static void +toshoboe_setbaud (struct toshoboe_cb *self) +{ + __u16 pconfig = 0; + __u8 config0l = 0; + + IRDA_DEBUG (2, "%s(%d/%d)\n", __FUNCTION__, self->speed, self->io.speed); + + switch (self->speed) + { + case 2400: + case 4800: + case 9600: + case 19200: + case 38400: + case 57600: + case 115200: +#ifdef USE_MIR + case 1152000: +#endif + case 4000000: + break; + default: + + printk (KERN_ERR DRIVER_NAME ": switch to unsupported baudrate %d\n", + self->speed); + return; + } + + switch (self->speed) + { + /* For SIR the preamble is done by adding XBOFs */ + /* to the packet */ + /* set to filtered SIR mode, filter looks for BOF and EOF */ + case 2400: + pconfig |= 47 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; + break; + case 4800: + pconfig |= 23 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; + break; + case 9600: + pconfig |= 11 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; + break; + case 19200: + pconfig |= 5 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; + break; + case 38400: + pconfig |= 2 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; + break; + case 57600: + pconfig |= 1 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; + break; + case 115200: + pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT; + break; + default: + /*Set to packet based reception */ + OUTB (RX_LEN >> 8, OBOE_MAXLENH); + OUTB (RX_LEN & 0xff, OBOE_MAXLENL); + break; + } + + switch (self->speed) + { + case 2400: + case 4800: + case 9600: + case 19200: + case 38400: + case 57600: + case 115200: + config0l = OBOE_CONFIG0L_ENSIR; + if (self->async) + { + /*Set to character based reception */ + /*System will lock if MAXLEN=0 */ + /*so have to be careful */ + OUTB (0x01, OBOE_MAXLENH); + OUTB (0x01, OBOE_MAXLENL); + OUTB (0x00, OBOE_MAXLENH); + } + else + { + /*Set to packet based reception */ + config0l |= OBOE_CONFIG0L_ENSIRF; + OUTB (RX_LEN >> 8, OBOE_MAXLENH); + OUTB (RX_LEN & 0xff, OBOE_MAXLENL); + } + break; + +#ifdef USE_MIR + /* MIR mode */ + /* Set for 16 bit CRC and enable MIR */ + /* Preamble now handled by the chip */ + case 1152000: + pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT; + pconfig |= 8 << OBOE_PCONFIG_WIDTHSHIFT; + pconfig |= 1 << OBOE_PCONFIG_PREAMBLESHIFT; + config0l = OBOE_CONFIG0L_CRC16 | OBOE_CONFIG0L_ENMIR; + break; +#endif + /* FIR mode */ + /* Set for 32 bit CRC and enable FIR */ + /* Preamble handled by the chip */ + case 4000000: + pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT; + /* Documentation says 14, but toshiba use 15 in their drivers */ + pconfig |= 15 << OBOE_PCONFIG_PREAMBLESHIFT; + config0l = OBOE_CONFIG0L_ENFIR; + break; + } + + /* Copy into new PHY config buffer */ + OUTBP (pconfig >> 8, OBOE_NEW_PCONFIGH); + OUTB (pconfig & 0xff, OBOE_NEW_PCONFIGL); + OUTB (config0l, OBOE_CONFIG0L); + + /* Now make OBOE copy from new PHY to current PHY */ + OUTB (0x0, OBOE_ENABLEH); + OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); + PROMPT; + + /* speed change executed */ + self->new_speed = 0; + self->io.speed = self->speed; +} + +/*Let the chip look at memory */ +static void +toshoboe_enablebm (struct toshoboe_cb *self) +{ + IRDA_DEBUG (4, "%s()\n", __FUNCTION__); + pci_set_master (self->pdev); +} + +/*setup the ring */ +static void +toshoboe_initring (struct toshoboe_cb *self) +{ + int i; + + IRDA_DEBUG (4, "%s()\n", __FUNCTION__); + + for (i = 0; i < TX_SLOTS; ++i) + { + self->ring->tx[i].len = 0; + self->ring->tx[i].control = 0x00; + self->ring->tx[i].address = virt_to_bus (self->tx_bufs[i]); + } + + for (i = 0; i < RX_SLOTS; ++i) + { + self->ring->rx[i].len = RX_LEN; + self->ring->rx[i].len = 0; + self->ring->rx[i].address = virt_to_bus (self->rx_bufs[i]); + self->ring->rx[i].control = OBOE_CTL_RX_HW_OWNS; + } +} + +static void +toshoboe_resetptrs (struct toshoboe_cb *self) +{ + /* Can reset pointers by twidling DMA */ + OUTB (0x0, OBOE_ENABLEH); + OUTBP (CONFIG0H_DMA_OFF, OBOE_CONFIG0H); + OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); + + self->rxs = inb_p (OBOE_RXSLOT) & OBOE_SLOT_MASK; + self->txs = inb_p (OBOE_TXSLOT) & OBOE_SLOT_MASK; +} + +/* Called in locked state */ +static void +toshoboe_initptrs (struct toshoboe_cb *self) +{ + + /* spin_lock_irqsave(self->spinlock, flags); */ + /* save_flags (flags); */ + + /* Can reset pointers by twidling DMA */ + toshoboe_resetptrs (self); + + OUTB (0x0, OBOE_ENABLEH); + OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H); + OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); + + self->txpending = 0; + + /* spin_unlock_irqrestore(self->spinlock, flags); */ + /* restore_flags (flags); */ +} + +/* Wake the chip up and get it looking at the rings */ +/* Called in locked state */ +static void +toshoboe_startchip (struct toshoboe_cb *self) +{ + __u32 physaddr; + + IRDA_DEBUG (4, "%s()\n", __FUNCTION__); + + toshoboe_initring (self); + toshoboe_enablebm (self); + OUTBP (OBOE_CONFIG1_RESET, OBOE_CONFIG1); + OUTBP (OBOE_CONFIG1_ON, OBOE_CONFIG1); + + /* Stop the clocks */ + OUTB (0, OBOE_ENABLEH); + + /*Set size of rings */ + OUTB (RING_SIZE, OBOE_RING_SIZE); + + /*Acknoledge any pending interrupts */ + OUTB (0xff, OBOE_ISR); + + /*Enable ints */ + OUTB (OBOE_INT_TXDONE | OBOE_INT_RXDONE | + OBOE_INT_TXUNDER | OBOE_INT_RXOVER | OBOE_INT_SIP , OBOE_IER); + + /*Acknoledge any pending interrupts */ + OUTB (0xff, OBOE_ISR); + + /*Set the maximum packet length to 0xfff (4095) */ + OUTB (RX_LEN >> 8, OBOE_MAXLENH); + OUTB (RX_LEN & 0xff, OBOE_MAXLENL); + + /*Shutdown DMA */ + OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H); + + /*Find out where the rings live */ + physaddr = virt_to_bus (self->ring); + + IRDA_ASSERT ((physaddr & 0x3ff) == 0, + printk (KERN_ERR DRIVER_NAME "ring not correctly aligned\n"); + return;); + + OUTB ((physaddr >> 10) & 0xff, OBOE_RING_BASE0); + OUTB ((physaddr >> 18) & 0xff, OBOE_RING_BASE1); + OUTB ((physaddr >> 26) & 0x3f, OBOE_RING_BASE2); + + /*Enable DMA controler in byte mode and RX */ + OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H); + + /* Start up the clocks */ + OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH); + + /*set to sensible speed */ + self->speed = 9600; + toshoboe_setbaud (self); + toshoboe_initptrs (self); +} + +static void +toshoboe_isntstuck (struct toshoboe_cb *self) +{ +} + +static void +toshoboe_checkstuck (struct toshoboe_cb *self) +{ + unsigned long flags; + + if (0) + { + spin_lock_irqsave(&self->spinlock, flags); + + /* This will reset the chip completely */ + printk (KERN_ERR DRIVER_NAME ": Resetting chip\n"); + + toshoboe_stopchip (self); + toshoboe_startchip (self); + spin_unlock_irqrestore(&self->spinlock, flags); + } +} + +/*Generate packet of about mtt us long */ +static int +toshoboe_makemttpacket (struct toshoboe_cb *self, void *buf, int mtt) +{ + int xbofs; + + xbofs = ((int) (mtt/100)) * (int) (self->speed); + xbofs=xbofs/80000; /*Eight bits per byte, and mtt is in us*/ + xbofs++; + + IRDA_DEBUG (2, DRIVER_NAME + ": generated mtt of %d bytes for %d us at %d baud\n" + , xbofs,mtt,self->speed); + + if (xbofs > TX_LEN) + { + printk (KERN_ERR DRIVER_NAME ": wanted %d bytes MTT but TX_LEN is %d\n", + xbofs, TX_LEN); + xbofs = TX_LEN; + } + + /*xbofs will do for SIR, MIR and FIR,SIR mode doesn't generate a checksum anyway */ + memset (buf, XBOF, xbofs); + + return xbofs; +} + +static int toshoboe_invalid_dev(int irq) +{ + printk (KERN_WARNING DRIVER_NAME ": irq %d for unknown device.\n", irq); + return 1; +} + +#ifdef USE_PROBE +/***********************************************************************/ +/* Probe code */ + +static void +toshoboe_dumptx (struct toshoboe_cb *self) +{ + int i; + PROBE_DEBUG(KERN_WARNING "TX:"); + for (i = 0; i < RX_SLOTS; ++i) + PROBE_DEBUG(" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control); + PROBE_DEBUG(" [%d]\n",self->speed); +} + +static void +toshoboe_dumprx (struct toshoboe_cb *self, int score) +{ + int i; + PROBE_DEBUG(" %d\nRX:",score); + for (i = 0; i < RX_SLOTS; ++i) + PROBE_DEBUG(" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control); + PROBE_DEBUG("\n"); +} + +static inline int +stuff_byte (__u8 byte, __u8 * buf) +{ + switch (byte) + { + case BOF: /* FALLTHROUGH */ + case EOF: /* FALLTHROUGH */ + case CE: + /* Insert transparently coded */ + buf[0] = CE; /* Send link escape */ + buf[1] = byte ^ IRDA_TRANS; /* Complement bit 5 */ + return 2; + /* break; */ + default: + /* Non-special value, no transparency required */ + buf[0] = byte; + return 1; + /* break; */ + } +} + +static irqreturn_t +toshoboe_probeinterrupt (int irq, void *dev_id, struct pt_regs *regs) +{ + struct toshoboe_cb *self = (struct toshoboe_cb *) dev_id; + __u8 irqstat; + + if (self == NULL && toshoboe_invalid_dev(irq)) + return IRQ_NONE; + + irqstat = INB (OBOE_ISR); + +/* was it us */ + if (!(irqstat & OBOE_INT_MASK)) + return IRQ_NONE; + +/* Ack all the interrupts */ + OUTB (irqstat, OBOE_ISR); + + if (irqstat & OBOE_INT_TXDONE) + { + int txp; + + self->int_tx++; + PROBE_DEBUG("T"); + + txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK; + if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS) + { + self->int_tx+=100; + PROBE_DEBUG("S"); + toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); + } + } + + if (irqstat & OBOE_INT_RXDONE) { + self->int_rx++; + PROBE_DEBUG("R"); } + if (irqstat & OBOE_INT_TXUNDER) { + self->int_txunder++; + PROBE_DEBUG("U"); } + if (irqstat & OBOE_INT_RXOVER) { + self->int_rxover++; + PROBE_DEBUG("O"); } + if (irqstat & OBOE_INT_SIP) { + self->int_sip++; + PROBE_DEBUG("I"); } + return IRQ_HANDLED; +} + +static int +toshoboe_maketestpacket (unsigned char *buf, int badcrc, int fir) +{ + int i; + int len = 0; + union + { + __u16 value; + __u8 bytes[2]; + } + fcs; + + if (fir) + { + memset (buf, 0, TT_LEN); + return (TT_LEN); + } + + fcs.value = INIT_FCS; + + memset (buf, XBOF, 10); + len += 10; + buf[len++] = BOF; + + for (i = 0; i < TT_LEN; ++i) + { + len += stuff_byte (i, buf + len); + fcs.value = irda_fcs (fcs.value, i); + } + + len += stuff_byte (fcs.bytes[0] ^ badcrc, buf + len); + len += stuff_byte (fcs.bytes[1] ^ badcrc, buf + len); + buf[len++] = EOF; + len++; + return len; +} + +static int +toshoboe_probefail (struct toshoboe_cb *self, char *msg) +{ + printk (KERN_ERR DRIVER_NAME "probe(%d) failed %s\n",self-> speed, msg); + toshoboe_dumpregs (self); + toshoboe_stopchip (self); + free_irq (self->io.irq, (void *) self); + return 0; +} + +static int +toshoboe_numvalidrcvs (struct toshoboe_cb *self) +{ + int i, ret = 0; + for (i = 0; i < RX_SLOTS; ++i) + if ((self->ring->rx[i].control & 0xe0) == 0) + ret++; + + return ret; +} + +static int +toshoboe_numrcvs (struct toshoboe_cb *self) +{ + int i, ret = 0; + for (i = 0; i < RX_SLOTS; ++i) + if (!(self->ring->rx[i].control & OBOE_CTL_RX_HW_OWNS)) + ret++; + + return ret; +} + +static int +toshoboe_probe (struct toshoboe_cb *self) +{ + int i, j, n; +#ifdef USE_MIR + int bauds[] = { 9600, 115200, 4000000, 1152000 }; +#else + int bauds[] = { 9600, 115200, 4000000 }; +#endif + unsigned long flags; + + IRDA_DEBUG (4, "%s()\n", __FUNCTION__); + + if (request_irq (self->io.irq, toshoboe_probeinterrupt, + self->io.irqflags, "toshoboe", (void *) self)) + { + printk (KERN_ERR DRIVER_NAME ": probe failed to allocate irq %d\n", + self->io.irq); + return 0; + } + + /* test 1: SIR filter and back to back */ + + for (j = 0; j < (sizeof (bauds) / sizeof (int)); ++j) + { + int fir = (j > 1); + toshoboe_stopchip (self); + + + spin_lock_irqsave(&self->spinlock, flags); + /*Address is already setup */ + toshoboe_startchip (self); + self->int_rx = self->int_tx = 0; + self->speed = bauds[j]; + toshoboe_setbaud (self); + toshoboe_initptrs (self); + spin_unlock_irqrestore(&self->spinlock, flags); + + self->ring->tx[self->txs].control = +/* (FIR only) OBOE_CTL_TX_SIP needed for switching to next slot */ +/* MIR: all received data is stored in one slot */ + (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX + : OBOE_CTL_TX_HW_OWNS ; + self->ring->tx[self->txs].len = + toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); + self->txs++; + self->txs %= TX_SLOTS; + + self->ring->tx[self->txs].control = + (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_SIP + : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ; + self->ring->tx[self->txs].len = + toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); + self->txs++; + self->txs %= TX_SLOTS; + + self->ring->tx[self->txs].control = + (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX + : OBOE_CTL_TX_HW_OWNS ; + self->ring->tx[self->txs].len = + toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); + self->txs++; + self->txs %= TX_SLOTS; + + self->ring->tx[self->txs].control = + (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX + | OBOE_CTL_TX_SIP | OBOE_CTL_TX_BAD_CRC + : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ; + self->ring->tx[self->txs].len = + toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir); + self->txs++; + self->txs %= TX_SLOTS; + + toshoboe_dumptx (self); + /* Turn on TX and RX and loopback */ + toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); + + i = 0; + n = fir ? 1 : 4; + while (toshoboe_numvalidrcvs (self) != n) + { + if (i > 4800) + return toshoboe_probefail (self, "filter test"); + udelay ((9600*(TT_LEN+16))/self->speed); + i++; + } + + n = fir ? 203 : 102; + while ((toshoboe_numrcvs(self) != self->int_rx) || (self->int_tx != n)) + { + if (i > 4800) + return toshoboe_probefail (self, "interrupt test"); + udelay ((9600*(TT_LEN+16))/self->speed); + i++; + } + toshoboe_dumprx (self,i); + + } + + /* test 2: SIR in char at a time */ + + toshoboe_stopchip (self); + self->int_rx = self->int_tx = 0; + + spin_lock_irqsave(&self->spinlock, flags); + toshoboe_startchip (self); + spin_unlock_irqrestore(&self->spinlock, flags); + + self->async = 1; + self->speed = 115200; + toshoboe_setbaud (self); + self->ring->tx[self->txs].control = + OBOE_CTL_TX_RTCENTX | OBOE_CTL_TX_HW_OWNS; + self->ring->tx[self->txs].len = 4; + + ((unsigned char *) self->tx_bufs[self->txs])[0] = 'f'; + ((unsigned char *) self->tx_bufs[self->txs])[1] = 'i'; + ((unsigned char *) self->tx_bufs[self->txs])[2] = 's'; + ((unsigned char *) self->tx_bufs[self->txs])[3] = 'h'; + toshoboe_dumptx (self); + toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); + + i = 0; + while (toshoboe_numvalidrcvs (self) != 4) + { + if (i > 100) + return toshoboe_probefail (self, "Async test"); + udelay (100); + i++; + } + + while ((toshoboe_numrcvs (self) != self->int_rx) || (self->int_tx != 1)) + { + if (i > 100) + return toshoboe_probefail (self, "Async interrupt test"); + udelay (100); + i++; + } + toshoboe_dumprx (self,i); + + self->async = 0; + self->speed = 9600; + toshoboe_setbaud (self); + toshoboe_stopchip (self); + + free_irq (self->io.irq, (void *) self); + + printk (KERN_WARNING DRIVER_NAME ": Self test passed ok\n"); + + return 1; +} +#endif + +/******************************************************************/ +/* Netdev style code */ + +/* Transmit something */ +static int +toshoboe_hard_xmit (struct sk_buff *skb, struct net_device *dev) +{ + struct toshoboe_cb *self; + __s32 speed; + int mtt, len, ctl; + unsigned long flags; + struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb; + + self = (struct toshoboe_cb *) dev->priv; + + IRDA_ASSERT (self != NULL, return 0; ); + + IRDA_DEBUG (1, "%s.tx:%x(%x)%x\n", __FUNCTION__ + ,skb->len,self->txpending,INB (OBOE_ENABLEH)); + if (!cb->magic) { + IRDA_DEBUG (2, "%s.Not IrLAP:%x\n", __FUNCTION__, cb->magic); +#ifdef DUMP_PACKETS + _dumpbufs(skb->data,skb->len,'>'); +#endif + } + + /* change speed pending, wait for its execution */ + if (self->new_speed) + return -EBUSY; + + /* device stopped (apm) wait for restart */ + if (self->stopped) + return -EBUSY; + + toshoboe_checkstuck (self); + + 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)) + { + spin_lock_irqsave(&self->spinlock, flags); + + if (self->txpending || skb->len) + { + self->new_speed = speed; + IRDA_DEBUG (1, "%s: Queued TxDone scheduled speed change %d\n" , + __FUNCTION__, speed); + /* if no data, that's all! */ + if (!skb->len) + { + spin_unlock_irqrestore(&self->spinlock, flags); + dev_kfree_skb (skb); + return 0; + } + /* True packet, go on, but */ + /* do not accept anything before change speed execution */ + netif_stop_queue(dev); + /* ready to process TxDone interrupt */ + spin_unlock_irqrestore(&self->spinlock, flags); + } + else + { + /* idle and no data, change speed now */ + self->speed = speed; + toshoboe_setbaud (self); + spin_unlock_irqrestore(&self->spinlock, flags); + dev_kfree_skb (skb); + return 0; + } + + } + + if ((mtt = irda_get_mtt(skb))) + { + /* This is fair since the queue should be empty anyway */ + spin_lock_irqsave(&self->spinlock, flags); + + if (self->txpending) + { + spin_unlock_irqrestore(&self->spinlock, flags); + return -EBUSY; + } + + /* If in SIR mode we need to generate a string of XBOFs */ + /* In MIR and FIR we need to generate a string of data */ + /* which we will add a wrong checksum to */ + + mtt = toshoboe_makemttpacket (self, self->tx_bufs[self->txs], mtt); + IRDA_DEBUG (1, "%s.mtt:%x(%x)%d\n", __FUNCTION__ + ,skb->len,mtt,self->txpending); + if (mtt) + { + self->ring->tx[self->txs].len = mtt & 0xfff; + + ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX; + if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON) + { + ctl |= OBOE_CTL_TX_BAD_CRC | OBOE_CTL_TX_SIP ; + } +#ifdef USE_MIR + else if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_MIRON) + { + ctl |= OBOE_CTL_TX_BAD_CRC; + } +#endif + self->ring->tx[self->txs].control = ctl; + + OUTB (0x0, OBOE_ENABLEH); + /* It is only a timer. Do not send mtt packet outside! */ + toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP); + + self->txpending++; + + self->txs++; + self->txs %= TX_SLOTS; + + } + else + { + printk(KERN_ERR DRIVER_NAME ": problem with mtt packet - ignored\n"); + } + spin_unlock_irqrestore(&self->spinlock, flags); + } + +#ifdef DUMP_PACKETS +dumpbufs(skb->data,skb->len,'>'); +#endif + + spin_lock_irqsave(&self->spinlock, flags); + + if (self->ring->tx[self->txs].control & OBOE_CTL_TX_HW_OWNS) + { + IRDA_DEBUG (0, "%s.ful:%x(%x)%x\n", __FUNCTION__ + ,skb->len, self->ring->tx[self->txs].control, self->txpending); + toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX); + spin_unlock_irqrestore(&self->spinlock, flags); + return -EBUSY; + } + + if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_SIRON) + { + len = async_wrap_skb (skb, self->tx_bufs[self->txs], TX_BUF_SZ); + } + else + { + len = skb->len; + memcpy (self->tx_bufs[self->txs], skb->data, len); + } + self->ring->tx[self->txs].len = len & 0x0fff; + + /*Sometimes the HW doesn't see us assert RTCENTX in the interrupt code */ + /*later this plays safe, we garuntee the last packet to be transmitted */ + /*has RTCENTX set */ + + ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX; + if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON) + { + ctl |= OBOE_CTL_TX_SIP ; + } + self->ring->tx[self->txs].control = ctl; + + /* If transmitter is idle start in one-shot mode */ + + if (!self->txpending) + toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX); + + self->txpending++; + + self->txs++; + self->txs %= TX_SLOTS; + + spin_unlock_irqrestore(&self->spinlock, flags); + dev_kfree_skb (skb); + + return 0; +} + +/*interrupt handler */ +static irqreturn_t +toshoboe_interrupt (int irq, void *dev_id, struct pt_regs *regs) +{ + struct toshoboe_cb *self = (struct toshoboe_cb *) dev_id; + __u8 irqstat; + struct sk_buff *skb = NULL; + + if (self == NULL && toshoboe_invalid_dev(irq)) + return IRQ_NONE; + + irqstat = INB (OBOE_ISR); + +/* was it us */ + if (!(irqstat & OBOE_INT_MASK)) + return IRQ_NONE; + +/* Ack all the interrupts */ + OUTB (irqstat, OBOE_ISR); + + toshoboe_isntstuck (self); + +/* Txdone */ + if (irqstat & OBOE_INT_TXDONE) + { + int txp, txpc; + int i; + + txp = self->txpending; + self->txpending = 0; + + for (i = 0; i < TX_SLOTS; ++i) + { + if (self->ring->tx[i].control & OBOE_CTL_TX_HW_OWNS) + self->txpending++; + } + IRDA_DEBUG (1, "%s.txd(%x)%x/%x\n", __FUNCTION__ + ,irqstat,txp,self->txpending); + + txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK; + + /* Got anything queued ? start it together */ + if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS) + { + txpc = txp; +#ifdef OPTIMIZE_TX + while (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS) + { + txp = txpc; + txpc++; + txpc %= TX_SLOTS; + self->stats.tx_packets++; + if (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS) + self->ring->tx[txp].control &= ~OBOE_CTL_TX_RTCENTX; + } + self->stats.tx_packets--; +#else + self->stats.tx_packets++; +#endif + toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX); + } + + if ((!self->txpending) && (self->new_speed)) + { + self->speed = self->new_speed; + IRDA_DEBUG (1, "%s: Executed TxDone scheduled speed change %d\n", + __FUNCTION__, self->speed); + toshoboe_setbaud (self); + } + + /* Tell network layer that we want more frames */ + if (!self->new_speed) + netif_wake_queue(self->netdev); + } + + if (irqstat & OBOE_INT_RXDONE) + { + while (!(self->ring->rx[self->rxs].control & OBOE_CTL_RX_HW_OWNS)) + { + int len = self->ring->rx[self->rxs].len; + skb = NULL; + IRDA_DEBUG (3, "%s.rcv:%x(%x)\n", __FUNCTION__ + ,len,self->ring->rx[self->rxs].control); + +#ifdef DUMP_PACKETS +dumpbufs(self->rx_bufs[self->rxs],len,'<'); +#endif + + if (self->ring->rx[self->rxs].control == 0) + { + __u8 enable = INB (OBOE_ENABLEH); + + /* In SIR mode we need to check the CRC as this */ + /* hasn't been done by the hardware */ + if (enable & OBOE_ENABLEH_SIRON) + { + if (!toshoboe_checkfcs (self->rx_bufs[self->rxs], len)) + len = 0; + /*Trim off the CRC */ + if (len > 1) + len -= 2; + else + len = 0; + IRDA_DEBUG (1, "%s.SIR:%x(%x)\n", __FUNCTION__, len,enable); + } + +#ifdef USE_MIR + else if (enable & OBOE_ENABLEH_MIRON) + { + if (len > 1) + len -= 2; + else + len = 0; + IRDA_DEBUG (2, "%s.MIR:%x(%x)\n", __FUNCTION__, len,enable); + } +#endif + else if (enable & OBOE_ENABLEH_FIRON) + { + if (len > 3) + len -= 4; /*FIXME: check this */ + else + len = 0; + IRDA_DEBUG (1, "%s.FIR:%x(%x)\n", __FUNCTION__, len,enable); + } + else + IRDA_DEBUG (0, "%s.?IR:%x(%x)\n", __FUNCTION__, len,enable); + + if (len) + { + skb = dev_alloc_skb (len + 1); + if (skb) + { + skb_reserve (skb, 1); + + skb_put (skb, len); + memcpy (skb->data, self->rx_bufs[self->rxs], len); + + self->stats.rx_packets++; + skb->dev = self->netdev; + skb->mac.raw = skb->data; + skb->protocol = htons (ETH_P_IRDA); + } + else + { + printk (KERN_INFO + "%s(), memory squeeze, dropping frame.\n", + __FUNCTION__); + } + } + } + else + { + /* TODO: =========================================== */ + /* if OBOE_CTL_RX_LENGTH, our buffers are too small */ + /* (MIR or FIR) data is lost. */ + /* (SIR) data is splitted in several slots. */ + /* we have to join all the received buffers received */ + /*in a large buffer before checking CRC. */ + IRDA_DEBUG (0, "%s.err:%x(%x)\n", __FUNCTION__ + ,len,self->ring->rx[self->rxs].control); + } + + self->ring->rx[self->rxs].len = 0x0; + self->ring->rx[self->rxs].control = OBOE_CTL_RX_HW_OWNS; + + self->rxs++; + self->rxs %= RX_SLOTS; + + if (skb) + netif_rx (skb); + + } + } + + if (irqstat & OBOE_INT_TXUNDER) + { + printk (KERN_WARNING DRIVER_NAME ": tx fifo underflow\n"); + } + if (irqstat & OBOE_INT_RXOVER) + { + printk (KERN_WARNING DRIVER_NAME ": rx fifo overflow\n"); + } +/* This must be useful for something... */ + if (irqstat & OBOE_INT_SIP) + { + self->int_sip++; + IRDA_DEBUG (1, "%s.sip:%x(%x)%x\n", __FUNCTION__ + ,self->int_sip,irqstat,self->txpending); + } + return IRQ_HANDLED; +} + + +static int +toshoboe_net_open (struct net_device *dev) +{ + struct toshoboe_cb *self; + unsigned long flags; + + IRDA_DEBUG (4, "%s()\n", __FUNCTION__); + + IRDA_ASSERT (dev != NULL, return -1; ); + self = (struct toshoboe_cb *) dev->priv; + + IRDA_ASSERT (self != NULL, return 0; ); + + if (self->async) + return -EBUSY; + + if (self->stopped) + return 0; + + if (request_irq (self->io.irq, toshoboe_interrupt, + SA_SHIRQ | SA_INTERRUPT, dev->name, (void *) self)) + { + return -EAGAIN; + } + + spin_lock_irqsave(&self->spinlock, flags); + toshoboe_startchip (self); + spin_unlock_irqrestore(&self->spinlock, flags); + + /* Ready to play! */ + netif_start_queue(dev); + + /* + * Open new IrLAP layer instance, now that everything should be + * initialized properly + */ + self->irlap = irlap_open (dev, &self->qos, driver_name); + + self->irdad = 1; + + return 0; +} + +static int +toshoboe_net_close (struct net_device *dev) +{ + struct toshoboe_cb *self; + + IRDA_DEBUG (4, "%s()\n", __FUNCTION__); + + IRDA_ASSERT (dev != NULL, return -1; ); + self = (struct toshoboe_cb *) dev->priv; + + /* Stop device */ + netif_stop_queue(dev); + + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close (self->irlap); + self->irlap = NULL; + + self->irdad = 0; + + free_irq (self->io.irq, (void *) self); + + if (!self->stopped) + { + toshoboe_stopchip (self); + } + + return 0; +} + +/* + * Function toshoboe_net_ioctl (dev, rq, cmd) + * + * Process IOCTL commands for this device + * + */ +static int +toshoboe_net_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct toshoboe_cb *self; + unsigned long flags; + int ret = 0; + + IRDA_ASSERT (dev != NULL, return -1; ); + + self = dev->priv; + + IRDA_ASSERT (self != NULL, return -1; ); + + IRDA_DEBUG (5, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, cmd); + + /* Disable interrupts & save flags */ + spin_lock_irqsave(&self->spinlock, flags); + + switch (cmd) + { + case SIOCSBANDWIDTH: /* Set bandwidth */ + /* This function will also be used by IrLAP to change the + * speed, so we still must allow for speed change within + * interrupt context. + */ + IRDA_DEBUG (1, "%s(BANDWIDTH), %s, (%X/%ld\n", __FUNCTION__ + ,dev->name, INB (OBOE_STATUS), irq->ifr_baudrate ); + if (!in_interrupt () && !capable (CAP_NET_ADMIN)) + return -EPERM; + + /* self->speed=irq->ifr_baudrate; */ + /* toshoboe_setbaud(self); */ + /* Just change speed once - inserted by Paul Bristow */ + self->new_speed = irq->ifr_baudrate; + break; + case SIOCSMEDIABUSY: /* Set media busy */ + IRDA_DEBUG (1, "%s(MEDIABUSY), %s, (%X/%x)\n", __FUNCTION__ + ,dev->name, INB (OBOE_STATUS), capable (CAP_NET_ADMIN) ); + if (!capable (CAP_NET_ADMIN)) + return -EPERM; + irda_device_set_media_busy (self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + irq->ifr_receiving = (INB (OBOE_STATUS) & OBOE_STATUS_RXBUSY) ? 1 : 0; + IRDA_DEBUG (3, "%s(RECEIVING), %s, (%X/%x)\n", __FUNCTION__ + ,dev->name, INB (OBOE_STATUS), irq->ifr_receiving ); + break; + default: + IRDA_DEBUG (1, "%s(?), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, cmd); + ret = -EOPNOTSUPP; + } + + spin_unlock_irqrestore(&self->spinlock, flags); + return ret; + +} + +MODULE_DESCRIPTION("Toshiba OBOE IrDA Device Driver"); +MODULE_AUTHOR("James McKenzie <james@fishsoup.dhs.org>"); +MODULE_LICENSE("GPL"); + +module_param (max_baud, int, 0); +MODULE_PARM_DESC(max_baud, "Maximum baud rate"); + +#ifdef USE_PROBE +module_param (do_probe, bool, 0); +MODULE_PARM_DESC(do_probe, "Enable/disable chip probing and self-test"); +#endif + +static void +toshoboe_close (struct pci_dev *pci_dev) +{ + int i; + struct toshoboe_cb *self = (struct toshoboe_cb*)pci_get_drvdata(pci_dev); + + IRDA_DEBUG (4, "%s()\n", __FUNCTION__); + + IRDA_ASSERT (self != NULL, return; ); + + if (!self->stopped) + { + toshoboe_stopchip (self); + } + + release_region (self->io.fir_base, self->io.fir_ext); + + for (i = 0; i < TX_SLOTS; ++i) + { + kfree (self->tx_bufs[i]); + self->tx_bufs[i] = NULL; + } + + for (i = 0; i < RX_SLOTS; ++i) + { + kfree (self->rx_bufs[i]); + self->rx_bufs[i] = NULL; + } + + unregister_netdev(self->netdev); + + kfree (self->ringbuf); + self->ringbuf = NULL; + self->ring = NULL; + + free_netdev(self->netdev); +} + +static int +toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid) +{ + struct toshoboe_cb *self; + struct net_device *dev; + int i = 0; + int ok = 0; + int err; + + IRDA_DEBUG (4, "%s()\n", __FUNCTION__); + + if ((err=pci_enable_device(pci_dev))) + return err; + + dev = alloc_irdadev(sizeof (struct toshoboe_cb)); + if (dev == NULL) + { + printk (KERN_ERR DRIVER_NAME ": can't allocate memory for " + "IrDA control block\n"); + return -ENOMEM; + } + + self = dev->priv; + self->netdev = dev; + self->pdev = pci_dev; + self->base = pci_resource_start(pci_dev,0); + + self->io.fir_base = self->base; + self->io.fir_ext = OBOE_IO_EXTENT; + self->io.irq = pci_dev->irq; + self->io.irqflags = SA_SHIRQ | SA_INTERRUPT; + + self->speed = self->io.speed = 9600; + self->async = 0; + + /* Lock the port that we need */ + if (NULL==request_region (self->io.fir_base, self->io.fir_ext, driver_name)) + { + printk (KERN_ERR DRIVER_NAME ": can't get iobase of 0x%03x\n" + ,self->io.fir_base); + err = -EBUSY; + goto freeself; + } + + spin_lock_init(&self->spinlock); + + irda_init_max_qos_capabilies (&self->qos); + self->qos.baud_rate.bits = 0; + + if (max_baud >= 2400) + self->qos.baud_rate.bits |= IR_2400; + /*if (max_baud>=4800) idev->qos.baud_rate.bits|=IR_4800; */ + if (max_baud >= 9600) + self->qos.baud_rate.bits |= IR_9600; + if (max_baud >= 19200) + self->qos.baud_rate.bits |= IR_19200; + if (max_baud >= 115200) + self->qos.baud_rate.bits |= IR_115200; +#ifdef USE_MIR + if (max_baud >= 1152000) + { + self->qos.baud_rate.bits |= IR_1152000; + } +#endif + if (max_baud >= 4000000) + { + self->qos.baud_rate.bits |= (IR_4000000 << 8); + } + + /*FIXME: work this out... */ + self->qos.min_turn_time.bits = 0xff; + + irda_qos_bits_to_value (&self->qos); + + /* Allocate twice the size to guarantee alignment */ + self->ringbuf = (void *) kmalloc (OBOE_RING_LEN << 1, GFP_KERNEL); + if (!self->ringbuf) + { + printk (KERN_ERR DRIVER_NAME ": can't allocate DMA buffers\n"); + err = -ENOMEM; + goto freeregion; + } + +#if (BITS_PER_LONG == 64) +#error broken on 64-bit: casts pointer to 32-bit, and then back to pointer. +#endif + + /*We need to align the taskfile on a taskfile size boundary */ + { + unsigned long addr; + + addr = (__u32) self->ringbuf; + addr &= ~(OBOE_RING_LEN - 1); + addr += OBOE_RING_LEN; + self->ring = (struct OboeRing *) addr; + } + + memset (self->ring, 0, OBOE_RING_LEN); + self->io.mem_base = (__u32) self->ring; + + ok = 1; + for (i = 0; i < TX_SLOTS; ++i) + { + self->tx_bufs[i] = kmalloc (TX_BUF_SZ, GFP_KERNEL); + if (!self->tx_bufs[i]) + ok = 0; + } + + for (i = 0; i < RX_SLOTS; ++i) + { + self->rx_bufs[i] = kmalloc (RX_BUF_SZ, GFP_KERNEL); + if (!self->rx_bufs[i]) + ok = 0; + } + + if (!ok) + { + printk (KERN_ERR DRIVER_NAME ": can't allocate rx/tx buffers\n"); + err = -ENOMEM; + goto freebufs; + } + + +#ifdef USE_PROBE + if (do_probe) + if (!toshoboe_probe (self)) + { + err = -ENODEV; + goto freebufs; + } +#endif + + SET_MODULE_OWNER(dev); + SET_NETDEV_DEV(dev, &pci_dev->dev); + dev->hard_start_xmit = toshoboe_hard_xmit; + dev->open = toshoboe_net_open; + dev->stop = toshoboe_net_close; + dev->do_ioctl = toshoboe_net_ioctl; + + err = register_netdev(dev); + if (err) + { + printk (KERN_ERR DRIVER_NAME ": register_netdev() failed\n"); + err = -ENOMEM; + goto freebufs; + } + printk (KERN_INFO "IrDA: Registered device %s\n", dev->name); + + pci_set_drvdata(pci_dev,self); + + printk (KERN_INFO DRIVER_NAME ": Using multiple tasks, version %s\n", rcsid); + + return 0; + +freebufs: + for (i = 0; i < TX_SLOTS; ++i) + if (self->tx_bufs[i]) + kfree (self->tx_bufs[i]); + for (i = 0; i < RX_SLOTS; ++i) + if (self->rx_bufs[i]) + kfree (self->rx_bufs[i]); + kfree(self->ringbuf); + +freeregion: + release_region (self->io.fir_base, self->io.fir_ext); + +freeself: + free_netdev(dev); + + return err; +} + +static int +toshoboe_gotosleep (struct pci_dev *pci_dev, pm_message_t crap) +{ + struct toshoboe_cb *self = (struct toshoboe_cb*)pci_get_drvdata(pci_dev); + unsigned long flags; + int i = 10; + + IRDA_DEBUG (4, "%s()\n", __FUNCTION__); + + if (!self || self->stopped) + return 0; + + if ((!self->irdad) && (!self->async)) + return 0; + +/* Flush all packets */ + while ((i--) && (self->txpending)) + udelay (10000); + + spin_lock_irqsave(&self->spinlock, flags); + + toshoboe_stopchip (self); + self->stopped = 1; + self->txpending = 0; + + spin_unlock_irqrestore(&self->spinlock, flags); + return 0; +} + +static int +toshoboe_wakeup (struct pci_dev *pci_dev) +{ + struct toshoboe_cb *self = (struct toshoboe_cb*)pci_get_drvdata(pci_dev); + unsigned long flags; + + IRDA_DEBUG (4, "%s()\n", __FUNCTION__); + + if (!self || !self->stopped) + return 0; + + if ((!self->irdad) && (!self->async)) + return 0; + + spin_lock_irqsave(&self->spinlock, flags); + + toshoboe_startchip (self); + self->stopped = 0; + + netif_wake_queue(self->netdev); + spin_unlock_irqrestore(&self->spinlock, flags); + return 0; +} + +static struct pci_driver donauboe_pci_driver = { + .name = "donauboe", + .id_table = toshoboe_pci_tbl, + .probe = toshoboe_open, + .remove = toshoboe_close, + .suspend = toshoboe_gotosleep, + .resume = toshoboe_wakeup +}; + +static int __init +donauboe_init (void) +{ + return pci_module_init(&donauboe_pci_driver); +} + +static void __exit +donauboe_cleanup (void) +{ + pci_unregister_driver(&donauboe_pci_driver); +} + +module_init(donauboe_init); +module_exit(donauboe_cleanup); diff --git a/drivers/net/irda/donauboe.h b/drivers/net/irda/donauboe.h new file mode 100644 index 000000000000..2ab173d9a0e4 --- /dev/null +++ b/drivers/net/irda/donauboe.h @@ -0,0 +1,363 @@ +/********************************************************************* + * + * Filename: toshoboe.h + * Version: 2.16 + * Description: Driver for the Toshiba OBOE (or type-O or 701) + * FIR Chipset, also supports the DONAUOBOE (type-DO + * or d01) FIR chipset which as far as I know is + * register compatible. + * Status: Experimental. + * Author: James McKenzie <james@fishsoup.dhs.org> + * Created at: Sat May 8 12:35:27 1999 + * Modified: 2.16 Martin Lucina <mato@kotelna.sk> + * Modified: 2.16 Sat Jun 22 18:54:29 2002 (sync headers) + * Modified: 2.17 Christian Gennerat <christian.gennerat@polytechnique.org> + * Modified: 2.17 jeu sep 12 08:50:20 2002 (add lock to be used by spinlocks) + * + * Copyright (c) 1999 James McKenzie, 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 James McKenzie nor Cambridge University admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + * Applicable Models : Libretto 100/110CT and many more. + * Toshiba refers to this chip as the type-O IR port, + * or the type-DO IR port. + * + * IrDA chip set list from Toshiba Computer Engineering Corp. + * model method maker controler Version + * Portege 320CT FIR,SIR Toshiba Oboe(Triangle) + * Portege 3010CT FIR,SIR Toshiba Oboe(Sydney) + * Portege 3015CT FIR,SIR Toshiba Oboe(Sydney) + * Portege 3020CT FIR,SIR Toshiba Oboe(Sydney) + * Portege 7020CT FIR,SIR ? ? + * + * Satell. 4090XCDT FIR,SIR ? ? + * + * Libretto 100CT FIR,SIR Toshiba Oboe + * Libretto 1000CT FIR,SIR Toshiba Oboe + * + * TECRA750DVD FIR,SIR Toshiba Oboe(Triangle) REV ID=14h + * TECRA780 FIR,SIR Toshiba Oboe(Sandlot) REV ID=32h,33h + * TECRA750CDT FIR,SIR Toshiba Oboe(Triangle) REV ID=13h,14h + * TECRA8000 FIR,SIR Toshiba Oboe(ISKUR) REV ID=23h + * + ********************************************************************/ + +/* The documentation for this chip is allegedly released */ +/* However I have not seen it, not have I managed to contact */ +/* anyone who has. HOWEVER the chip bears a striking resemblence */ +/* to the IrDA controller in the Toshiba RISC TMPR3922 chip */ +/* the documentation for this is freely available at */ +/* http://www.toshiba.com/taec/components/Generic/TMPR3922.shtml */ +/* The mapping between the registers in that document and the */ +/* Registers in the 701 oboe chip are as follows */ + + +/* 3922 reg 701 regs, by bit numbers */ +/* 7- 0 15- 8 24-16 31-25 */ +/* $28 0x0 0x1 */ +/* $2c SEE NOTE 1 */ +/* $30 0x6 0x7 */ +/* $34 0x8 0x9 SEE NOTE 2 */ +/* $38 0x10 0x11 */ +/* $3C 0xe SEE NOTE 3 */ +/* $40 0x12 0x13 */ +/* $44 0x14 0x15 */ +/* $48 0x16 0x17 */ +/* $4c 0x18 0x19 */ +/* $50 0x1a 0x1b */ + +/* FIXME: could be 0x1b 0x1a here */ + +/* $54 0x1d 0x1c */ +/* $5C 0xf SEE NOTE 4 */ +/* $130 SEE NOTE 5 */ +/* $134 SEE NOTE 6 */ +/* */ +/* NOTES: */ +/* 1. The pointer to ring is packed in most unceremoniusly */ +/* 701 Register Address bits (A9-A0 must be zero) */ +/* 0x4: A17 A16 A15 A14 A13 A12 A11 A10 */ +/* 0x5: A25 A24 A23 A22 A21 A20 A19 A18 */ +/* 0x2: 0 0 A31 A30 A29 A28 A27 A26 */ +/* */ +/* 2. The M$ drivers do a write 0x1 to 0x9, however the 3922 */ +/* documentation would suggest that a write of 0x1 to 0x8 */ +/* would be more appropriate. */ +/* */ +/* 3. This assignment is tenuous at best, register 0xe seems to */ +/* have bits arranged 0 0 0 R/W R/W R/W R/W R/W */ +/* if either of the lower two bits are set the chip seems to */ +/* switch off */ +/* */ +/* 4. Bits 7-4 seem to be different 4 seems just to be generic */ +/* receiver busy flag */ +/* */ +/* 5. and 6. The IER and ISR have a different bit assignment */ +/* The lower three bits of both read back as ones */ +/* ISR is register 0xc, IER is register 0xd */ +/* 7 6 5 4 3 2 1 0 */ +/* 0xc: TxDone RxDone TxUndr RxOver SipRcv 1 1 1 */ +/* 0xd: TxDone RxDone TxUndr RxOver SipRcv 1 1 1 */ +/* TxDone xmitt done (generated only if generate interrupt bit */ +/* is set in the ring) */ +/* RxDone recv completed (or other recv condition if you set it */ +/* up */ +/* TxUnder underflow in Transmit FIFO */ +/* RxOver overflow in Recv FIFO */ +/* SipRcv received serial gap (or other condition you set) */ +/* Interrupts are enabled by writing a one to the IER register */ +/* Interrupts are cleared by writting a one to the ISR register */ +/* */ +/* 6. The remaining registers: 0x6 and 0x3 appear to be */ +/* reserved parts of 16 or 32 bit registersthe remainder */ +/* 0xa 0xb 0x1e 0x1f could possibly be (by their behaviour) */ +/* the Unicast Filter register at $58. */ +/* */ +/* 7. While the core obviously expects 32 bit accesses all the */ +/* M$ drivers do 8 bit accesses, infact the Miniport ones */ +/* write and read back the byte serveral times (why?) */ + + +#ifndef TOSHOBOE_H +#define TOSHOBOE_H + +/* Registers */ + +#define OBOE_IO_EXTENT 0x1f + +/*Receive and transmit slot pointers */ +#define OBOE_REG(i) (i+(self->base)) +#define OBOE_RXSLOT OBOE_REG(0x0) +#define OBOE_TXSLOT OBOE_REG(0x1) +#define OBOE_SLOT_MASK 0x3f + +#define OBOE_TXRING_OFFSET 0x200 +#define OBOE_TXRING_OFFSET_IN_SLOTS 0x40 + +/*pointer to the ring */ +#define OBOE_RING_BASE0 OBOE_REG(0x4) +#define OBOE_RING_BASE1 OBOE_REG(0x5) +#define OBOE_RING_BASE2 OBOE_REG(0x2) +#define OBOE_RING_BASE3 OBOE_REG(0x3) + +/*Number of slots in the ring */ +#define OBOE_RING_SIZE OBOE_REG(0x7) +#define OBOE_RING_SIZE_RX4 0x00 +#define OBOE_RING_SIZE_RX8 0x01 +#define OBOE_RING_SIZE_RX16 0x03 +#define OBOE_RING_SIZE_RX32 0x07 +#define OBOE_RING_SIZE_RX64 0x0f +#define OBOE_RING_SIZE_TX4 0x00 +#define OBOE_RING_SIZE_TX8 0x10 +#define OBOE_RING_SIZE_TX16 0x30 +#define OBOE_RING_SIZE_TX32 0x70 +#define OBOE_RING_SIZE_TX64 0xf0 + +#define OBOE_RING_MAX_SIZE 64 + +/*Causes the gubbins to re-examine the ring */ +#define OBOE_PROMPT OBOE_REG(0x9) +#define OBOE_PROMPT_BIT 0x1 + +/* Interrupt Status Register */ +#define OBOE_ISR OBOE_REG(0xc) +/* Interrupt Enable Register */ +#define OBOE_IER OBOE_REG(0xd) +/* Interrupt bits for IER and ISR */ +#define OBOE_INT_TXDONE 0x80 +#define OBOE_INT_RXDONE 0x40 +#define OBOE_INT_TXUNDER 0x20 +#define OBOE_INT_RXOVER 0x10 +#define OBOE_INT_SIP 0x08 +#define OBOE_INT_MASK 0xf8 + +/*Reset Register */ +#define OBOE_CONFIG1 OBOE_REG(0xe) +#define OBOE_CONFIG1_RST 0x01 +#define OBOE_CONFIG1_DISABLE 0x02 +#define OBOE_CONFIG1_4 0x08 +#define OBOE_CONFIG1_8 0x08 + +#define OBOE_CONFIG1_ON 0x8 +#define OBOE_CONFIG1_RESET 0xf +#define OBOE_CONFIG1_OFF 0xe + +#define OBOE_STATUS OBOE_REG(0xf) +#define OBOE_STATUS_RXBUSY 0x10 +#define OBOE_STATUS_FIRRX 0x04 +#define OBOE_STATUS_MIRRX 0x02 +#define OBOE_STATUS_SIRRX 0x01 + + +/*Speed control registers */ +#define OBOE_CONFIG0L OBOE_REG(0x10) +#define OBOE_CONFIG0H OBOE_REG(0x11) + +#define OBOE_CONFIG0H_TXONLOOP 0x80 /*Transmit when looping (dangerous) */ +#define OBOE_CONFIG0H_LOOP 0x40 /*Loopback Tx->Rx */ +#define OBOE_CONFIG0H_ENTX 0x10 /*Enable Tx */ +#define OBOE_CONFIG0H_ENRX 0x08 /*Enable Rx */ +#define OBOE_CONFIG0H_ENDMAC 0x04 /*Enable/reset* the DMA controller */ +#define OBOE_CONFIG0H_RCVANY 0x02 /*DMA mode 1=bytes, 0=dwords */ + +#define OBOE_CONFIG0L_CRC16 0x80 /*CRC 1=16 bit 0=32 bit */ +#define OBOE_CONFIG0L_ENFIR 0x40 /*Enable FIR */ +#define OBOE_CONFIG0L_ENMIR 0x20 /*Enable MIR */ +#define OBOE_CONFIG0L_ENSIR 0x10 /*Enable SIR */ +#define OBOE_CONFIG0L_ENSIRF 0x08 /*Enable SIR framer */ +#define OBOE_CONFIG0L_SIRTEST 0x04 /*Enable SIR framer in MIR and FIR */ +#define OBOE_CONFIG0L_INVERTTX 0x02 /*Invert Tx Line */ +#define OBOE_CONFIG0L_INVERTRX 0x01 /*Invert Rx Line */ + +#define OBOE_BOF OBOE_REG(0x12) +#define OBOE_EOF OBOE_REG(0x13) + +#define OBOE_ENABLEL OBOE_REG(0x14) +#define OBOE_ENABLEH OBOE_REG(0x15) + +#define OBOE_ENABLEH_PHYANDCLOCK 0x80 /*Toggle low to copy config in */ +#define OBOE_ENABLEH_CONFIGERR 0x40 +#define OBOE_ENABLEH_FIRON 0x20 +#define OBOE_ENABLEH_MIRON 0x10 +#define OBOE_ENABLEH_SIRON 0x08 +#define OBOE_ENABLEH_ENTX 0x04 +#define OBOE_ENABLEH_ENRX 0x02 +#define OBOE_ENABLEH_CRC16 0x01 + +#define OBOE_ENABLEL_BROADCAST 0x01 + +#define OBOE_CURR_PCONFIGL OBOE_REG(0x16) /*Current config */ +#define OBOE_CURR_PCONFIGH OBOE_REG(0x17) + +#define OBOE_NEW_PCONFIGL OBOE_REG(0x18) +#define OBOE_NEW_PCONFIGH OBOE_REG(0x19) + +#define OBOE_PCONFIGH_BAUDMASK 0xfc +#define OBOE_PCONFIGH_WIDTHMASK 0x04 +#define OBOE_PCONFIGL_WIDTHMASK 0xe0 +#define OBOE_PCONFIGL_PREAMBLEMASK 0x1f + +#define OBOE_PCONFIG_BAUDMASK 0xfc00 +#define OBOE_PCONFIG_BAUDSHIFT 10 +#define OBOE_PCONFIG_WIDTHMASK 0x04e0 +#define OBOE_PCONFIG_WIDTHSHIFT 5 +#define OBOE_PCONFIG_PREAMBLEMASK 0x001f +#define OBOE_PCONFIG_PREAMBLESHIFT 0 + +#define OBOE_MAXLENL OBOE_REG(0x1a) +#define OBOE_MAXLENH OBOE_REG(0x1b) + +#define OBOE_RXCOUNTH OBOE_REG(0x1c) /*Reset on recipt */ +#define OBOE_RXCOUNTL OBOE_REG(0x1d) /*of whole packet */ + +/* The PCI ID of the OBOE chip */ +#ifndef PCI_DEVICE_ID_FIR701 +#define PCI_DEVICE_ID_FIR701 0x0701 +#endif + +#ifndef PCI_DEVICE_ID_FIRD01 +#define PCI_DEVICE_ID_FIRD01 0x0d01 +#endif + +struct OboeSlot +{ + __u16 len; /*Tweleve bits of packet length */ + __u8 unused; + __u8 control; /*Slot control/status see below */ + __u32 address; /*Slot buffer address */ +} +__attribute__ ((packed)); + +#define OBOE_NTASKS OBOE_TXRING_OFFSET_IN_SLOTS + +struct OboeRing +{ + struct OboeSlot rx[OBOE_NTASKS]; + struct OboeSlot tx[OBOE_NTASKS]; +}; + +#define OBOE_RING_LEN (sizeof(struct OboeRing)) + + +#define OBOE_CTL_TX_HW_OWNS 0x80 /*W/R This slot owned by the hardware */ +#define OBOE_CTL_TX_DISTX_CRC 0x40 /*W Disable CRC generation for [FM]IR */ +#define OBOE_CTL_TX_BAD_CRC 0x20 /*W Generate bad CRC */ +#define OBOE_CTL_TX_SIP 0x10 /*W Generate an SIP after xmittion */ +#define OBOE_CTL_TX_MKUNDER 0x08 /*W Generate an underrun error */ +#define OBOE_CTL_TX_RTCENTX 0x04 /*W Enable receiver and generate TXdone */ + /* After this slot is processed */ +#define OBOE_CTL_TX_UNDER 0x01 /*R Set by hardware to indicate underrun */ + + +#define OBOE_CTL_RX_HW_OWNS 0x80 /*W/R This slot owned by hardware */ +#define OBOE_CTL_RX_PHYERR 0x40 /*R Decoder error on receiption */ +#define OBOE_CTL_RX_CRCERR 0x20 /*R CRC error only set for [FM]IR */ +#define OBOE_CTL_RX_LENGTH 0x10 /*R Packet > max Rx length */ +#define OBOE_CTL_RX_OVER 0x08 /*R set to indicate an overflow */ +#define OBOE_CTL_RX_SIRBAD 0x04 /*R SIR had BOF in packet or ABORT sequence */ +#define OBOE_CTL_RX_RXEOF 0x02 /*R Finished receiving on this slot */ + + +struct toshoboe_cb +{ + struct net_device *netdev; /* Yes! we are some kind of netdevice */ + struct net_device_stats stats; + struct tty_driver ttydev; + + struct irlap_cb *irlap; /* The link layer we are binded to */ + + chipio_t io; /* IrDA controller information */ + struct qos_info qos; /* QoS capabilities for this device */ + + __u32 flags; /* Interface flags */ + + struct pci_dev *pdev; /*PCI device */ + int base; /*IO base */ + + + int txpending; /*how many tx's are pending */ + int txs, rxs; /*Which slots are we at */ + + int irdad; /*Driver under control of netdev end */ + int async; /*Driver under control of async end */ + + + int stopped; /*Stopped by some or other APM stuff */ + + int filter; /*In SIR mode do we want to receive + frames or byte ranges */ + + void *ringbuf; /*The ring buffer */ + struct OboeRing *ring; /*The ring */ + + void *tx_bufs[OBOE_RING_MAX_SIZE]; /*The buffers */ + void *rx_bufs[OBOE_RING_MAX_SIZE]; + + + int speed; /*Current setting of the speed */ + int new_speed; /*Set to request a speed change */ + +/* The spinlock protect critical parts of the driver. + * Locking is done like this : + * spin_lock_irqsave(&self->spinlock, flags); + * Releasing the lock : + * spin_unlock_irqrestore(&self->spinlock, flags); + */ + spinlock_t spinlock; + /* Used for the probe and diagnostics code */ + int int_rx; + int int_tx; + int int_txunder; + int int_rxover; + int int_sip; +}; + + +#endif diff --git a/drivers/net/irda/ep7211_ir.c b/drivers/net/irda/ep7211_ir.c new file mode 100644 index 000000000000..31896262d21c --- /dev/null +++ b/drivers/net/irda/ep7211_ir.c @@ -0,0 +1,122 @@ +/* + * IR port driver for the Cirrus Logic EP7211 processor. + * + * Copyright 2001, Blue Mug Inc. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/init.h> + +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> + +#include <asm/io.h> +#include <asm/hardware.h> + +#define MIN_DELAY 25 /* 15 us, but wait a little more to be sure */ +#define MAX_DELAY 10000 /* 1 ms */ + +static void ep7211_ir_open(dongle_t *self, struct qos_info *qos); +static void ep7211_ir_close(dongle_t *self); +static int ep7211_ir_change_speed(struct irda_task *task); +static int ep7211_ir_reset(struct irda_task *task); + +static struct dongle_reg dongle = { + .type = IRDA_EP7211_IR, + .open = ep7211_ir_open, + .close = ep7211_ir_close, + .reset = ep7211_ir_reset, + .change_speed = ep7211_ir_change_speed, + .owner = THIS_MODULE, +}; + +static void ep7211_ir_open(dongle_t *self, struct qos_info *qos) +{ + unsigned int syscon1, flags; + + save_flags(flags); cli(); + + /* Turn on the SIR encoder. */ + syscon1 = clps_readl(SYSCON1); + syscon1 |= SYSCON1_SIREN; + clps_writel(syscon1, SYSCON1); + + /* XXX: We should disable modem status interrupts on the first + UART (interrupt #14). */ + + restore_flags(flags); +} + +static void ep7211_ir_close(dongle_t *self) +{ + unsigned int syscon1, flags; + + save_flags(flags); cli(); + + /* Turn off the SIR encoder. */ + syscon1 = clps_readl(SYSCON1); + syscon1 &= ~SYSCON1_SIREN; + clps_writel(syscon1, SYSCON1); + + /* XXX: If we've disabled the modem status interrupts, we should + reset them back to their original state. */ + + restore_flags(flags); +} + +/* + * Function ep7211_ir_change_speed (task) + * + * Change speed of the EP7211 I/R port. We don't really have to do anything + * for the EP7211 as long as the rate is being changed at the serial port + * level. + */ +static int ep7211_ir_change_speed(struct irda_task *task) +{ + irda_task_next_state(task, IRDA_TASK_DONE); + return 0; +} + +/* + * Function ep7211_ir_reset (task) + * + * Reset the EP7211 I/R. We don't really have to do anything. + * + */ +static int ep7211_ir_reset(struct irda_task *task) +{ + irda_task_next_state(task, IRDA_TASK_DONE); + return 0; +} + +/* + * Function ep7211_ir_init(void) + * + * Initialize EP7211 I/R module + * + */ +static int __init ep7211_ir_init(void) +{ + return irda_device_register_dongle(&dongle); +} + +/* + * Function ep7211_ir_cleanup(void) + * + * Cleanup EP7211 I/R module + * + */ +static void __exit ep7211_ir_cleanup(void) +{ + irda_device_unregister_dongle(&dongle); +} + +MODULE_AUTHOR("Jon McClintock <jonm@bluemug.com>"); +MODULE_DESCRIPTION("EP7211 I/R driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-8"); /* IRDA_EP7211_IR */ + +module_init(ep7211_ir_init); +module_exit(ep7211_ir_cleanup); diff --git a/drivers/net/irda/esi-sir.c b/drivers/net/irda/esi-sir.c new file mode 100644 index 000000000000..a908df7c4b9d --- /dev/null +++ b/drivers/net/irda/esi-sir.c @@ -0,0 +1,159 @@ +/********************************************************************* + * + * Filename: esi.c + * Version: 1.6 + * Description: Driver for the Extended Systems JetEye PC dongle + * Status: Experimental. + * Author: Dag Brattli <dagb@cs.uit.no> + * Created at: Sat Feb 21 18:54:38 1998 + * Modified at: Sun Oct 27 22:01:04 2002 + * Modified by: Martin Diehl <mad@mdiehl.de> + * + * Copyright (c) 1999 Dag Brattli, <dagb@cs.uit.no>, + * Copyright (c) 1998 Thomas Davis, <ratbert@radiks.net>, + * Copyright (c) 2002 Martin Diehl, <mad@mdiehl.de>, + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> + +#include <net/irda/irda.h> + +#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; + + /* Power up and set dongle to 9600 baud */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + + 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); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int esi_close(struct sir_dev *dev) +{ + /* Power off dongle */ + sirdev_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 + * Apparently (see old esi-driver) no delays are needed here... + * + */ +static int esi_change_speed(struct sir_dev *dev, unsigned speed) +{ + int ret = 0; + int dtr, rts; + + switch (speed) { + case 19200: + dtr = TRUE; + rts = FALSE; + break; + case 115200: + dtr = rts = TRUE; + break; + default: + ret = -EINVAL; + speed = 9600; + /* fall through */ + case 9600: + dtr = FALSE; + rts = TRUE; + break; + } + + /* Change speed of dongle */ + sirdev_set_dtr_rts(dev, dtr, rts); + dev->speed = speed; + + return ret; +} + +/* + * Function esi_reset (task) + * + * Reset dongle; + * + */ +static int esi_reset(struct sir_dev *dev) +{ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + /* Hm, the old esi-driver left the dongle unpowered relying on + * the following speed change to repower. This might work for + * the esi because we only need the modem lines. However, now the + * general rule is reset must bring the dongle to some working + * well-known state because speed change might write to registers. + * The old esi-driver didn't any delay here - let's hope it' fine. + */ + + sirdev_set_dtr_rts(dev, FALSE, TRUE); + dev->speed = 9600; + + return 0; +} + +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("Extended Systems JetEye PC dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-1"); /* IRDA_ESI_DONGLE */ + +module_init(esi_sir_init); +module_exit(esi_sir_cleanup); + diff --git a/drivers/net/irda/esi.c b/drivers/net/irda/esi.c new file mode 100644 index 000000000000..d3a61af6402d --- /dev/null +++ b/drivers/net/irda/esi.c @@ -0,0 +1,149 @@ +/********************************************************************* + * + * Filename: esi.c + * Version: 1.5 + * Description: Driver for the Extended Systems JetEye PC dongle + * Status: Experimental. + * Author: Dag Brattli <dagb@cs.uit.no> + * Created at: Sat Feb 21 18:54:38 1998 + * Modified at: Fri Dec 17 09:14:04 1999 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1999 Dag Brattli, <dagb@cs.uit.no>, + * Copyright (c) 1998 Thomas Davis, <ratbert@radiks.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 + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/init.h> + +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> + +static void esi_open(dongle_t *self, struct qos_info *qos); +static void esi_close(dongle_t *self); +static int esi_change_speed(struct irda_task *task); +static int esi_reset(struct irda_task *task); + +static struct dongle_reg dongle = { + .type = IRDA_ESI_DONGLE, + .open = esi_open, + .close = esi_close, + .reset = esi_reset, + .change_speed = esi_change_speed, + .owner = THIS_MODULE, +}; + +static int __init esi_init(void) +{ + return irda_device_register_dongle(&dongle); +} + +static void __exit esi_cleanup(void) +{ + irda_device_unregister_dongle(&dongle); +} + +static void esi_open(dongle_t *self, struct qos_info *qos) +{ + qos->baud_rate.bits &= IR_9600|IR_19200|IR_115200; + qos->min_turn_time.bits = 0x01; /* Needs at least 10 ms */ +} + +static void esi_close(dongle_t *dongle) +{ + /* Power off dongle */ + dongle->set_dtr_rts(dongle->dev, FALSE, FALSE); +} + +/* + * Function esi_change_speed (task) + * + * Set the speed for the Extended Systems JetEye PC ESI-9680 type dongle + * + */ +static int esi_change_speed(struct irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + __u32 speed = (__u32) task->param; + int dtr, rts; + + switch (speed) { + case 19200: + dtr = TRUE; + rts = FALSE; + break; + case 115200: + dtr = rts = TRUE; + break; + case 9600: + default: + dtr = FALSE; + rts = TRUE; + break; + } + + /* Change speed of dongle */ + self->set_dtr_rts(self->dev, dtr, rts); + self->speed = speed; + + irda_task_next_state(task, IRDA_TASK_DONE); + + return 0; +} + +/* + * Function esi_reset (task) + * + * Reset dongle; + * + */ +static int esi_reset(struct irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + + self->set_dtr_rts(self->dev, FALSE, FALSE); + irda_task_next_state(task, IRDA_TASK_DONE); + + return 0; +} + +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("Extended Systems JetEye PC dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-1"); /* IRDA_ESI_DONGLE */ + +/* + * Function init_module (void) + * + * Initialize ESI module + * + */ +module_init(esi_init); + +/* + * Function cleanup_module (void) + * + * Cleanup ESI module + * + */ +module_exit(esi_cleanup); + diff --git a/drivers/net/irda/girbil-sir.c b/drivers/net/irda/girbil-sir.c new file mode 100644 index 000000000000..0d2fe87fb9b7 --- /dev/null +++ b/drivers/net/irda/girbil-sir.c @@ -0,0 +1,258 @@ +/********************************************************************* + * + * Filename: girbil.c + * Version: 1.2 + * Description: Implementation for the Greenwich GIrBIL dongle + * Status: Experimental. + * Author: Dag Brattli <dagb@cs.uit.no> + * Created at: Sat Feb 6 21:02:33 1999 + * Modified at: Fri Dec 17 09:13:20 1999 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsų admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> + +#include <net/irda/irda.h> + +#include "sir-dev.h" + +static int girbil_reset(struct sir_dev *dev); +static int girbil_open(struct sir_dev *dev); +static int girbil_close(struct sir_dev *dev); +static int girbil_change_speed(struct sir_dev *dev, unsigned speed); + +/* Control register 1 */ +#define GIRBIL_TXEN 0x01 /* Enable transmitter */ +#define GIRBIL_RXEN 0x02 /* Enable receiver */ +#define GIRBIL_ECAN 0x04 /* Cancel self emmited data */ +#define GIRBIL_ECHO 0x08 /* Echo control characters */ + +/* LED Current Register (0x2) */ +#define GIRBIL_HIGH 0x20 +#define GIRBIL_MEDIUM 0x21 +#define GIRBIL_LOW 0x22 + +/* Baud register (0x3) */ +#define GIRBIL_2400 0x30 +#define GIRBIL_4800 0x31 +#define GIRBIL_9600 0x32 +#define GIRBIL_19200 0x33 +#define GIRBIL_38400 0x34 +#define GIRBIL_57600 0x35 +#define GIRBIL_115200 0x36 + +/* Mode register (0x4) */ +#define GIRBIL_IRDA 0x40 +#define GIRBIL_ASK 0x41 + +/* Control register 2 (0x5) */ +#define GIRBIL_LOAD 0x51 /* Load the new baud rate value */ + +static struct dongle_driver girbil = { + .owner = THIS_MODULE, + .driver_name = "Greenwich GIrBIL", + .type = IRDA_GIRBIL_DONGLE, + .open = girbil_open, + .close = girbil_close, + .reset = girbil_reset, + .set_speed = girbil_change_speed, +}; + +static int __init girbil_sir_init(void) +{ + return irda_register_dongle(&girbil); +} + +static void __exit girbil_sir_cleanup(void) +{ + irda_unregister_dongle(&girbil); +} + +static int girbil_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* Power on dongle */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + qos->min_turn_time.bits = 0x03; + irda_qos_bits_to_value(qos); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int girbil_close(struct sir_dev *dev) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* Power off dongle */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function girbil_change_speed (dev, speed) + * + * Set the speed for the Girbil type dongle. + * + */ + +#define GIRBIL_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED + 1) + +static int girbil_change_speed(struct sir_dev *dev, unsigned speed) +{ + unsigned state = dev->fsm.substate; + unsigned delay = 0; + u8 control[2]; + static int ret = 0; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* dongle alread reset - port and dongle at default speed */ + + switch(state) { + + case SIRDEV_STATE_DONGLE_SPEED: + + /* Set DTR and Clear RTS to enter command mode */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + + udelay(25); /* better wait a little while */ + + ret = 0; + switch (speed) { + default: + ret = -EINVAL; + /* fall through */ + case 9600: + control[0] = GIRBIL_9600; + break; + case 19200: + control[0] = GIRBIL_19200; + break; + case 34800: + control[0] = GIRBIL_38400; + break; + case 57600: + control[0] = GIRBIL_57600; + break; + case 115200: + control[0] = GIRBIL_115200; + break; + } + control[1] = GIRBIL_LOAD; + + /* Write control bytes */ + sirdev_raw_write(dev, control, 2); + + dev->speed = speed; + + state = GIRBIL_STATE_WAIT_SPEED; + delay = 100; + break; + + case GIRBIL_STATE_WAIT_SPEED: + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + udelay(25); /* better wait a little while */ + break; + + default: + IRDA_ERROR("%s - undefined state %d\n", __FUNCTION__, state); + ret = -EINVAL; + break; + } + dev->fsm.substate = state; + return (delay > 0) ? delay : ret; +} + +/* + * Function girbil_reset (driver) + * + * This function resets the girbil dongle. + * + * Algorithm: + * 0. set RTS, and wait at least 5 ms + * 1. clear RTS + */ + + +#define GIRBIL_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET + 1) +#define GIRBIL_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET + 2) +#define GIRBIL_STATE_WAIT3_RESET (SIRDEV_STATE_DONGLE_RESET + 3) + +static int girbil_reset(struct sir_dev *dev) +{ + unsigned state = dev->fsm.substate; + unsigned delay = 0; + u8 control = GIRBIL_TXEN | GIRBIL_RXEN; + int ret = 0; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + switch (state) { + case SIRDEV_STATE_DONGLE_RESET: + /* Reset dongle */ + sirdev_set_dtr_rts(dev, TRUE, FALSE); + /* Sleep at least 5 ms */ + delay = 20; + state = GIRBIL_STATE_WAIT1_RESET; + break; + + case GIRBIL_STATE_WAIT1_RESET: + /* Set DTR and clear RTS to enter command mode */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + delay = 20; + state = GIRBIL_STATE_WAIT2_RESET; + break; + + case GIRBIL_STATE_WAIT2_RESET: + /* Write control byte */ + sirdev_raw_write(dev, &control, 1); + delay = 20; + state = GIRBIL_STATE_WAIT3_RESET; + break; + + case GIRBIL_STATE_WAIT3_RESET: + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + dev->speed = 9600; + break; + + default: + IRDA_ERROR("%s(), undefined state %d\n", __FUNCTION__, state); + ret = -1; + break; + } + dev->fsm.substate = state; + return (delay > 0) ? delay : ret; +} + +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("Greenwich GIrBIL dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-4"); /* IRDA_GIRBIL_DONGLE */ + +module_init(girbil_sir_init); +module_exit(girbil_sir_cleanup); diff --git a/drivers/net/irda/girbil.c b/drivers/net/irda/girbil.c new file mode 100644 index 000000000000..248aeb0c726c --- /dev/null +++ b/drivers/net/irda/girbil.c @@ -0,0 +1,250 @@ +/********************************************************************* + * + * Filename: girbil.c + * Version: 1.2 + * Description: Implementation for the Greenwich GIrBIL dongle + * Status: Experimental. + * Author: Dag Brattli <dagb@cs.uit.no> + * Created at: Sat Feb 6 21:02:33 1999 + * Modified at: Fri Dec 17 09:13:20 1999 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsų admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/init.h> + +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> + +static int girbil_reset(struct irda_task *task); +static void girbil_open(dongle_t *self, struct qos_info *qos); +static void girbil_close(dongle_t *self); +static int girbil_change_speed(struct irda_task *task); + +/* Control register 1 */ +#define GIRBIL_TXEN 0x01 /* Enable transmitter */ +#define GIRBIL_RXEN 0x02 /* Enable receiver */ +#define GIRBIL_ECAN 0x04 /* Cancel self emmited data */ +#define GIRBIL_ECHO 0x08 /* Echo control characters */ + +/* LED Current Register (0x2) */ +#define GIRBIL_HIGH 0x20 +#define GIRBIL_MEDIUM 0x21 +#define GIRBIL_LOW 0x22 + +/* Baud register (0x3) */ +#define GIRBIL_2400 0x30 +#define GIRBIL_4800 0x31 +#define GIRBIL_9600 0x32 +#define GIRBIL_19200 0x33 +#define GIRBIL_38400 0x34 +#define GIRBIL_57600 0x35 +#define GIRBIL_115200 0x36 + +/* Mode register (0x4) */ +#define GIRBIL_IRDA 0x40 +#define GIRBIL_ASK 0x41 + +/* Control register 2 (0x5) */ +#define GIRBIL_LOAD 0x51 /* Load the new baud rate value */ + +static struct dongle_reg dongle = { + .type = IRDA_GIRBIL_DONGLE, + .open = girbil_open, + .close = girbil_close, + .reset = girbil_reset, + .change_speed = girbil_change_speed, + .owner = THIS_MODULE, +}; + +static int __init girbil_init(void) +{ + return irda_device_register_dongle(&dongle); +} + +static void __exit girbil_cleanup(void) +{ + irda_device_unregister_dongle(&dongle); +} + +static void girbil_open(dongle_t *self, struct qos_info *qos) +{ + qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + qos->min_turn_time.bits = 0x03; +} + +static void girbil_close(dongle_t *self) +{ + /* Power off dongle */ + self->set_dtr_rts(self->dev, FALSE, FALSE); +} + +/* + * Function girbil_change_speed (dev, speed) + * + * Set the speed for the Girbil type dongle. + * + */ +static int girbil_change_speed(struct irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + __u32 speed = (__u32) task->param; + __u8 control[2]; + int ret = 0; + + self->speed_task = task; + + switch (task->state) { + case IRDA_TASK_INIT: + /* Need to reset the dongle and go to 9600 bps before + programming */ + if (irda_task_execute(self, girbil_reset, NULL, task, + (void *) speed)) + { + /* Dongle need more time to reset */ + irda_task_next_state(task, IRDA_TASK_CHILD_WAIT); + + /* Give reset 1 sec to finish */ + ret = msecs_to_jiffies(1000); + } + break; + case IRDA_TASK_CHILD_WAIT: + IRDA_WARNING("%s(), resetting dongle timed out!\n", + __FUNCTION__); + ret = -1; + break; + case IRDA_TASK_CHILD_DONE: + /* Set DTR and Clear RTS to enter command mode */ + self->set_dtr_rts(self->dev, FALSE, TRUE); + + switch (speed) { + case 9600: + default: + control[0] = GIRBIL_9600; + break; + case 19200: + control[0] = GIRBIL_19200; + break; + case 34800: + control[0] = GIRBIL_38400; + break; + case 57600: + control[0] = GIRBIL_57600; + break; + case 115200: + control[0] = GIRBIL_115200; + break; + } + control[1] = GIRBIL_LOAD; + + /* Write control bytes */ + self->write(self->dev, control, 2); + irda_task_next_state(task, IRDA_TASK_WAIT); + ret = msecs_to_jiffies(100); + break; + case IRDA_TASK_WAIT: + /* Go back to normal mode */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + irda_task_next_state(task, IRDA_TASK_DONE); + self->speed_task = NULL; + break; + default: + IRDA_ERROR("%s(), unknown state %d\n", + __FUNCTION__, task->state); + irda_task_next_state(task, IRDA_TASK_DONE); + self->speed_task = NULL; + ret = -1; + break; + } + return ret; +} + +/* + * Function girbil_reset (driver) + * + * This function resets the girbil dongle. + * + * Algorithm: + * 0. set RTS, and wait at least 5 ms + * 1. clear RTS + */ +static int girbil_reset(struct irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + __u8 control = GIRBIL_TXEN | GIRBIL_RXEN; + int ret = 0; + + self->reset_task = task; + + switch (task->state) { + case IRDA_TASK_INIT: + /* Reset dongle */ + self->set_dtr_rts(self->dev, TRUE, FALSE); + irda_task_next_state(task, IRDA_TASK_WAIT1); + /* Sleep at least 5 ms */ + ret = msecs_to_jiffies(20); + break; + case IRDA_TASK_WAIT1: + /* Set DTR and clear RTS to enter command mode */ + self->set_dtr_rts(self->dev, FALSE, TRUE); + irda_task_next_state(task, IRDA_TASK_WAIT2); + ret = msecs_to_jiffies(20); + break; + case IRDA_TASK_WAIT2: + /* Write control byte */ + self->write(self->dev, &control, 1); + irda_task_next_state(task, IRDA_TASK_WAIT3); + ret = msecs_to_jiffies(20); + break; + case IRDA_TASK_WAIT3: + /* Go back to normal mode */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + irda_task_next_state(task, IRDA_TASK_DONE); + self->reset_task = NULL; + break; + default: + IRDA_ERROR("%s(), unknown state %d\n", + __FUNCTION__, task->state); + irda_task_next_state(task, IRDA_TASK_DONE); + self->reset_task = NULL; + ret = -1; + break; + } + return ret; +} + +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("Greenwich GIrBIL dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-4"); /* IRDA_GIRBIL_DONGLE */ + +/* + * Function init_module (void) + * + * Initialize Girbil module + * + */ +module_init(girbil_init); + +/* + * Function cleanup_module (void) + * + * Cleanup Girbil module + * + */ +module_exit(girbil_cleanup); + diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c new file mode 100644 index 000000000000..46e0022d3258 --- /dev/null +++ b/drivers/net/irda/irda-usb.c @@ -0,0 +1,1602 @@ +/***************************************************************************** + * + * Filename: irda-usb.c + * Version: 0.9b + * Description: IrDA-USB Driver + * Status: Experimental + * Author: Dag Brattli <dag@brattli.net> + * + * Copyright (C) 2000, Roman Weissgaerber <weissg@vienna.at> + * Copyright (C) 2001, Dag Brattli <dag@brattli.net> + * Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +/* + * IMPORTANT NOTE + * -------------- + * + * As of kernel 2.5.20, this is the state of compliance and testing of + * this driver (irda-usb) with regards to the USB low level drivers... + * + * This driver has been tested SUCCESSFULLY with the following drivers : + * o usb-uhci-hcd (For Intel/Via USB controllers) + * o uhci-hcd (Alternate/JE driver for Intel/Via USB controllers) + * o ohci-hcd (For other USB controllers) + * + * This driver has NOT been tested with the following drivers : + * o ehci-hcd (USB 2.0 controllers) + * + * Note that all HCD drivers do URB_ZERO_PACKET and timeout properly, + * so we don't have to worry about that anymore. + * One common problem is the failure to set the address on the dongle, + * but this happens before the driver gets loaded... + * + * Jean II + */ + +/*------------------------------------------------------------------*/ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/slab.h> +#include <linux/rtnetlink.h> +#include <linux/usb.h> + +#include "irda-usb.h" + +/*------------------------------------------------------------------*/ + +static int qos_mtt_bits = 0; + +/* These are the currently known IrDA USB dongles. Add new dongles here */ +static struct usb_device_id dongles[] = { + /* ACTiSYS Corp., ACT-IR2000U FIR-USB Adapter */ + { USB_DEVICE(0x9c4, 0x011), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, + /* Look like ACTiSYS, Report : IBM Corp., IBM UltraPort IrDA */ + { USB_DEVICE(0x4428, 0x012), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, + /* KC Technology Inc., KC-180 USB IrDA Device */ + { USB_DEVICE(0x50f, 0x180), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, + /* Extended Systems, Inc., XTNDAccess IrDA USB (ESI-9685) */ + { USB_DEVICE(0x8e9, 0x100), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, + { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | + USB_DEVICE_ID_MATCH_INT_SUBCLASS, + .bInterfaceClass = USB_CLASS_APP_SPEC, + .bInterfaceSubClass = USB_CLASS_IRDA, + .driver_info = IUC_DEFAULT, }, + { }, /* The end */ +}; + +/* + * Important note : + * Devices based on the SigmaTel chipset (0x66f, 0x4200) are not designed + * using the "USB-IrDA specification" (yes, there exist such a thing), and + * therefore not supported by this driver (don't add them above). + * There is a Linux driver, stir4200, that support those USB devices. + * Jean II + */ + +MODULE_DEVICE_TABLE(usb, dongles); + +/*------------------------------------------------------------------*/ + +static struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf); +static void irda_usb_disconnect(struct usb_interface *intf); +static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self); +static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *dev); +static int irda_usb_open(struct irda_usb_cb *self); +static void irda_usb_close(struct irda_usb_cb *self); +static void speed_bulk_callback(struct urb *urb, struct pt_regs *regs); +static void write_bulk_callback(struct urb *urb, struct pt_regs *regs); +static void irda_usb_receive(struct urb *urb, struct pt_regs *regs); +static int irda_usb_net_open(struct net_device *dev); +static int irda_usb_net_close(struct net_device *dev); +static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static void irda_usb_net_timeout(struct net_device *dev); +static struct net_device_stats *irda_usb_net_get_stats(struct net_device *dev); + +/************************ TRANSMIT ROUTINES ************************/ +/* + * Receive packets from the IrDA stack and send them on the USB pipe. + * Handle speed change, timeout and lot's of ugliness... + */ + +/*------------------------------------------------------------------*/ +/* + * Function irda_usb_build_header(self, skb, header) + * + * Builds USB-IrDA outbound header + * + * When we send an IrDA frame over an USB pipe, we add to it a 1 byte + * header. This function create this header with the proper values. + * + * Important note : the USB-IrDA spec 1.0 say very clearly in chapter 5.4.2.2 + * that the setting of the link speed and xbof number in this outbound header + * should be applied *AFTER* the frame has been sent. + * Unfortunately, some devices are not compliant with that... It seems that + * reading the spec is far too difficult... + * Jean II + */ +static void irda_usb_build_header(struct irda_usb_cb *self, + __u8 *header, + int force) +{ + /* Set the negotiated link speed */ + if (self->new_speed != -1) { + /* Hum... Ugly hack :-( + * Some device are not compliant with the spec and change + * parameters *before* sending the frame. - Jean II + */ + if ((self->capability & IUC_SPEED_BUG) && + (!force) && (self->speed != -1)) { + /* No speed and xbofs change here + * (we'll do it later in the write callback) */ + IRDA_DEBUG(2, "%s(), not changing speed yet\n", __FUNCTION__); + *header = 0; + return; + } + + IRDA_DEBUG(2, "%s(), changing speed to %d\n", __FUNCTION__, self->new_speed); + self->speed = self->new_speed; + /* We will do ` self->new_speed = -1; ' in the completion + * handler just in case the current URB fail - Jean II */ + + switch (self->speed) { + case 2400: + *header = SPEED_2400; + break; + default: + case 9600: + *header = SPEED_9600; + break; + case 19200: + *header = SPEED_19200; + break; + case 38400: + *header = SPEED_38400; + break; + case 57600: + *header = SPEED_57600; + break; + case 115200: + *header = SPEED_115200; + break; + case 576000: + *header = SPEED_576000; + break; + case 1152000: + *header = SPEED_1152000; + break; + case 4000000: + *header = SPEED_4000000; + self->new_xbofs = 0; + break; + } + } else + /* No change */ + *header = 0; + + /* Set the negotiated additional XBOFS */ + if (self->new_xbofs != -1) { + IRDA_DEBUG(2, "%s(), changing xbofs to %d\n", __FUNCTION__, self->new_xbofs); + self->xbofs = self->new_xbofs; + /* We will do ` self->new_xbofs = -1; ' in the completion + * handler just in case the current URB fail - Jean II */ + + switch (self->xbofs) { + case 48: + *header |= 0x10; + break; + case 28: + case 24: /* USB spec 1.0 says 24 */ + *header |= 0x20; + break; + default: + case 12: + *header |= 0x30; + break; + case 5: /* Bug in IrLAP spec? (should be 6) */ + case 6: + *header |= 0x40; + break; + case 3: + *header |= 0x50; + break; + case 2: + *header |= 0x60; + break; + case 1: + *header |= 0x70; + break; + case 0: + *header |= 0x80; + break; + } + } +} + +/*------------------------------------------------------------------*/ +/* + * Send a command to change the speed of the dongle + * Need to be called with spinlock on. + */ +static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self) +{ + __u8 *frame; + struct urb *urb; + int ret; + + IRDA_DEBUG(2, "%s(), speed=%d, xbofs=%d\n", __FUNCTION__, + self->new_speed, self->new_xbofs); + + /* Grab the speed URB */ + urb = self->speed_urb; + if (urb->status != 0) { + IRDA_WARNING("%s(), URB still in use!\n", __FUNCTION__); + return; + } + + /* Allocate the fake frame */ + frame = self->speed_buff; + + /* Set the new speed and xbofs in this fake frame */ + irda_usb_build_header(self, frame, 1); + + /* Submit the 0 length IrDA frame to trigger new speed settings */ + usb_fill_bulk_urb(urb, self->usbdev, + usb_sndbulkpipe(self->usbdev, self->bulk_out_ep), + frame, IRDA_USB_SPEED_MTU, + speed_bulk_callback, self); + urb->transfer_buffer_length = USB_IRDA_HEADER; + urb->transfer_flags = URB_ASYNC_UNLINK; + + /* Irq disabled -> GFP_ATOMIC */ + if ((ret = usb_submit_urb(urb, GFP_ATOMIC))) { + IRDA_WARNING("%s(), failed Speed URB\n", __FUNCTION__); + } +} + +/*------------------------------------------------------------------*/ +/* + * Speed URB callback + * Now, we can only get called for the speed URB. + */ +static void speed_bulk_callback(struct urb *urb, struct pt_regs *regs) +{ + struct irda_usb_cb *self = urb->context; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* We should always have a context */ + IRDA_ASSERT(self != NULL, return;); + /* We should always be called for the speed URB */ + IRDA_ASSERT(urb == self->speed_urb, return;); + + /* Check for timeout and other USB nasties */ + if (urb->status != 0) { + /* I get a lot of -ECONNABORTED = -103 here - Jean II */ + IRDA_DEBUG(0, "%s(), URB complete status %d, transfer_flags 0x%04X\n", __FUNCTION__, urb->status, urb->transfer_flags); + + /* Don't do anything here, that might confuse the USB layer. + * Instead, we will wait for irda_usb_net_timeout(), the + * network layer watchdog, to fix the situation. + * Jean II */ + /* A reset of the dongle might be welcomed here - Jean II */ + return; + } + + /* urb is now available */ + //urb->status = 0; -> tested above + + /* New speed and xbof is now commited in hardware */ + self->new_speed = -1; + self->new_xbofs = -1; + + /* Allow the stack to send more packets */ + netif_wake_queue(self->netdev); +} + +/*------------------------------------------------------------------*/ +/* + * Send an IrDA frame to the USB dongle (for transmission) + */ +static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct irda_usb_cb *self = netdev->priv; + struct urb *urb = self->tx_urb; + unsigned long flags; + s32 speed; + s16 xbofs; + int res, mtt; + int err = 1; /* Failed */ + + IRDA_DEBUG(4, "%s() on %s\n", __FUNCTION__, netdev->name); + + netif_stop_queue(netdev); + + /* Protect us from USB callbacks, net watchdog and else. */ + spin_lock_irqsave(&self->lock, flags); + + /* Check if the device is still there. + * We need to check self->present under the spinlock because + * of irda_usb_disconnect() is synchronous - Jean II */ + if (!self->present) { + IRDA_DEBUG(0, "%s(), Device is gone...\n", __FUNCTION__); + goto drop; + } + + /* Check if we need to change the number of xbofs */ + xbofs = irda_get_next_xbofs(skb); + if ((xbofs != self->xbofs) && (xbofs != -1)) { + self->new_xbofs = xbofs; + } + + /* Check if we need to change the speed */ + speed = irda_get_next_speed(skb); + if ((speed != self->speed) && (speed != -1)) { + /* Set the desired speed */ + self->new_speed = speed; + + /* Check for empty frame */ + if (!skb->len) { + /* IrLAP send us an empty frame to make us change the + * speed. Changing speed with the USB adapter is in + * fact sending an empty frame to the adapter, so we + * could just let the present function do its job. + * However, we would wait for min turn time, + * do an extra memcpy and increment packet counters... + * Jean II */ + irda_usb_change_speed_xbofs(self); + netdev->trans_start = jiffies; + /* Will netif_wake_queue() in callback */ + err = 0; /* No error */ + goto drop; + } + } + + if (urb->status != 0) { + IRDA_WARNING("%s(), URB still in use!\n", __FUNCTION__); + goto drop; + } + + /* Make sure there is room for IrDA-USB header. The actual + * allocation will be done lower in skb_push(). + * Also, we don't use directly skb_cow(), because it require + * headroom >= 16, which force unnecessary copies - Jean II */ + if (skb_headroom(skb) < USB_IRDA_HEADER) { + IRDA_DEBUG(0, "%s(), Insuficient skb headroom.\n", __FUNCTION__); + if (skb_cow(skb, USB_IRDA_HEADER)) { + IRDA_WARNING("%s(), failed skb_cow() !!!\n", __FUNCTION__); + goto drop; + } + } + + /* Change setting for next frame */ + irda_usb_build_header(self, skb_push(skb, USB_IRDA_HEADER), 0); + + /* FIXME: Make macro out of this one */ + ((struct irda_skb_cb *)skb->cb)->context = self; + + usb_fill_bulk_urb(urb, self->usbdev, + usb_sndbulkpipe(self->usbdev, self->bulk_out_ep), + skb->data, IRDA_SKB_MAX_MTU, + write_bulk_callback, skb); + urb->transfer_buffer_length = skb->len; + /* Note : unlink *must* be Asynchronous because of the code in + * irda_usb_net_timeout() -> call in irq - Jean II */ + urb->transfer_flags = URB_ASYNC_UNLINK; + /* This flag (URB_ZERO_PACKET) indicates that what we send is not + * a continuous stream of data but separate packets. + * In this case, the USB layer will insert an empty USB frame (TD) + * after each of our packets that is exact multiple of the frame size. + * This is how the dongle will detect the end of packet - Jean II */ + urb->transfer_flags |= URB_ZERO_PACKET; + + /* Generate min turn time. FIXME: can we do better than this? */ + /* Trying to a turnaround time at this level is trying to measure + * processor clock cycle with a wrist-watch, approximate at best... + * + * What we know is the last time we received a frame over USB. + * Due to latency over USB that depend on the USB load, we don't + * know when this frame was received over IrDA (a few ms before ?) + * Then, same story for our outgoing frame... + * + * In theory, the USB dongle is supposed to handle the turnaround + * by itself (spec 1.0, chater 4, page 6). Who knows ??? That's + * why this code is enabled only for dongles that doesn't meet + * the spec. + * Jean II */ + if (self->capability & IUC_NO_TURN) { + mtt = irda_get_mtt(skb); + if (mtt) { + int diff; + do_gettimeofday(&self->now); + diff = self->now.tv_usec - self->stamp.tv_usec; +#ifdef IU_USB_MIN_RTT + /* Factor in USB delays -> Get rid of udelay() that + * would be lost in the noise - Jean II */ + diff += IU_USB_MIN_RTT; +#endif /* IU_USB_MIN_RTT */ + /* If the usec counter did wraparound, the diff will + * go negative (tv_usec is a long), so we need to + * correct it by one second. Jean II */ + if (diff < 0) + diff += 1000000; + + /* Check if the mtt is larger than the time we have + * already used by all the protocol processing + */ + if (mtt > diff) { + mtt -= diff; + if (mtt > 1000) + mdelay(mtt/1000); + else + udelay(mtt); + } + } + } + + /* Ask USB to send the packet - Irq disabled -> GFP_ATOMIC */ + if ((res = usb_submit_urb(urb, GFP_ATOMIC))) { + IRDA_WARNING("%s(), failed Tx URB\n", __FUNCTION__); + self->stats.tx_errors++; + /* Let USB recover : We will catch that in the watchdog */ + /*netif_start_queue(netdev);*/ + } else { + /* Increment packet stats */ + self->stats.tx_packets++; + self->stats.tx_bytes += skb->len; + + netdev->trans_start = jiffies; + } + spin_unlock_irqrestore(&self->lock, flags); + + return 0; + +drop: + /* Drop silently the skb and exit */ + dev_kfree_skb(skb); + spin_unlock_irqrestore(&self->lock, flags); + return err; /* Usually 1 */ +} + +/*------------------------------------------------------------------*/ +/* + * Note : this function will be called only for tx_urb... + */ +static void write_bulk_callback(struct urb *urb, struct pt_regs *regs) +{ + unsigned long flags; + struct sk_buff *skb = urb->context; + struct irda_usb_cb *self = ((struct irda_skb_cb *) skb->cb)->context; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* We should always have a context */ + IRDA_ASSERT(self != NULL, return;); + /* We should always be called for the speed URB */ + IRDA_ASSERT(urb == self->tx_urb, return;); + + /* Free up the skb */ + dev_kfree_skb_any(skb); + urb->context = NULL; + + /* Check for timeout and other USB nasties */ + if (urb->status != 0) { + /* I get a lot of -ECONNABORTED = -103 here - Jean II */ + IRDA_DEBUG(0, "%s(), URB complete status %d, transfer_flags 0x%04X\n", __FUNCTION__, urb->status, urb->transfer_flags); + + /* Don't do anything here, that might confuse the USB layer, + * and we could go in recursion and blow the kernel stack... + * Instead, we will wait for irda_usb_net_timeout(), the + * network layer watchdog, to fix the situation. + * Jean II */ + /* A reset of the dongle might be welcomed here - Jean II */ + return; + } + + /* urb is now available */ + //urb->status = 0; -> tested above + + /* Make sure we read self->present properly */ + spin_lock_irqsave(&self->lock, flags); + + /* If the network is closed, stop everything */ + if ((!self->netopen) || (!self->present)) { + IRDA_DEBUG(0, "%s(), Network is gone...\n", __FUNCTION__); + spin_unlock_irqrestore(&self->lock, flags); + return; + } + + /* If changes to speed or xbofs is pending... */ + if ((self->new_speed != -1) || (self->new_xbofs != -1)) { + if ((self->new_speed != self->speed) || + (self->new_xbofs != self->xbofs)) { + /* We haven't changed speed yet (because of + * IUC_SPEED_BUG), so do it now - Jean II */ + IRDA_DEBUG(1, "%s(), Changing speed now...\n", __FUNCTION__); + irda_usb_change_speed_xbofs(self); + } else { + /* New speed and xbof is now commited in hardware */ + self->new_speed = -1; + self->new_xbofs = -1; + /* Done, waiting for next packet */ + netif_wake_queue(self->netdev); + } + } else { + /* Otherwise, allow the stack to send more packets */ + netif_wake_queue(self->netdev); + } + spin_unlock_irqrestore(&self->lock, flags); +} + +/*------------------------------------------------------------------*/ +/* + * Watchdog timer from the network layer. + * After a predetermined timeout, if we don't give confirmation that + * the packet has been sent (i.e. no call to netif_wake_queue()), + * the network layer will call this function. + * Note that URB that we submit have also a timeout. When the URB timeout + * expire, the normal URB callback is called (write_bulk_callback()). + */ +static void irda_usb_net_timeout(struct net_device *netdev) +{ + unsigned long flags; + struct irda_usb_cb *self = netdev->priv; + struct urb *urb; + int done = 0; /* If we have made any progress */ + + IRDA_DEBUG(0, "%s(), Network layer thinks we timed out!\n", __FUNCTION__); + IRDA_ASSERT(self != NULL, return;); + + /* Protect us from USB callbacks, net Tx and else. */ + spin_lock_irqsave(&self->lock, flags); + + /* self->present *MUST* be read under spinlock */ + if (!self->present) { + IRDA_WARNING("%s(), device not present!\n", __FUNCTION__); + netif_stop_queue(netdev); + spin_unlock_irqrestore(&self->lock, flags); + return; + } + + /* Check speed URB */ + urb = self->speed_urb; + if (urb->status != 0) { + IRDA_DEBUG(0, "%s: Speed change timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", netdev->name, urb->status, urb->transfer_flags); + + switch (urb->status) { + case -EINPROGRESS: + usb_unlink_urb(urb); + /* Note : above will *NOT* call netif_wake_queue() + * in completion handler, we will come back here. + * Jean II */ + done = 1; + break; + case -ECONNABORTED: /* -103 */ + case -ECONNRESET: /* -104 */ + case -ETIMEDOUT: /* -110 */ + case -ENOENT: /* -2 (urb unlinked by us) */ + default: /* ??? - Play safe */ + urb->status = 0; + netif_wake_queue(self->netdev); + done = 1; + break; + } + } + + /* Check Tx URB */ + urb = self->tx_urb; + if (urb->status != 0) { + struct sk_buff *skb = urb->context; + + IRDA_DEBUG(0, "%s: Tx timed out, urb->status=%d, urb->transfer_flags=0x%04X\n", netdev->name, urb->status, urb->transfer_flags); + + /* Increase error count */ + self->stats.tx_errors++; + +#ifdef IU_BUG_KICK_TIMEOUT + /* Can't be a bad idea to reset the speed ;-) - Jean II */ + if(self->new_speed == -1) + self->new_speed = self->speed; + if(self->new_xbofs == -1) + self->new_xbofs = self->xbofs; + irda_usb_change_speed_xbofs(self); +#endif /* IU_BUG_KICK_TIMEOUT */ + + switch (urb->status) { + case -EINPROGRESS: + usb_unlink_urb(urb); + /* Note : above will *NOT* call netif_wake_queue() + * in completion handler, because urb->status will + * be -ENOENT. We will fix that at the next watchdog, + * leaving more time to USB to recover... + * Also, we are in interrupt, so we need to have + * URB_ASYNC_UNLINK to work properly... + * Jean II */ + done = 1; + break; + case -ECONNABORTED: /* -103 */ + case -ECONNRESET: /* -104 */ + case -ETIMEDOUT: /* -110 */ + case -ENOENT: /* -2 (urb unlinked by us) */ + default: /* ??? - Play safe */ + if(skb != NULL) { + dev_kfree_skb_any(skb); + urb->context = NULL; + } + urb->status = 0; + netif_wake_queue(self->netdev); + done = 1; + break; + } + } + spin_unlock_irqrestore(&self->lock, flags); + + /* Maybe we need a reset */ + /* Note : Some drivers seem to use a usb_set_interface() when they + * need to reset the hardware. Hum... + */ + + /* if(done == 0) */ +} + +/************************* RECEIVE ROUTINES *************************/ +/* + * Receive packets from the USB layer stack and pass them to the IrDA stack. + * Try to work around USB failures... + */ + +/* + * Note : + * Some of you may have noticed that most dongle have an interrupt in pipe + * that we don't use. Here is the little secret... + * When we hang a Rx URB on the bulk in pipe, it generates some USB traffic + * in every USB frame. This is unnecessary overhead. + * The interrupt in pipe will generate an event every time a packet is + * received. Reading an interrupt pipe adds minimal overhead, but has some + * latency (~1ms). + * If we are connected (speed != 9600), we want to minimise latency, so + * we just always hang the Rx URB and ignore the interrupt. + * If we are not connected (speed == 9600), there is usually no Rx traffic, + * and we want to minimise the USB overhead. In this case we should wait + * on the interrupt pipe and hang the Rx URB only when an interrupt is + * received. + * Jean II + */ + +/*------------------------------------------------------------------*/ +/* + * Submit a Rx URB to the USB layer to handle reception of a frame + * Mostly called by the completion callback of the previous URB. + * + * Jean II + */ +static void irda_usb_submit(struct irda_usb_cb *self, struct sk_buff *skb, struct urb *urb) +{ + struct irda_skb_cb *cb; + int ret; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* This should never happen */ + IRDA_ASSERT(skb != NULL, return;); + IRDA_ASSERT(urb != NULL, return;); + + /* Save ourselves in the skb */ + cb = (struct irda_skb_cb *) skb->cb; + cb->context = self; + + /* Reinitialize URB */ + usb_fill_bulk_urb(urb, self->usbdev, + usb_rcvbulkpipe(self->usbdev, self->bulk_in_ep), + skb->data, skb->truesize, + irda_usb_receive, skb); + /* Note : unlink *must* be synchronous because of the code in + * irda_usb_net_close() -> free the skb - Jean II */ + urb->status = 0; + + /* Can be called from irda_usb_receive (irq handler) -> GFP_ATOMIC */ + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) { + /* If this ever happen, we are in deep s***. + * Basically, the Rx path will stop... */ + IRDA_WARNING("%s(), Failed to submit Rx URB %d\n", + __FUNCTION__, ret); + } +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_usb_receive(urb) + * + * Called by the USB subsystem when a frame has been received + * + */ +static void irda_usb_receive(struct urb *urb, struct pt_regs *regs) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct irda_usb_cb *self; + struct irda_skb_cb *cb; + struct sk_buff *newskb; + struct sk_buff *dataskb; + int docopy; + + IRDA_DEBUG(2, "%s(), len=%d\n", __FUNCTION__, urb->actual_length); + + /* Find ourselves */ + cb = (struct irda_skb_cb *) skb->cb; + IRDA_ASSERT(cb != NULL, return;); + self = (struct irda_usb_cb *) cb->context; + IRDA_ASSERT(self != NULL, return;); + + /* If the network is closed or the device gone, stop everything */ + if ((!self->netopen) || (!self->present)) { + IRDA_DEBUG(0, "%s(), Network is gone!\n", __FUNCTION__); + /* Don't re-submit the URB : will stall the Rx path */ + return; + } + + /* Check the status */ + if (urb->status != 0) { + switch (urb->status) { + case -EILSEQ: + self->stats.rx_errors++; + self->stats.rx_crc_errors++; + break; + case -ECONNRESET: /* -104 */ + IRDA_DEBUG(0, "%s(), Connection Reset (-104), transfer_flags 0x%04X \n", __FUNCTION__, urb->transfer_flags); + /* uhci_cleanup_unlink() is going to kill the Rx + * URB just after we return. No problem, at this + * point the URB will be idle ;-) - Jean II */ + break; + default: + IRDA_DEBUG(0, "%s(), RX status %d,transfer_flags 0x%04X \n", __FUNCTION__, urb->status, urb->transfer_flags); + break; + } + goto done; + } + + /* Check for empty frames */ + if (urb->actual_length <= USB_IRDA_HEADER) { + IRDA_WARNING("%s(), empty frame!\n", __FUNCTION__); + goto done; + } + + /* + * Remember the time we received this frame, so we can + * reduce the min turn time a bit since we will know + * how much time we have used for protocol processing + */ + do_gettimeofday(&self->stamp); + + /* Check if we need to copy the data to a new skb or not. + * For most frames, we use ZeroCopy and pass the already + * allocated skb up the stack. + * If the frame is small, it is more efficient to copy it + * to save memory (copy will be fast anyway - that's + * called Rx-copy-break). Jean II */ + docopy = (urb->actual_length < IRDA_RX_COPY_THRESHOLD); + + /* Allocate a new skb */ + newskb = dev_alloc_skb(docopy ? urb->actual_length : IRDA_SKB_MAX_MTU); + if (!newskb) { + self->stats.rx_dropped++; + /* We could deliver the current skb, but this would stall + * the Rx path. Better drop the packet... Jean II */ + goto done; + } + + /* Make sure IP header get aligned (IrDA header is 5 bytes) */ + /* But IrDA-USB header is 1 byte. Jean II */ + //skb_reserve(newskb, USB_IRDA_HEADER - 1); + + if(docopy) { + /* Copy packet, so we can recycle the original */ + memcpy(newskb->data, skb->data, urb->actual_length); + /* Deliver this new skb */ + dataskb = newskb; + /* And hook the old skb to the URB + * Note : we don't need to "clean up" the old skb, + * as we never touched it. Jean II */ + } else { + /* We are using ZeroCopy. Deliver old skb */ + dataskb = skb; + /* And hook the new skb to the URB */ + skb = newskb; + } + + /* Set proper length on skb & remove USB-IrDA header */ + skb_put(dataskb, urb->actual_length); + skb_pull(dataskb, USB_IRDA_HEADER); + + /* Ask the networking layer to queue the packet for the IrDA stack */ + dataskb->dev = self->netdev; + dataskb->mac.raw = dataskb->data; + dataskb->protocol = htons(ETH_P_IRDA); + netif_rx(dataskb); + + /* Keep stats up to date */ + self->stats.rx_bytes += dataskb->len; + self->stats.rx_packets++; + self->netdev->last_rx = jiffies; + +done: + /* Note : at this point, the URB we've just received (urb) + * is still referenced by the USB layer. For example, if we + * have received a -ECONNRESET, uhci_cleanup_unlink() will + * continue to process it (in fact, cleaning it up). + * If we were to submit this URB, disaster would ensue. + * Therefore, we submit our idle URB, and put this URB in our + * idle slot.... + * Jean II */ + /* Note : with this scheme, we could submit the idle URB before + * processing the Rx URB. Another time... Jean II */ + + /* Submit the idle URB to replace the URB we've just received */ + irda_usb_submit(self, skb, self->idle_rx_urb); + /* Recycle Rx URB : Now, the idle URB is the present one */ + urb->context = NULL; + self->idle_rx_urb = urb; +} + +/*------------------------------------------------------------------*/ +/* + * Callbak from IrDA layer. IrDA wants to know if we have + * started receiving anything. + */ +static int irda_usb_is_receiving(struct irda_usb_cb *self) +{ + /* Note : because of the way UHCI works, it's almost impossible + * to get this info. The Controller DMA directly to memory and + * signal only when the whole frame is finished. To know if the + * first TD of the URB has been filled or not seems hard work... + * + * The other solution would be to use the "receiving" command + * on the default decriptor with a usb_control_msg(), but that + * would add USB traffic and would return result only in the + * next USB frame (~1ms). + * + * I've been told that current dongles send status info on their + * interrupt endpoint, and that's what the Windows driver uses + * to know this info. Unfortunately, this is not yet in the spec... + * + * Jean II + */ + + return 0; /* For now */ +} + +/********************** IRDA DEVICE CALLBACKS **********************/ +/* + * Main calls from the IrDA/Network subsystem. + * Mostly registering a new irda-usb device and removing it.... + * We only deal with the IrDA side of the business, the USB side will + * be dealt with below... + */ + + +/*------------------------------------------------------------------*/ +/* + * Function irda_usb_net_open (dev) + * + * Network device is taken up. Usually this is done by "ifconfig irda0 up" + * + * Note : don't mess with self->netopen - Jean II + */ +static int irda_usb_net_open(struct net_device *netdev) +{ + struct irda_usb_cb *self; + char hwname[16]; + int i; + + IRDA_DEBUG(1, "%s()\n", __FUNCTION__); + + IRDA_ASSERT(netdev != NULL, return -1;); + self = (struct irda_usb_cb *) netdev->priv; + IRDA_ASSERT(self != NULL, return -1;); + + /* Can only open the device if it's there */ + if(!self->present) { + IRDA_WARNING("%s(), device not present!\n", __FUNCTION__); + return -1; + } + + /* Initialise default speed and xbofs value + * (IrLAP will change that soon) */ + self->speed = -1; + self->xbofs = -1; + self->new_speed = -1; + self->new_xbofs = -1; + + /* To do *before* submitting Rx urbs and starting net Tx queue + * Jean II */ + self->netopen = 1; + + /* + * Now that everything should be initialized properly, + * Open new IrLAP layer instance to take care of us... + * Note : will send immediately a speed change... + */ + sprintf(hwname, "usb#%d", self->usbdev->devnum); + self->irlap = irlap_open(netdev, &self->qos, hwname); + IRDA_ASSERT(self->irlap != NULL, return -1;); + + /* Allow IrLAP to send data to us */ + netif_start_queue(netdev); + + /* We submit all the Rx URB except for one that we keep idle. + * Need to be initialised before submitting other USBs, because + * in some cases as soon as we submit the URBs the USB layer + * will trigger a dummy receive - Jean II */ + self->idle_rx_urb = self->rx_urb[IU_MAX_ACTIVE_RX_URBS]; + self->idle_rx_urb->context = NULL; + + /* Now that we can pass data to IrLAP, allow the USB layer + * to send us some data... */ + for (i = 0; i < IU_MAX_ACTIVE_RX_URBS; i++) { + struct sk_buff *skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); + if (!skb) { + /* If this ever happen, we are in deep s***. + * Basically, we can't start the Rx path... */ + IRDA_WARNING("%s(), Failed to allocate Rx skb\n", + __FUNCTION__); + return -1; + } + //skb_reserve(newskb, USB_IRDA_HEADER - 1); + irda_usb_submit(self, skb, self->rx_urb[i]); + } + + /* Ready to play !!! */ + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_usb_net_close (self) + * + * Network device is taken down. Usually this is done by + * "ifconfig irda0 down" + */ +static int irda_usb_net_close(struct net_device *netdev) +{ + struct irda_usb_cb *self; + int i; + + IRDA_DEBUG(1, "%s()\n", __FUNCTION__); + + IRDA_ASSERT(netdev != NULL, return -1;); + self = (struct irda_usb_cb *) netdev->priv; + IRDA_ASSERT(self != NULL, return -1;); + + /* Clear this flag *before* unlinking the urbs and *before* + * stopping the network Tx queue - Jean II */ + self->netopen = 0; + + /* Stop network Tx queue */ + netif_stop_queue(netdev); + + /* Deallocate all the Rx path buffers (URBs and skb) */ + for (i = 0; i < IU_MAX_RX_URBS; i++) { + struct urb *urb = self->rx_urb[i]; + struct sk_buff *skb = (struct sk_buff *) urb->context; + /* Cancel the receive command */ + usb_kill_urb(urb); + /* The skb is ours, free it */ + if(skb) { + dev_kfree_skb(skb); + urb->context = NULL; + } + } + /* Cancel Tx and speed URB - need to be synchronous to avoid races */ + self->tx_urb->transfer_flags &= ~URB_ASYNC_UNLINK; + usb_kill_urb(self->tx_urb); + self->speed_urb->transfer_flags &= ~URB_ASYNC_UNLINK; + usb_kill_urb(self->speed_urb); + + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close(self->irlap); + self->irlap = NULL; + + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * IOCTLs : Extra out-of-band network commands... + */ +static int irda_usb_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + unsigned long flags; + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct irda_usb_cb *self; + int ret = 0; + + IRDA_ASSERT(dev != NULL, return -1;); + self = dev->priv; + IRDA_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)) + return -EPERM; + /* Protect us from USB callbacks, net watchdog and else. */ + spin_lock_irqsave(&self->lock, flags); + /* Check if the device is still there */ + if(self->present) { + /* Set the desired speed */ + self->new_speed = irq->ifr_baudrate; + irda_usb_change_speed_xbofs(self); + } + spin_unlock_irqrestore(&self->lock, flags); + break; + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + /* Check if the IrDA stack is still there */ + if(self->netopen) + irda_device_set_media_busy(self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + irq->ifr_receiving = irda_usb_is_receiving(self); + break; + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Get device stats (for /proc/net/dev and ifconfig) + */ +static struct net_device_stats *irda_usb_net_get_stats(struct net_device *dev) +{ + struct irda_usb_cb *self = dev->priv; + return &self->stats; +} + +/********************* IRDA CONFIG SUBROUTINES *********************/ +/* + * Various subroutines dealing with IrDA and network stuff we use to + * configure and initialise each irda-usb instance. + * These functions are used below in the main calls of the driver... + */ + +/*------------------------------------------------------------------*/ +/* + * Set proper values in the IrDA QOS structure + */ +static inline void irda_usb_init_qos(struct irda_usb_cb *self) +{ + struct irda_class_desc *desc; + + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + desc = self->irda_desc; + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + /* See spec section 7.2 for meaning. + * Values are little endian (as most USB stuff), the IrDA stack + * use it in native order (see parameters.c). - Jean II */ + self->qos.baud_rate.bits = le16_to_cpu(desc->wBaudRate); + self->qos.min_turn_time.bits = desc->bmMinTurnaroundTime; + self->qos.additional_bofs.bits = desc->bmAdditionalBOFs; + self->qos.window_size.bits = desc->bmWindowSize; + self->qos.data_size.bits = desc->bmDataSize; + + IRDA_DEBUG(0, "%s(), dongle says speed=0x%X, size=0x%X, window=0x%X, bofs=0x%X, turn=0x%X\n", + __FUNCTION__, self->qos.baud_rate.bits, self->qos.data_size.bits, self->qos.window_size.bits, self->qos.additional_bofs.bits, self->qos.min_turn_time.bits); + + /* Don't always trust what the dongle tell us */ + if(self->capability & IUC_SIR_ONLY) + self->qos.baud_rate.bits &= 0x00ff; + if(self->capability & IUC_SMALL_PKT) + self->qos.data_size.bits = 0x07; + if(self->capability & IUC_NO_WINDOW) + self->qos.window_size.bits = 0x01; + if(self->capability & IUC_MAX_WINDOW) + self->qos.window_size.bits = 0x7f; + if(self->capability & IUC_MAX_XBOFS) + self->qos.additional_bofs.bits = 0x01; + +#if 1 + /* Module parameter can override the rx window size */ + if (qos_mtt_bits) + self->qos.min_turn_time.bits = qos_mtt_bits; +#endif + /* + * Note : most of those values apply only for the receive path, + * the transmit path will be set differently - Jean II + */ + irda_qos_bits_to_value(&self->qos); +} + +/*------------------------------------------------------------------*/ +/* + * Initialise the network side of the irda-usb instance + * Called when a new USB instance is registered in irda_usb_probe() + */ +static inline int irda_usb_open(struct irda_usb_cb *self) +{ + struct net_device *netdev = self->netdev; + + IRDA_DEBUG(1, "%s()\n", __FUNCTION__); + + irda_usb_init_qos(self); + + /* Override the network functions we need to use */ + netdev->hard_start_xmit = irda_usb_hard_xmit; + netdev->tx_timeout = irda_usb_net_timeout; + netdev->watchdog_timeo = 250*HZ/1000; /* 250 ms > USB timeout */ + netdev->open = irda_usb_net_open; + netdev->stop = irda_usb_net_close; + netdev->get_stats = irda_usb_net_get_stats; + netdev->do_ioctl = irda_usb_net_ioctl; + + return register_netdev(netdev); +} + +/*------------------------------------------------------------------*/ +/* + * Cleanup the network side of the irda-usb instance + * Called when a USB instance is removed in irda_usb_disconnect() + */ +static inline void irda_usb_close(struct irda_usb_cb *self) +{ + IRDA_DEBUG(1, "%s()\n", __FUNCTION__); + + /* Remove netdevice */ + unregister_netdev(self->netdev); + + /* Remove the speed buffer */ + if (self->speed_buff != NULL) { + kfree(self->speed_buff); + self->speed_buff = NULL; + } +} + +/********************** USB CONFIG SUBROUTINES **********************/ +/* + * Various subroutines dealing with USB stuff we use to configure and + * initialise each irda-usb instance. + * These functions are used below in the main calls of the driver... + */ + +/*------------------------------------------------------------------*/ +/* + * Function irda_usb_parse_endpoints(dev, ifnum) + * + * Parse the various endpoints and find the one we need. + * + * The endpoint are the pipes used to communicate with the USB device. + * The spec defines 2 endpoints of type bulk transfer, one in, and one out. + * These are used to pass frames back and forth with the dongle. + * Most dongle have also an interrupt endpoint, that will be probably + * documented in the next spec... + */ +static inline int irda_usb_parse_endpoints(struct irda_usb_cb *self, struct usb_host_endpoint *endpoint, int ennum) +{ + int i; /* Endpoint index in table */ + + /* Init : no endpoints */ + self->bulk_in_ep = 0; + self->bulk_out_ep = 0; + self->bulk_int_ep = 0; + + /* Let's look at all those endpoints */ + for(i = 0; i < ennum; i++) { + /* All those variables will get optimised by the compiler, + * so let's aim for clarity... - Jean II */ + __u8 ep; /* Endpoint address */ + __u8 dir; /* Endpoint direction */ + __u8 attr; /* Endpoint attribute */ + __u16 psize; /* Endpoint max packet size in bytes */ + + /* Get endpoint address, direction and attribute */ + ep = endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + dir = endpoint[i].desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK; + attr = endpoint[i].desc.bmAttributes; + psize = le16_to_cpu(endpoint[i].desc.wMaxPacketSize); + + /* Is it a bulk endpoint ??? */ + if(attr == USB_ENDPOINT_XFER_BULK) { + /* We need to find an IN and an OUT */ + if(dir == USB_DIR_IN) { + /* This is our Rx endpoint */ + self->bulk_in_ep = ep; + } else { + /* This is our Tx endpoint */ + self->bulk_out_ep = ep; + self->bulk_out_mtu = psize; + } + } else { + if((attr == USB_ENDPOINT_XFER_INT) && + (dir == USB_DIR_IN)) { + /* This is our interrupt endpoint */ + self->bulk_int_ep = ep; + } else { + IRDA_ERROR("%s(), Unrecognised endpoint %02X.\n", __FUNCTION__, ep); + } + } + } + + IRDA_DEBUG(0, "%s(), And our endpoints are : in=%02X, out=%02X (%d), int=%02X\n", + __FUNCTION__, self->bulk_in_ep, self->bulk_out_ep, self->bulk_out_mtu, self->bulk_int_ep); + /* Should be 8, 16, 32 or 64 bytes */ + IRDA_ASSERT(self->bulk_out_mtu == 64, ;); + + return((self->bulk_in_ep != 0) && (self->bulk_out_ep != 0)); +} + +#ifdef IU_DUMP_CLASS_DESC +/*------------------------------------------------------------------*/ +/* + * Function usb_irda_dump_class_desc(desc) + * + * Prints out the contents of the IrDA class descriptor + * + */ +static inline void irda_usb_dump_class_desc(struct irda_class_desc *desc) +{ + /* Values are little endian */ + printk("bLength=%x\n", desc->bLength); + printk("bDescriptorType=%x\n", desc->bDescriptorType); + printk("bcdSpecRevision=%x\n", le16_to_cpu(desc->bcdSpecRevision)); + printk("bmDataSize=%x\n", desc->bmDataSize); + printk("bmWindowSize=%x\n", desc->bmWindowSize); + printk("bmMinTurnaroundTime=%d\n", desc->bmMinTurnaroundTime); + printk("wBaudRate=%x\n", le16_to_cpu(desc->wBaudRate)); + printk("bmAdditionalBOFs=%x\n", desc->bmAdditionalBOFs); + printk("bIrdaRateSniff=%x\n", desc->bIrdaRateSniff); + printk("bMaxUnicastList=%x\n", desc->bMaxUnicastList); +} +#endif /* IU_DUMP_CLASS_DESC */ + +/*------------------------------------------------------------------*/ +/* + * Function irda_usb_find_class_desc(intf) + * + * Returns instance of IrDA class descriptor, or NULL if not found + * + * The class descriptor is some extra info that IrDA USB devices will + * offer to us, describing their IrDA characteristics. We will use that in + * irda_usb_init_qos() + */ +static inline struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf) +{ + struct usb_device *dev = interface_to_usbdev (intf); + struct irda_class_desc *desc; + int ret; + + desc = kmalloc(sizeof (*desc), GFP_KERNEL); + if (desc == NULL) + return NULL; + memset(desc, 0, sizeof(*desc)); + + /* USB-IrDA class spec 1.0: + * 6.1.3: Standard "Get Descriptor" Device Request is not + * appropriate to retrieve class-specific descriptor + * 6.2.5: Class Specific "Get Class Descriptor" Interface Request + * is mandatory and returns the USB-IrDA class descriptor + */ + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev,0), + IU_REQ_GET_CLASS_DESC, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, intf->altsetting->desc.bInterfaceNumber, desc, + sizeof(*desc), 500); + + IRDA_DEBUG(1, "%s(), ret=%d\n", __FUNCTION__, ret); + if (ret < sizeof(*desc)) { + IRDA_WARNING("usb-irda: class_descriptor read %s (%d)\n", + (ret<0) ? "failed" : "too short", ret); + } + else if (desc->bDescriptorType != USB_DT_IRDA) { + IRDA_WARNING("usb-irda: bad class_descriptor type\n"); + } + else { +#ifdef IU_DUMP_CLASS_DESC + irda_usb_dump_class_desc(desc); +#endif /* IU_DUMP_CLASS_DESC */ + + return desc; + } + kfree(desc); + return NULL; +} + +/*********************** USB DEVICE CALLBACKS ***********************/ +/* + * Main calls from the USB subsystem. + * Mostly registering a new irda-usb device and removing it.... + */ + +/*------------------------------------------------------------------*/ +/* + * This routine is called by the USB subsystem for each new device + * in the system. We need to check if the device is ours, and in + * this case start handling it. + * The USB layer protect us from reentrancy (via BKL), so we don't need + * to spinlock in there... Jean II + */ +static int irda_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct net_device *net; + struct usb_device *dev = interface_to_usbdev(intf); + struct irda_usb_cb *self = NULL; + struct usb_host_interface *interface; + struct irda_class_desc *irda_desc; + int ret = -ENOMEM; + int i; /* Driver instance index / Rx URB index */ + + /* Note : the probe make sure to call us only for devices that + * matches the list of dongle (top of the file). So, we + * don't need to check if the dongle is really ours. + * Jean II */ + + IRDA_MESSAGE("IRDA-USB found at address %d, Vendor: %x, Product: %x\n", + dev->devnum, le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + net = alloc_irdadev(sizeof(*self)); + if (!net) + goto err_out; + + SET_MODULE_OWNER(net); + SET_NETDEV_DEV(net, &intf->dev); + self = net->priv; + self->netdev = net; + spin_lock_init(&self->lock); + + /* Create all of the needed urbs */ + for (i = 0; i < IU_MAX_RX_URBS; i++) { + self->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!self->rx_urb[i]) { + goto err_out_1; + } + } + self->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!self->tx_urb) { + goto err_out_1; + } + self->speed_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!self->speed_urb) { + goto err_out_2; + } + + /* Is this really necessary? (no, except maybe for broken devices) */ + if (usb_reset_configuration (dev) < 0) { + err("reset_configuration failed"); + ret = -EIO; + goto err_out_3; + } + + /* Is this really necessary? */ + /* Note : some driver do hardcode the interface number, some others + * specify an alternate, but very few driver do like this. + * Jean II */ + ret = usb_set_interface(dev, intf->altsetting->desc.bInterfaceNumber, 0); + IRDA_DEBUG(1, "usb-irda: set interface %d result %d\n", intf->altsetting->desc.bInterfaceNumber, ret); + switch (ret) { + case 0: + break; + case -EPIPE: /* -EPIPE = -32 */ + /* Martin Diehl says if we get a -EPIPE we should + * be fine and we don't need to do a usb_clear_halt(). + * - Jean II */ + IRDA_DEBUG(0, "%s(), Received -EPIPE, ignoring...\n", __FUNCTION__); + break; + default: + IRDA_DEBUG(0, "%s(), Unknown error %d\n", __FUNCTION__, ret); + ret = -EIO; + goto err_out_3; + } + + /* Find our endpoints */ + interface = intf->cur_altsetting; + if(!irda_usb_parse_endpoints(self, interface->endpoint, + interface->desc.bNumEndpoints)) { + IRDA_ERROR("%s(), Bogus endpoints...\n", __FUNCTION__); + ret = -EIO; + goto err_out_3; + } + + /* Find IrDA class descriptor */ + irda_desc = irda_usb_find_class_desc(intf); + ret = -ENODEV; + if (irda_desc == NULL) + goto err_out_3; + + self->irda_desc = irda_desc; + self->present = 1; + self->netopen = 0; + self->capability = id->driver_info; + self->usbdev = dev; + self->usbintf = intf; + + /* Allocate the buffer for speed changes */ + /* Don't change this buffer size and allocation without doing + * some heavy and complete testing. Don't ask why :-( + * Jean II */ + self->speed_buff = (char *) kmalloc(IRDA_USB_SPEED_MTU, GFP_KERNEL); + if (self->speed_buff == NULL) + goto err_out_3; + + memset(self->speed_buff, 0, IRDA_USB_SPEED_MTU); + + ret = irda_usb_open(self); + if (ret) + goto err_out_4; + + IRDA_MESSAGE("IrDA: Registered device %s\n", net->name); + usb_set_intfdata(intf, self); + return 0; + +err_out_4: + kfree(self->speed_buff); +err_out_3: + /* Free all urbs that we may have created */ + usb_free_urb(self->speed_urb); +err_out_2: + usb_free_urb(self->tx_urb); +err_out_1: + for (i = 0; i < IU_MAX_RX_URBS; i++) { + if (self->rx_urb[i]) + usb_free_urb(self->rx_urb[i]); + } + free_netdev(net); +err_out: + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * The current irda-usb device is removed, the USB layer tell us + * to shut it down... + * One of the constraints is that when we exit this function, + * we cannot use the usb_device no more. Gone. Destroyed. kfree(). + * Most other subsystem allow you to destroy the instance at a time + * when it's convenient to you, to postpone it to a later date, but + * not the USB subsystem. + * So, we must make bloody sure that everything gets deactivated. + * Jean II + */ +static void irda_usb_disconnect(struct usb_interface *intf) +{ + unsigned long flags; + struct irda_usb_cb *self = usb_get_intfdata(intf); + int i; + + IRDA_DEBUG(1, "%s()\n", __FUNCTION__); + + usb_set_intfdata(intf, NULL); + if (!self) + return; + + /* Make sure that the Tx path is not executing. - Jean II */ + spin_lock_irqsave(&self->lock, flags); + + /* Oups ! We are not there any more. + * This will stop/desactivate the Tx path. - Jean II */ + self->present = 0; + + /* We need to have irq enabled to unlink the URBs. That's OK, + * at this point the Tx path is gone - Jean II */ + spin_unlock_irqrestore(&self->lock, flags); + + /* Hum... Check if networking is still active (avoid races) */ + if((self->netopen) || (self->irlap)) { + /* Accept no more transmissions */ + /*netif_device_detach(self->netdev);*/ + netif_stop_queue(self->netdev); + /* Stop all the receive URBs */ + for (i = 0; i < IU_MAX_RX_URBS; i++) + usb_kill_urb(self->rx_urb[i]); + /* Cancel Tx and speed URB. + * Toggle flags to make sure it's synchronous. */ + self->tx_urb->transfer_flags &= ~URB_ASYNC_UNLINK; + usb_kill_urb(self->tx_urb); + self->speed_urb->transfer_flags &= ~URB_ASYNC_UNLINK; + usb_kill_urb(self->speed_urb); + } + + /* Cleanup the device stuff */ + irda_usb_close(self); + /* No longer attached to USB bus */ + self->usbdev = NULL; + self->usbintf = NULL; + + /* Clean up our urbs */ + for (i = 0; i < IU_MAX_RX_URBS; i++) + usb_free_urb(self->rx_urb[i]); + /* Clean up Tx and speed URB */ + usb_free_urb(self->tx_urb); + usb_free_urb(self->speed_urb); + + /* Free self and network device */ + free_netdev(self->netdev); + IRDA_DEBUG(0, "%s(), USB IrDA Disconnected\n", __FUNCTION__); +} + +/*------------------------------------------------------------------*/ +/* + * USB device callbacks + */ +static struct usb_driver irda_driver = { + .owner = THIS_MODULE, + .name = "irda-usb", + .probe = irda_usb_probe, + .disconnect = irda_usb_disconnect, + .id_table = dongles, +}; + +/************************* MODULE CALLBACKS *************************/ +/* + * Deal with module insertion/removal + * Mostly tell USB about our existence + */ + +/*------------------------------------------------------------------*/ +/* + * Module insertion + */ +static int __init usb_irda_init(void) +{ + int ret; + + ret = usb_register(&irda_driver); + if (ret < 0) + return ret; + + IRDA_MESSAGE("USB IrDA support registered\n"); + return 0; +} +module_init(usb_irda_init); + +/*------------------------------------------------------------------*/ +/* + * Module removal + */ +static void __exit usb_irda_cleanup(void) +{ + /* Deregister the driver and remove all pending instances */ + usb_deregister(&irda_driver); +} +module_exit(usb_irda_cleanup); + +/*------------------------------------------------------------------*/ +/* + * Module parameters + */ +module_param(qos_mtt_bits, int, 0); +MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); +MODULE_AUTHOR("Roman Weissgaerber <weissg@vienna.at>, Dag Brattli <dag@brattli.net> and Jean Tourrilhes <jt@hpl.hp.com>"); +MODULE_DESCRIPTION("IrDA-USB Dongle Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/irda/irda-usb.h b/drivers/net/irda/irda-usb.h new file mode 100644 index 000000000000..bd8f66542322 --- /dev/null +++ b/drivers/net/irda/irda-usb.h @@ -0,0 +1,163 @@ +/***************************************************************************** + * + * Filename: irda-usb.h + * Version: 0.9b + * Description: IrDA-USB Driver + * Status: Experimental + * Author: Dag Brattli <dag@brattli.net> + * + * Copyright (C) 2001, Roman Weissgaerber <weissg@vienna.at> + * Copyright (C) 2000, Dag Brattli <dag@brattli.net> + * Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + *****************************************************************************/ + +#include <linux/time.h> + +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> /* struct irlap_cb */ + +#define RX_COPY_THRESHOLD 200 +#define IRDA_USB_MAX_MTU 2051 +#define IRDA_USB_SPEED_MTU 64 /* Weird, but work like this */ + +/* Maximum number of active URB on the Rx path + * This is the amount of buffers the we keep between the USB harware and the + * IrDA stack. + * + * Note : the network layer does also queue the packets between us and the + * IrDA stack, and is actually pretty fast and efficient in doing that. + * Therefore, we don't need to have a large number of URBs, and we can + * perfectly live happy with only one. We certainly don't need to keep the + * full IrTTP window around here... + * I repeat for those who have trouble to understand : 1 URB is plenty + * good enough to handle back-to-back (brickwalled) frames. I tried it, + * it works (it's the hardware that has trouble doing it). + * + * Having 2 URBs would allow the USB stack to process one URB while we take + * care of the other and then swap the URBs... + * On the other hand, increasing the number of URB will have penalities + * in term of latency and will interact with the link management in IrLAP... + * Jean II */ +#define IU_MAX_ACTIVE_RX_URBS 1 /* Don't touch !!! */ + +/* When a Rx URB is passed back to us, we can't reuse it immediately, + * because it may still be referenced by the USB layer. Therefore we + * need to keep one extra URB in the Rx path. + * Jean II */ +#define IU_MAX_RX_URBS (IU_MAX_ACTIVE_RX_URBS + 1) + +/* Various ugly stuff to try to workaround generic problems */ +/* Send speed command in case of timeout, just for trying to get things sane */ +#define IU_BUG_KICK_TIMEOUT +/* Show the USB class descriptor */ +#undef IU_DUMP_CLASS_DESC +/* Assume a minimum round trip latency for USB transfer (in us)... + * USB transfer are done in the next USB slot if there is no traffic + * (1/19 msec) and is done at 12 Mb/s : + * Waiting for slot + tx = (53us + 16us) * 2 = 137us minimum. + * Rx notification will only be done at the end of the USB frame period : + * OHCI : frame period = 1ms + * UHCI : frame period = 1ms, but notification can take 2 or 3 ms :-( + * EHCI : frame period = 125us */ +#define IU_USB_MIN_RTT 500 /* This should be safe in most cases */ + +/* Inbound header */ +#define MEDIA_BUSY 0x80 + +#define SPEED_2400 0x01 +#define SPEED_9600 0x02 +#define SPEED_19200 0x03 +#define SPEED_38400 0x04 +#define SPEED_57600 0x05 +#define SPEED_115200 0x06 +#define SPEED_576000 0x07 +#define SPEED_1152000 0x08 +#define SPEED_4000000 0x09 + +/* Basic capabilities */ +#define IUC_DEFAULT 0x00 /* Basic device compliant with 1.0 spec */ +/* Main bugs */ +#define IUC_SPEED_BUG 0x01 /* Device doesn't set speed after the frame */ +#define IUC_NO_WINDOW 0x02 /* Device doesn't behave with big Rx window */ +#define IUC_NO_TURN 0x04 /* Device doesn't do turnaround by itself */ +/* Not currently used */ +#define IUC_SIR_ONLY 0x08 /* Device doesn't behave at FIR speeds */ +#define IUC_SMALL_PKT 0x10 /* Device doesn't behave with big Rx packets */ +#define IUC_MAX_WINDOW 0x20 /* Device underestimate the Rx window */ +#define IUC_MAX_XBOFS 0x40 /* Device need more xbofs than advertised */ + +/* USB class definitions */ +#define USB_IRDA_HEADER 0x01 +#define USB_CLASS_IRDA 0x02 /* USB_CLASS_APP_SPEC subclass */ +#define USB_DT_IRDA 0x21 + +struct irda_class_desc { + __u8 bLength; + __u8 bDescriptorType; + __u16 bcdSpecRevision; + __u8 bmDataSize; + __u8 bmWindowSize; + __u8 bmMinTurnaroundTime; + __u16 wBaudRate; + __u8 bmAdditionalBOFs; + __u8 bIrdaRateSniff; + __u8 bMaxUnicastList; +} __attribute__ ((packed)); + +/* class specific interface request to get the IrDA-USB class descriptor + * (6.2.5, USB-IrDA class spec 1.0) */ + +#define IU_REQ_GET_CLASS_DESC 0x06 + +struct irda_usb_cb { + struct irda_class_desc *irda_desc; + struct usb_device *usbdev; /* init: probe_irda */ + struct usb_interface *usbintf; /* init: probe_irda */ + int netopen; /* Device is active for network */ + int present; /* Device is present on the bus */ + __u32 capability; /* Capability of the hardware */ + __u8 bulk_in_ep; /* Rx Endpoint assignments */ + __u8 bulk_out_ep; /* Tx Endpoint assignments */ + __u16 bulk_out_mtu; /* Max Tx packet size in bytes */ + __u8 bulk_int_ep; /* Interrupt Endpoint assignments */ + + wait_queue_head_t wait_q; /* for timeouts */ + + struct urb *rx_urb[IU_MAX_RX_URBS]; /* URBs used to receive data frames */ + struct urb *idle_rx_urb; /* Pointer to idle URB in Rx path */ + struct urb *tx_urb; /* URB used to send data frames */ + struct urb *speed_urb; /* URB used to send speed commands */ + + struct net_device *netdev; /* Yes! we are some kind of netdev. */ + struct net_device_stats stats; + struct irlap_cb *irlap; /* The link layer we are binded to */ + struct qos_info qos; + hashbin_t *tx_list; /* Queued transmit skb's */ + char *speed_buff; /* Buffer for speed changes */ + + struct timeval stamp; + struct timeval now; + + spinlock_t lock; /* For serializing operations */ + + __u16 xbofs; /* Current xbofs setting */ + __s16 new_xbofs; /* xbofs we need to set */ + __u32 speed; /* Current speed */ + __s32 new_speed; /* speed we need to set */ +}; + diff --git a/drivers/net/irda/irport.c b/drivers/net/irda/irport.c new file mode 100644 index 000000000000..5971315f3fa0 --- /dev/null +++ b/drivers/net/irda/irport.c @@ -0,0 +1,1146 @@ +/********************************************************************* + * + * Filename: irport.c + * Version: 1.0 + * Description: Half duplex serial port SIR driver for IrDA. + * Status: Experimental. + * Author: Dag Brattli <dagb@cs.uit.no> + * Created at: Sun Aug 3 13:49:59 1997 + * Modified at: Fri Jan 28 20:22:38 2000 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * Sources: serial.c by Linus Torvalds + * + * Copyright (c) 1997, 1998, 1999-2000 Dag Brattli, All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * This driver is ment to be a small half duplex serial driver to be + * used for IR-chipsets that has a UART (16550) compatibility mode. + * Eventually it will replace irtty, because of irtty has some + * problems that is hard to get around when we don't have control + * over the serial driver. This driver may also be used by FIR + * drivers to handle SIR mode for them. + * + ********************************************************************/ + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/skbuff.h> +#include <linux/serial_reg.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/rtnetlink.h> +#include <linux/bitops.h> + +#include <asm/system.h> +#include <asm/io.h> + +#include <net/irda/irda.h> +#include <net/irda/wrapper.h> +#include "irport.h" + +#define IO_EXTENT 8 + +/* + * Currently you'll need to set these values using insmod like this: + * insmod irport io=0x3e8 irq=11 + */ +static unsigned int io[] = { ~0, ~0, ~0, ~0 }; +static unsigned int irq[] = { 0, 0, 0, 0 }; + +static unsigned int qos_mtt_bits = 0x03; + +static struct irport_cb *dev_self[] = { NULL, NULL, NULL, NULL}; +static char *driver_name = "irport"; + +static inline void irport_write_wakeup(struct irport_cb *self); +static inline int irport_write(int iobase, int fifo_size, __u8 *buf, int len); +static inline void irport_receive(struct irport_cb *self); + +static int irport_net_ioctl(struct net_device *dev, struct ifreq *rq, + int cmd); +static inline int irport_is_receiving(struct irport_cb *self); +static int irport_set_dtr_rts(struct net_device *dev, int dtr, int rts); +static int irport_raw_write(struct net_device *dev, __u8 *buf, int len); +static struct net_device_stats *irport_net_get_stats(struct net_device *dev); +static int irport_change_speed_complete(struct irda_task *task); +static void irport_timeout(struct net_device *dev); + +static irqreturn_t irport_interrupt(int irq, void *dev_id, + struct pt_regs *regs); +static int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev); +static void irport_change_speed(void *priv, __u32 speed); +static int irport_net_open(struct net_device *dev); +static int irport_net_close(struct net_device *dev); + +static struct irport_cb * +irport_open(int i, unsigned int iobase, unsigned int irq) +{ + struct net_device *dev; + struct irport_cb *self; + + IRDA_DEBUG(1, "%s()\n", __FUNCTION__); + + /* Lock the port that we need */ + if (!request_region(iobase, IO_EXTENT, driver_name)) { + IRDA_DEBUG(0, "%s(), can't get iobase of 0x%03x\n", + __FUNCTION__, iobase); + goto err_out1; + } + + /* + * Allocate new instance of the driver + */ + dev = alloc_irdadev(sizeof(struct irport_cb)); + if (!dev) { + IRDA_ERROR("%s(), can't allocate memory for " + "irda device!\n", __FUNCTION__); + goto err_out2; + } + + self = dev->priv; + spin_lock_init(&self->lock); + + /* Need to store self somewhere */ + dev_self[i] = self; + self->priv = self; + self->index = i; + + /* Initialize IO */ + self->io.sir_base = iobase; + self->io.sir_ext = IO_EXTENT; + self->io.irq = irq; + self->io.fifo_size = 16; /* 16550A and compatible */ + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| + IR_115200; + + self->qos.min_turn_time.bits = qos_mtt_bits; + irda_qos_bits_to_value(&self->qos); + + /* Bootstrap ZeroCopy Rx */ + self->rx_buff.truesize = IRDA_SKB_MAX_MTU; + self->rx_buff.skb = __dev_alloc_skb(self->rx_buff.truesize, + GFP_KERNEL); + if (self->rx_buff.skb == NULL) { + IRDA_ERROR("%s(), can't allocate memory for " + "receive buffer!\n", __FUNCTION__); + goto err_out3; + } + skb_reserve(self->rx_buff.skb, 1); + self->rx_buff.head = self->rx_buff.skb->data; + /* No need to memset the buffer, unless you are really pedantic */ + + /* Finish setup the Rx buffer descriptor */ + self->rx_buff.in_frame = FALSE; + self->rx_buff.state = OUTSIDE_FRAME; + self->rx_buff.data = self->rx_buff.head; + + /* Specify how much memory we want */ + self->tx_buff.truesize = 4000; + + /* Allocate memory if needed */ + if (self->tx_buff.truesize > 0) { + self->tx_buff.head = (__u8 *) kmalloc(self->tx_buff.truesize, + GFP_KERNEL); + if (self->tx_buff.head == NULL) { + IRDA_ERROR("%s(), can't allocate memory for " + "transmit buffer!\n", __FUNCTION__); + goto err_out4; + } + memset(self->tx_buff.head, 0, self->tx_buff.truesize); + } + self->tx_buff.data = self->tx_buff.head; + + self->netdev = dev; + /* Keep track of module usage */ + SET_MODULE_OWNER(dev); + + /* May be overridden by piggyback drivers */ + self->interrupt = irport_interrupt; + self->change_speed = irport_change_speed; + + /* Override the network functions we need to use */ + dev->hard_start_xmit = irport_hard_xmit; + dev->tx_timeout = irport_timeout; + dev->watchdog_timeo = HZ; /* Allow time enough for speed change */ + dev->open = irport_net_open; + dev->stop = irport_net_close; + dev->get_stats = irport_net_get_stats; + dev->do_ioctl = irport_net_ioctl; + + /* Make ifconfig display some details */ + dev->base_addr = iobase; + dev->irq = irq; + + if (register_netdev(dev)) { + IRDA_ERROR("%s(), register_netdev() failed!\n", __FUNCTION__); + goto err_out5; + } + IRDA_MESSAGE("IrDA: Registered device %s (irport io=0x%X irq=%d)\n", + dev->name, iobase, irq); + + return self; + err_out5: + kfree(self->tx_buff.head); + err_out4: + kfree_skb(self->rx_buff.skb); + err_out3: + free_netdev(dev); + dev_self[i] = NULL; + err_out2: + release_region(iobase, IO_EXTENT); + err_out1: + return NULL; +} + +static int irport_close(struct irport_cb *self) +{ + IRDA_ASSERT(self != NULL, return -1;); + + /* We are not using any dongle anymore! */ + if (self->dongle) + irda_device_dongle_cleanup(self->dongle); + self->dongle = NULL; + + /* Remove netdevice */ + unregister_netdev(self->netdev); + + /* Release the IO-port that this driver is using */ + IRDA_DEBUG(0 , "%s(), Releasing Region %03x\n", + __FUNCTION__, self->io.sir_base); + release_region(self->io.sir_base, self->io.sir_ext); + + if (self->tx_buff.head) + kfree(self->tx_buff.head); + + if (self->rx_buff.skb) + kfree_skb(self->rx_buff.skb); + self->rx_buff.skb = NULL; + + /* Remove ourselves */ + dev_self[self->index] = NULL; + free_netdev(self->netdev); + + return 0; +} + +static void irport_stop(struct irport_cb *self) +{ + int iobase; + + iobase = self->io.sir_base; + + /* We can't lock, we may be called from a FIR driver - Jean II */ + + /* We are not transmitting any more */ + self->transmitting = 0; + + /* Reset UART */ + outb(0, iobase+UART_MCR); + + /* Turn off interrupts */ + outb(0, iobase+UART_IER); +} + +static void irport_start(struct irport_cb *self) +{ + int iobase; + + iobase = self->io.sir_base; + + irport_stop(self); + + /* We can't lock, we may be called from a FIR driver - Jean II */ + + /* Initialize UART */ + outb(UART_LCR_WLEN8, iobase+UART_LCR); /* Reset DLAB */ + outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase+UART_MCR); + + /* Turn on interrups */ + outb(UART_IER_RLSI | UART_IER_RDI |UART_IER_THRI, iobase+UART_IER); +} + +/* + * Function irport_probe (void) + * + * Start IO port + * + */ +int irport_probe(int iobase) +{ + IRDA_DEBUG(4, "%s(), iobase=%#x\n", __FUNCTION__, iobase); + + return 0; +} + +/* + * Function irport_get_fcr (speed) + * + * Compute value of fcr + * + */ +static inline unsigned int irport_get_fcr(__u32 speed) +{ + unsigned int fcr; /* FIFO control reg */ + + /* Enable fifos */ + fcr = UART_FCR_ENABLE_FIFO; + + /* + * Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and + * almost 1,7 ms at 19200 bps. At speeds above that we can just forget + * about this timeout since it will always be fast enough. + */ + if (speed < 38400) + fcr |= UART_FCR_TRIGGER_1; + else + //fcr |= UART_FCR_TRIGGER_14; + fcr |= UART_FCR_TRIGGER_8; + + return(fcr); +} + +/* + * Function irport_change_speed (self, speed) + * + * Set speed of IrDA port to specified baudrate + * + * This function should be called with irq off and spin-lock. + */ +static void irport_change_speed(void *priv, __u32 speed) +{ + struct irport_cb *self = (struct irport_cb *) priv; + int iobase; + unsigned int fcr; /* FIFO control reg */ + unsigned int lcr; /* Line control reg */ + int divisor; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(speed != 0, return;); + + IRDA_DEBUG(1, "%s(), Setting speed to: %d - iobase=%#x\n", + __FUNCTION__, speed, self->io.sir_base); + + /* We can't lock, we may be called from a FIR driver - Jean II */ + + iobase = self->io.sir_base; + + /* Update accounting for new speed */ + self->io.speed = speed; + + /* Turn off interrupts */ + outb(0, iobase+UART_IER); + + divisor = SPEED_MAX/speed; + + /* Get proper fifo configuration */ + fcr = irport_get_fcr(speed); + + /* IrDA ports use 8N1 */ + lcr = UART_LCR_WLEN8; + + outb(UART_LCR_DLAB | lcr, iobase+UART_LCR); /* Set DLAB */ + outb(divisor & 0xff, iobase+UART_DLL); /* Set speed */ + outb(divisor >> 8, iobase+UART_DLM); + outb(lcr, iobase+UART_LCR); /* Set 8N1 */ + outb(fcr, iobase+UART_FCR); /* Enable FIFO's */ + + /* Turn on interrups */ + /* This will generate a fatal interrupt storm. + * People calling us will do that properly - Jean II */ + //outb(/*UART_IER_RLSI|*/UART_IER_RDI/*|UART_IER_THRI*/, iobase+UART_IER); +} + +/* + * Function __irport_change_speed (instance, state, param) + * + * State machine for changing speed of the device. We do it this way since + * we cannot use schedule_timeout() when we are in interrupt context + * + */ +int __irport_change_speed(struct irda_task *task) +{ + struct irport_cb *self; + __u32 speed = (__u32) task->param; + unsigned long flags = 0; + int wasunlocked = 0; + int ret = 0; + + IRDA_DEBUG(2, "%s(), <%ld>\n", __FUNCTION__, jiffies); + + self = (struct irport_cb *) task->instance; + + IRDA_ASSERT(self != NULL, return -1;); + + /* Locking notes : this function may be called from irq context with + * spinlock, via irport_write_wakeup(), or from non-interrupt without + * spinlock (from the task timer). Yuck ! + * This is ugly, and unsafe is the spinlock is not already aquired. + * This will be fixed when irda-task get rewritten. + * Jean II */ + if (!spin_is_locked(&self->lock)) { + spin_lock_irqsave(&self->lock, flags); + wasunlocked = 1; + } + + switch (task->state) { + case IRDA_TASK_INIT: + case IRDA_TASK_WAIT: + /* Are we ready to change speed yet? */ + if (self->tx_buff.len > 0) { + task->state = IRDA_TASK_WAIT; + + /* Try again later */ + ret = msecs_to_jiffies(20); + break; + } + + if (self->dongle) + irda_task_next_state(task, IRDA_TASK_CHILD_INIT); + else + irda_task_next_state(task, IRDA_TASK_CHILD_DONE); + break; + case IRDA_TASK_CHILD_INIT: + /* Go to default speed */ + self->change_speed(self->priv, 9600); + + /* Change speed of dongle */ + if (irda_task_execute(self->dongle, + self->dongle->issue->change_speed, + NULL, task, (void *) speed)) + { + /* Dongle need more time to change its speed */ + irda_task_next_state(task, IRDA_TASK_CHILD_WAIT); + + /* Give dongle 1 sec to finish */ + ret = msecs_to_jiffies(1000); + } else + /* Child finished immediately */ + irda_task_next_state(task, IRDA_TASK_CHILD_DONE); + break; + case IRDA_TASK_CHILD_WAIT: + IRDA_WARNING("%s(), changing speed of dongle timed out!\n", __FUNCTION__); + ret = -1; + break; + case IRDA_TASK_CHILD_DONE: + /* Finally we are ready to change the speed */ + self->change_speed(self->priv, speed); + + irda_task_next_state(task, IRDA_TASK_DONE); + break; + default: + IRDA_ERROR("%s(), unknown state %d\n", + __FUNCTION__, task->state); + irda_task_next_state(task, IRDA_TASK_DONE); + ret = -1; + break; + } + /* Put stuff in the state we found them - Jean II */ + if(wasunlocked) { + spin_unlock_irqrestore(&self->lock, flags); + } + + return ret; +} + +/* + * Function irport_change_speed_complete (task) + * + * Called when the change speed operation completes + * + */ +static int irport_change_speed_complete(struct irda_task *task) +{ + struct irport_cb *self; + + IRDA_DEBUG(1, "%s()\n", __FUNCTION__); + + self = (struct irport_cb *) task->instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->netdev != NULL, return -1;); + + /* Finished changing speed, so we are not busy any longer */ + /* Signal network layer so it can try to send the frame */ + + netif_wake_queue(self->netdev); + + return 0; +} + +/* + * Function irport_timeout (struct net_device *dev) + * + * The networking layer thinks we timed out. + * + */ + +static void irport_timeout(struct net_device *dev) +{ + struct irport_cb *self; + int iobase; + int iir, lsr; + unsigned long flags; + + self = (struct irport_cb *) dev->priv; + IRDA_ASSERT(self != NULL, return;); + iobase = self->io.sir_base; + + IRDA_WARNING("%s: transmit timed out, jiffies = %ld, trans_start = %ld\n", + dev->name, jiffies, dev->trans_start); + spin_lock_irqsave(&self->lock, flags); + + /* Debug what's happening... */ + + /* Get interrupt status */ + lsr = inb(iobase+UART_LSR); + /* Read interrupt register */ + iir = inb(iobase+UART_IIR); + IRDA_DEBUG(0, "%s(), iir=%02x, lsr=%02x, iobase=%#x\n", + __FUNCTION__, iir, lsr, iobase); + + IRDA_DEBUG(0, "%s(), transmitting=%d, remain=%d, done=%d\n", + __FUNCTION__, self->transmitting, self->tx_buff.len, + self->tx_buff.data - self->tx_buff.head); + + /* Now, restart the port */ + irport_start(self); + self->change_speed(self->priv, self->io.speed); + /* This will re-enable irqs */ + outb(/*UART_IER_RLSI|*/UART_IER_RDI/*|UART_IER_THRI*/, iobase+UART_IER); + dev->trans_start = jiffies; + spin_unlock_irqrestore(&self->lock, flags); + + netif_wake_queue(dev); +} + +/* + * Function irport_wait_hw_transmitter_finish () + * + * Wait for the real end of HW transmission + * + * The UART is a strict FIFO, and we get called only when we have finished + * pushing data to the FIFO, so the maximum amount of time we must wait + * is only for the FIFO to drain out. + * + * We use a simple calibrated loop. We may need to adjust the loop + * delay (udelay) to balance I/O traffic and latency. And we also need to + * adjust the maximum timeout. + * It would probably be better to wait for the proper interrupt, + * but it doesn't seem to be available. + * + * We can't use jiffies or kernel timers because : + * 1) We are called from the interrupt handler, which disable softirqs, + * so jiffies won't be increased + * 2) Jiffies granularity is usually very coarse (10ms), and we don't + * want to wait that long to detect stuck hardware. + * Jean II + */ + +static void irport_wait_hw_transmitter_finish(struct irport_cb *self) +{ + int iobase; + int count = 1000; /* 1 ms */ + + iobase = self->io.sir_base; + + /* Calibrated busy loop */ + while((count-- > 0) && !(inb(iobase+UART_LSR) & UART_LSR_TEMT)) + udelay(1); + + 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 + * waits until the next transmitt interrupt, and continues until the + * frame is transmitted. + */ +static int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct irport_cb *self; + unsigned long flags; + int iobase; + s32 speed; + + IRDA_DEBUG(1, "%s()\n", __FUNCTION__); + + IRDA_ASSERT(dev != NULL, return 0;); + + self = (struct irport_cb *) dev->priv; + IRDA_ASSERT(self != NULL, return 0;); + + iobase = self->io.sir_base; + + netif_stop_queue(dev); + + /* Make sure tests & 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 */ + 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; + } 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; + + /* 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); + + return 0; +} + +/* + * 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; + + IRDA_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 inline void irport_receive(struct irport_cb *self) +{ + int boguscount = 0; + int iobase; + + IRDA_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 too long */ + if (boguscount++ > 32) { + IRDA_DEBUG(2,"%s(), breaking!\n", __FUNCTION__); + break; + } + } while (inb(iobase+UART_LSR) & UART_LSR_DR); +} + +/* + * Function irport_interrupt (irq, dev_id, regs) + * + * Interrupt handler + */ +static irqreturn_t irport_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct irport_cb *self; + int boguscount = 0; + int iobase; + int iir, lsr; + int handled = 0; + + if (!dev) { + IRDA_WARNING("%s() irq %d for unknown device.\n", __FUNCTION__, irq); + return IRQ_NONE; + } + self = (struct irport_cb *) dev->priv; + + spin_lock(&self->lock); + + iobase = self->io.sir_base; + + /* 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 */ + + /* Check status even is iir reg is cleared, more robust and + * eliminate a read on the I/O bus - Jean II */ + do { + /* Get interrupt status ; Clear interrupt */ + lsr = inb(iobase+UART_LSR); + + /* Are we receiving or transmitting ? */ + if(!self->transmitting) { + /* Received something ? */ + if (lsr & UART_LSR_DR) + irport_receive(self); + } else { + /* Room in Tx fifo ? */ + if (lsr & (UART_LSR_THRE | UART_LSR_TEMT)) + irport_write_wakeup(self); + } + + /* 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) { + IRDA_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); + + spin_unlock(&self->lock); + return IRQ_RETVAL(handled); +} + +/* + * Function irport_net_open (dev) + * + * Network device is taken up. Usually this is done by "ifconfig irda0 up" + * + */ +static int irport_net_open(struct net_device *dev) +{ + struct irport_cb *self; + int iobase; + char hwname[16]; + unsigned long flags; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + IRDA_ASSERT(dev != NULL, return -1;); + self = (struct irport_cb *) dev->priv; + + iobase = self->io.sir_base; + + if (request_irq(self->io.irq, self->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); + /* 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); + + + /* Give self a hardware name */ + sprintf(hwname, "SIR @ 0x%03x", self->io.sir_base); + + /* + * Open new IrLAP layer instance, now that everything should be + * initialized properly + */ + self->irlap = irlap_open(dev, &self->qos, hwname); + + /* Ready to play! */ + + netif_start_queue(dev); + + return 0; +} + +/* + * Function irport_net_close (self) + * + * Network device is taken down. Usually this is done by + * "ifconfig irda0 down" + */ +static int irport_net_close(struct net_device *dev) +{ + struct irport_cb *self; + int iobase; + unsigned long flags; + + IRDA_DEBUG(4, "%s()\n", __FUNCTION__); + + IRDA_ASSERT(dev != NULL, return -1;); + self = (struct irport_cb *) dev->priv; + + IRDA_ASSERT(self != NULL, return -1;); + + iobase = self->io.sir_base; + + /* Stop device */ + netif_stop_queue(dev); + + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close(self->irlap); + self->irlap = NULL; + + spin_lock_irqsave(&self->lock, flags); + irport_stop(self); + spin_unlock_irqrestore(&self->lock, flags); + + free_irq(self->io.irq, dev); + + return 0; +} + +/* + * Function irport_is_receiving (self) + * + * Returns true is we are currently receiving data + * + */ +static inline int irport_is_receiving(struct irport_cb *self) +{ + return (self->rx_buff.state != OUTSIDE_FRAME); +} + +/* + * Function irport_set_dtr_rts (tty, dtr, rts) + * + * This function can be used by dongles etc. to set or reset the status + * of the dtr and rts lines + */ +static int irport_set_dtr_rts(struct net_device *dev, int dtr, int rts) +{ + struct irport_cb *self = dev->priv; + int iobase; + + IRDA_ASSERT(self != NULL, return -1;); + + iobase = self->io.sir_base; + + if (dtr) + dtr = UART_MCR_DTR; + if (rts) + rts = UART_MCR_RTS; + + outb(dtr|rts|UART_MCR_OUT2, iobase+UART_MCR); + + return 0; +} + +static int irport_raw_write(struct net_device *dev, __u8 *buf, int len) +{ + struct irport_cb *self = (struct irport_cb *) dev->priv; + int actual = 0; + int iobase; + + IRDA_ASSERT(self != NULL, return -1;); + + iobase = self->io.sir_base; + + /* Tx FIFO should be empty! */ + if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) { + IRDA_DEBUG( 0, "%s(), failed, fifo not empty!\n", __FUNCTION__); + return -1; + } + + /* Fill FIFO with current frame */ + while (actual < len) { + /* Transmit next byte */ + outb(buf[actual], iobase+UART_TX); + actual++; + } + + return actual; +} + +/* + * Function irport_net_ioctl (dev, rq, cmd) + * + * Process IOCTL commands for this device + * + */ +static int irport_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct irport_cb *self; + dongle_t *dongle; + unsigned long flags; + int ret = 0; + + IRDA_ASSERT(dev != NULL, return -1;); + + self = dev->priv; + + IRDA_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 + irda_task_execute(self, __irport_change_speed, NULL, + NULL, (void *) irq->ifr_baudrate); + break; + case SIOCSDONGLE: /* Set dongle */ + if (!capable(CAP_NET_ADMIN)) { + 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); + if (!dongle) + break; + + dongle->set_mode = NULL; + dongle->read = NULL; + dongle->write = irport_raw_write; + dongle->set_dtr_rts = irport_set_dtr_rts; + + /* Now initialize the dongle! */ + dongle->issue->open(dongle, &self->qos); + + /* Reset dongle */ + irda_task_execute(dongle, dongle->issue->reset, NULL, NULL, + NULL); + + /* Make dongle available to driver only now to avoid + * race conditions - Jean II */ + self->dongle = dongle; + 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 = irport_is_receiving(self); + break; + case SIOCSDTRRTS: + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + + /* No real need to lock... */ + spin_lock_irqsave(&self->lock, flags); + irport_set_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts); + spin_unlock_irqrestore(&self->lock, flags); + break; + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static struct net_device_stats *irport_net_get_stats(struct net_device *dev) +{ + struct irport_cb *self = (struct irport_cb *) dev->priv; + + return &self->stats; +} + +static int __init irport_init(void) +{ + int i; + + for (i=0; (io[i] < 2000) && (i < 4); i++) { + if (irport_open(i, io[i], irq[i]) != NULL) + return 0; + } + /* + * Maybe something failed, but we can still be usable for FIR drivers + */ + return 0; +} + +/* + * Function irport_cleanup () + * + * Close all configured ports + * + */ +static void __exit irport_cleanup(void) +{ + int i; + + IRDA_DEBUG( 4, "%s()\n", __FUNCTION__); + + for (i=0; i < 4; i++) { + if (dev_self[i]) + irport_close(dev_self[i]); + } +} + +MODULE_PARM(io, "1-4i"); +MODULE_PARM_DESC(io, "Base I/O addresses"); +MODULE_PARM(irq, "1-4i"); +MODULE_PARM_DESC(irq, "IRQ lines"); + +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("Half duplex serial driver for IrDA SIR mode"); +MODULE_LICENSE("GPL"); + +module_init(irport_init); +module_exit(irport_cleanup); + diff --git a/drivers/net/irda/irport.h b/drivers/net/irda/irport.h new file mode 100644 index 000000000000..fc89c8c3dd7f --- /dev/null +++ b/drivers/net/irda/irport.h @@ -0,0 +1,80 @@ +/********************************************************************* + * + * Filename: irport.h + * Version: 0.1 + * Description: Serial driver for IrDA + * Status: Experimental. + * Author: Dag Brattli <dagb@cs.uit.no> + * Created at: Sun Aug 3 13:49:59 1997 + * Modified at: Fri Jan 14 10:21:10 2000 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1997, 1998-2000 Dag Brattli <dagb@cs.uit.no> + * 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. + * + ********************************************************************/ + +#ifndef IRPORT_H +#define IRPORT_H + +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/types.h> +#include <linux/spinlock.h> + +#include <net/irda/irda_device.h> + +#define SPEED_DEFAULT 9600 +#define SPEED_MAX 115200 + +/* + * These are the supported serial types. + */ +#define PORT_UNKNOWN 0 +#define PORT_8250 1 +#define PORT_16450 2 +#define PORT_16550 3 +#define PORT_16550A 4 +#define PORT_CIRRUS 5 +#define PORT_16650 6 +#define PORT_MAX 6 + +#define FRAME_MAX_SIZE 2048 + +struct irport_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 attached 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 */ + dongle_t *dongle; /* Dongle driver */ + + __u32 flags; /* Interface flags */ + __u32 new_speed; + int mode; + int index; /* Instance index */ + int transmitting; /* Are we transmitting ? */ + + spinlock_t lock; /* For serializing operations */ + + /* For piggyback drivers */ + void *priv; + void (*change_speed)(void *priv, __u32 speed); + int (*interrupt)(int irq, void *dev_id, struct pt_regs *regs); +}; + +#endif /* IRPORT_H */ diff --git a/drivers/net/irda/irtty-sir.c b/drivers/net/irda/irtty-sir.c new file mode 100644 index 000000000000..7d23aa375908 --- /dev/null +++ b/drivers/net/irda/irtty-sir.c @@ -0,0 +1,642 @@ +/********************************************************************* + * + * Filename: irtty-sir.c + * Version: 2.0 + * Description: IrDA line discipline implementation + * Status: Experimental. + * Author: Dag Brattli <dagb@cs.uit.no> + * Created at: Tue Dec 9 21:18:38 1997 + * Modified at: Sun Oct 27 22:13:30 2002 + * Modified by: Martin Diehl <mad@mdiehl.de> + * Sources: slip.c by Laurence Culhane, <loz@holmes.demon.co.uk> + * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/tty.h> +#include <linux/init.h> +#include <asm/uaccess.h> +#include <linux/smp_lock.h> +#include <linux/delay.h> + +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> + +#include "sir-dev.h" +#include "irtty-sir.h" + +static int qos_mtt_bits = 0x03; /* 5 ms or more */ + +module_param(qos_mtt_bits, int, 0); +MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); + +/* ------------------------------------------------------- */ + +/* 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; + + IRDA_ASSERT(priv != NULL, return -1;); + IRDA_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; + + IRDA_ASSERT(priv != NULL, return;); + IRDA_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 { + msleep(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; + + IRDA_ASSERT(priv != NULL, return -1;); + IRDA_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 set = 0; + int clear = 0; + + IRDA_ASSERT(priv != NULL, return -1;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -1;); + + if (rts) + set |= TIOCM_RTS; + else + clear |= TIOCM_RTS; + if (dtr) + set |= TIOCM_DTR; + else + clear |= TIOCM_DTR; + + /* + * We can't use ioctl() because it expects a non-null file structure, + * and we don't have that here. + * This function is not yet defined for all tty driver, so + * let's be careful... Jean II + */ + IRDA_ASSERT(priv->tty->driver->tiocmset != NULL, return -1;); + priv->tty->driver->tiocmset(priv->tty, NULL, set, clear); + + 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. + */ + +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; + + IRDA_ASSERT(priv != NULL, return -1;); + IRDA_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, 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; + + IRDA_ASSERT(priv != NULL, return;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return;); + + if (unlikely(count==0)) /* yes, this happens */ + return; + + dev = priv->dev; + if (!dev) { + IRDA_WARNING("%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; + + IRDA_ASSERT(priv != NULL, return 0;); + IRDA_ASSERT(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; + + IRDA_ASSERT(priv != NULL, return;); + IRDA_ASSERT(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(); +} + +/*****************************************************************/ + +/* serialize ldisc open/close with sir_dev */ +static DECLARE_MUTEX(irtty_sem); + +/* 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; +} + +/* ------------------------------------------------------- */ + +static 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 err = 0; + + IRDA_ASSERT(priv != NULL, return -ENODEV;); + IRDA_ASSERT(priv->magic == IRTTY_MAGIC, return -EBADR;); + + IRDA_DEBUG(3, "%s(cmd=0x%X)\n", __FUNCTION__, cmd); + + dev = priv->dev; + IRDA_ASSERT(dev != NULL, return -1;); + + 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: + IRDA_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 __user *)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; + int ret = 0; + + /* Module stuff handled via irda_ldisc.owner - Jean II */ + + /* 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); + + /* 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, tty->name); + 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); + + IRDA_DEBUG(0, "%s - %s: irda line discipline opened\n", __FUNCTION__, tty->name); + + return 0; + +out_put: + sirdev_put_instance(dev); +out: + 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; + + IRDA_ASSERT(priv != NULL, return;); + IRDA_ASSERT(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 = NULL; + + 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); + + IRDA_DEBUG(0, "%s - %s: irda line discipline closed\n", __FUNCTION__, tty->name); +} + +/* ------------------------------------------------------- */ + +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, + .owner = THIS_MODULE, +}; + +/* ------------------------------------------------------- */ + +static int __init irtty_sir_init(void) +{ + int err; + + if ((err = tty_register_ldisc(N_IRDA, &irda_ldisc)) != 0) + IRDA_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))) { + IRDA_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 <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("IrDA TTY device driver"); +MODULE_ALIAS_LDISC(N_IRDA); +MODULE_LICENSE("GPL"); + diff --git a/drivers/net/irda/irtty-sir.h b/drivers/net/irda/irtty-sir.h new file mode 100644 index 000000000000..b132d8f6eb13 --- /dev/null +++ b/drivers/net/irda/irtty-sir.h @@ -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 <net/irda/irda.h> +#include <net/irda/irda_device.h> // 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 --git a/drivers/net/irda/litelink-sir.c b/drivers/net/irda/litelink-sir.c new file mode 100644 index 000000000000..73261c54bbfd --- /dev/null +++ b/drivers/net/irda/litelink-sir.c @@ -0,0 +1,209 @@ +/********************************************************************* + * + * Filename: litelink.c + * Version: 1.1 + * Description: Driver for the Parallax LiteLink dongle + * Status: Stable + * Author: Dag Brattli <dagb@cs.uit.no> + * Created at: Fri May 7 12:50:33 1999 + * Modified at: Fri Dec 17 09:14:23 1999 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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 + * + ********************************************************************/ + +/* + * Modified at: Thu Jan 15 2003 + * Modified by: Eugene Crosser <crosser@average.org> + * + * Convert to "new" IRDA infrastructure for kernel 2.6 + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> + +#include <net/irda/irda.h> + +#include "sir-dev.h" + +#define MIN_DELAY 25 /* 15 us, but wait a little more to be sure */ +#define MAX_DELAY 10000 /* 1 ms */ + +static int litelink_open(struct sir_dev *dev); +static int litelink_close(struct sir_dev *dev); +static int litelink_change_speed(struct sir_dev *dev, unsigned speed); +static int litelink_reset(struct sir_dev *dev); + +/* These are the baudrates supported - 9600 must be last one! */ +static unsigned baud_rates[] = { 115200, 57600, 38400, 19200, 9600 }; + +static struct dongle_driver litelink = { + .owner = THIS_MODULE, + .driver_name = "Parallax LiteLink", + .type = IRDA_LITELINK_DONGLE, + .open = litelink_open, + .close = litelink_close, + .reset = litelink_reset, + .set_speed = litelink_change_speed, +}; + +static int __init litelink_sir_init(void) +{ + return irda_register_dongle(&litelink); +} + +static void __exit litelink_sir_cleanup(void) +{ + irda_unregister_dongle(&litelink); +} + +static int litelink_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* Power up dongle */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Set the speeds we can accept */ + qos->baud_rate.bits &= IR_115200|IR_57600|IR_38400|IR_19200|IR_9600; + qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */ + irda_qos_bits_to_value(qos); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int litelink_close(struct sir_dev *dev) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* Power off dongle */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function litelink_change_speed (task) + * + * Change speed of the Litelink dongle. To cycle through the available + * baud rates, pulse RTS low for a few ms. + */ +static int litelink_change_speed(struct sir_dev *dev, unsigned speed) +{ + int i; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* dongle already reset by irda-thread - current speed (dongle and + * port) is the default speed (115200 for litelink!) + */ + + /* Cycle through avaiable baudrates until we reach the correct one */ + for (i = 0; baud_rates[i] != speed; i++) { + + /* end-of-list reached due to invalid speed request */ + if (baud_rates[i] == 9600) + break; + + /* Set DTR, clear RTS */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + + /* Set DTR, Set RTS */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + } + + dev->speed = baud_rates[i]; + + /* invalid baudrate should not happen - but if, we return -EINVAL and + * the dongle configured for 9600 so the stack has a chance to recover + */ + + return (dev->speed == speed) ? 0 : -EINVAL; +} + +/* + * Function litelink_reset (task) + * + * Reset the Litelink type dongle. + * + */ +static int litelink_reset(struct sir_dev *dev) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* probably the power-up can be dropped here, but with only + * 15 usec delay it's not worth the risk unless somebody with + * the hardware confirms it doesn't break anything... + */ + + /* Power on dongle */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + + /* Clear RTS to reset dongle */ + sirdev_set_dtr_rts(dev, TRUE, FALSE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + + /* This dongles speed defaults to 115200 bps */ + dev->speed = 115200; + + return 0; +} + +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("Parallax Litelink dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-5"); /* IRDA_LITELINK_DONGLE */ + +/* + * Function init_module (void) + * + * Initialize Litelink module + * + */ +module_init(litelink_sir_init); + +/* + * Function cleanup_module (void) + * + * Cleanup Litelink module + * + */ +module_exit(litelink_sir_cleanup); diff --git a/drivers/net/irda/litelink.c b/drivers/net/irda/litelink.c new file mode 100644 index 000000000000..7db11431d0f4 --- /dev/null +++ b/drivers/net/irda/litelink.c @@ -0,0 +1,179 @@ +/********************************************************************* + * + * Filename: litelink.c + * Version: 1.1 + * Description: Driver for the Parallax LiteLink dongle + * Status: Stable + * Author: Dag Brattli <dagb@cs.uit.no> + * Created at: Fri May 7 12:50:33 1999 + * Modified at: Fri Dec 17 09:14:23 1999 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/init.h> + +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> + +#define MIN_DELAY 25 /* 15 us, but wait a little more to be sure */ +#define MAX_DELAY 10000 /* 1 ms */ + +static void litelink_open(dongle_t *self, struct qos_info *qos); +static void litelink_close(dongle_t *self); +static int litelink_change_speed(struct irda_task *task); +static int litelink_reset(struct irda_task *task); + +/* These are the baudrates supported */ +static __u32 baud_rates[] = { 115200, 57600, 38400, 19200, 9600 }; + +static struct dongle_reg dongle = { + .type = IRDA_LITELINK_DONGLE, + .open = litelink_open, + .close = litelink_close, + .reset = litelink_reset, + .change_speed = litelink_change_speed, + .owner = THIS_MODULE, +}; + +static int __init litelink_init(void) +{ + return irda_device_register_dongle(&dongle); +} + +static void __exit litelink_cleanup(void) +{ + irda_device_unregister_dongle(&dongle); +} + +static void litelink_open(dongle_t *self, struct qos_info *qos) +{ + qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + qos->min_turn_time.bits = 0x7f; /* Needs 0.01 ms */ +} + +static void litelink_close(dongle_t *self) +{ + /* Power off dongle */ + self->set_dtr_rts(self->dev, FALSE, FALSE); +} + +/* + * Function litelink_change_speed (task) + * + * Change speed of the Litelink dongle. To cycle through the available + * baud rates, pulse RTS low for a few ms. + */ +static int litelink_change_speed(struct irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + __u32 speed = (__u32) task->param; + int i; + + /* Clear RTS to reset dongle */ + self->set_dtr_rts(self->dev, TRUE, FALSE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + + /* Go back to normal mode */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + + /* Cycle through avaiable baudrates until we reach the correct one */ + for (i=0; i<5 && baud_rates[i] != speed; i++) { + /* Set DTR, clear RTS */ + self->set_dtr_rts(self->dev, FALSE, TRUE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + + /* Set DTR, Set RTS */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + } + irda_task_next_state(task, IRDA_TASK_DONE); + + return 0; +} + +/* + * Function litelink_reset (task) + * + * Reset the Litelink type dongle. + * + */ +static int litelink_reset(struct irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + + /* Power on dongle */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + + /* Clear RTS to reset dongle */ + self->set_dtr_rts(self->dev, TRUE, FALSE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + + /* Go back to normal mode */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + + /* This dongles speed defaults to 115200 bps */ + self->speed = 115200; + + irda_task_next_state(task, IRDA_TASK_DONE); + + return 0; +} + +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("Parallax Litelink dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-5"); /* IRDA_LITELINK_DONGLE */ + +/* + * Function init_module (void) + * + * Initialize Litelink module + * + */ +module_init(litelink_init); + +/* + * Function cleanup_module (void) + * + * Cleanup Litelink module + * + */ +module_exit(litelink_cleanup); diff --git a/drivers/net/irda/ma600-sir.c b/drivers/net/irda/ma600-sir.c new file mode 100644 index 000000000000..ebed168b7da6 --- /dev/null +++ b/drivers/net/irda/ma600-sir.c @@ -0,0 +1,264 @@ +/********************************************************************* + * + * Filename: ma600.c + * Version: 0.1 + * Description: Implementation of the MA600 dongle + * Status: Experimental. + * Author: Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95 + * Created at: Sat Jun 10 20:02:35 2000 + * Modified at: Sat Aug 16 09:34:13 2003 + * Modified by: Martin Diehl <mad@mdiehl.de> (modified for new sir_dev) + * + * Note: very thanks to Mr. Maru Wang <maru@mobileaction.com.tw> for providing + * information on the MA600 dongle + * + * Copyright (c) 2000 Leung, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/sched.h> + +#include <net/irda/irda.h> + +#include "sir-dev.h" + +static int ma600_open(struct sir_dev *); +static int ma600_close(struct sir_dev *); +static int ma600_change_speed(struct sir_dev *, unsigned); +static int ma600_reset(struct sir_dev *); + +/* control byte for MA600 */ +#define MA600_9600 0x00 +#define MA600_19200 0x01 +#define MA600_38400 0x02 +#define MA600_57600 0x03 +#define MA600_115200 0x04 +#define MA600_DEV_ID1 0x05 +#define MA600_DEV_ID2 0x06 +#define MA600_2400 0x08 + +static struct dongle_driver ma600 = { + .owner = THIS_MODULE, + .driver_name = "MA600", + .type = IRDA_MA600_DONGLE, + .open = ma600_open, + .close = ma600_close, + .reset = ma600_reset, + .set_speed = ma600_change_speed, +}; + + +static int __init ma600_sir_init(void) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + return irda_register_dongle(&ma600); +} + +static void __exit ma600_sir_cleanup(void) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + irda_unregister_dongle(&ma600); +} + +/* + Power on: + (0) Clear RTS and DTR for 1 second + (1) Set RTS and DTR for 1 second + (2) 9600 bps now + Note: assume RTS, DTR are clear before +*/ +static int ma600_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Explicitly set the speeds we can accept */ + qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400 + |IR_57600|IR_115200; + /* Hm, 0x01 means 10ms - for >= 1ms we would need 0x07 */ + qos->min_turn_time.bits = 0x01; /* Needs at least 1 ms */ + irda_qos_bits_to_value(qos); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int ma600_close(struct sir_dev *dev) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* Power off dongle */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +static __u8 get_control_byte(__u32 speed) +{ + __u8 byte; + + switch (speed) { + default: + case 115200: + byte = MA600_115200; + break; + case 57600: + byte = MA600_57600; + break; + case 38400: + byte = MA600_38400; + break; + case 19200: + byte = MA600_19200; + break; + case 9600: + byte = MA600_9600; + break; + case 2400: + byte = MA600_2400; + break; + } + + return byte; +} + +/* + * Function ma600_change_speed (dev, speed) + * + * Set the speed for the MA600 type dongle. + * + * The dongle has already been reset to a known state (dongle default) + * We cycle through speeds by pulsing RTS low and then high. + */ + +/* + * Function ma600_change_speed (dev, speed) + * + * Set the speed for the MA600 type dongle. + * + * Algorithm + * 1. Reset (already done by irda thread state machine) + * 2. clear RTS, set DTR and wait for 1ms + * 3. send Control Byte to the MA600 through TXD to set new baud rate + * wait until the stop bit of Control Byte is sent (for 9600 baud rate, + * it takes about 10 msec) + * 4. set RTS, set DTR (return to NORMAL Operation) + * 5. wait at least 10 ms, new setting (baud rate, etc) takes effect here + * after + */ + +/* total delays are only about 20ms - let's just sleep for now to + * avoid the state machine complexity before we get things working + */ + +static int ma600_change_speed(struct sir_dev *dev, unsigned speed) +{ + u8 byte; + + IRDA_DEBUG(2, "%s(), speed=%d (was %d)\n", __FUNCTION__, + speed, dev->speed); + + /* dongle already reset, dongle and port at default speed (9600) */ + + /* Set RTS low for 1 ms */ + sirdev_set_dtr_rts(dev, TRUE, FALSE); + mdelay(1); + + /* Write control byte */ + byte = get_control_byte(speed); + sirdev_raw_write(dev, &byte, sizeof(byte)); + + /* Wait at least 10ms: fake wait_until_sent - 10 bits at 9600 baud*/ + msleep(15); /* old ma600 uses 15ms */ + +#if 1 + /* read-back of the control byte. ma600 is the first dongle driver + * which uses this so there might be some unidentified issues. + * Disable this in case of problems with readback. + */ + + sirdev_raw_read(dev, &byte, sizeof(byte)); + if (byte != get_control_byte(speed)) { + IRDA_WARNING("%s(): bad control byte read-back %02x != %02x\n", + __FUNCTION__, (unsigned) byte, + (unsigned) get_control_byte(speed)); + return -1; + } + else + IRDA_DEBUG(2, "%s() control byte write read OK\n", __FUNCTION__); +#endif + + /* Set DTR, Set RTS */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Wait at least 10ms */ + msleep(10); + + /* dongle is now switched to the new speed */ + dev->speed = speed; + + return 0; +} + +/* + * Function ma600_reset (dev) + * + * This function resets the ma600 dongle. + * + * Algorithm: + * 0. DTR=0, RTS=1 and wait 10 ms + * 1. DTR=1, RTS=1 and wait 10 ms + * 2. 9600 bps now + */ + +/* total delays are only about 20ms - let's just sleep for now to + * avoid the state machine complexity before we get things working + */ + +int ma600_reset(struct sir_dev *dev) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* Reset the dongle : set DTR low for 10 ms */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + msleep(10); + + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + msleep(10); + + dev->speed = 9600; /* That's the dongle-default */ + + return 0; +} + +MODULE_AUTHOR("Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95"); +MODULE_DESCRIPTION("MA600 dongle driver version 0.1"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-11"); /* IRDA_MA600_DONGLE */ + +module_init(ma600_sir_init); +module_exit(ma600_sir_cleanup); + diff --git a/drivers/net/irda/ma600.c b/drivers/net/irda/ma600.c new file mode 100644 index 000000000000..f5e6836667fd --- /dev/null +++ b/drivers/net/irda/ma600.c @@ -0,0 +1,354 @@ +/********************************************************************* + * + * Filename: ma600.c + * Version: 0.1 + * Description: Implementation of the MA600 dongle + * Status: Experimental. + * Author: Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95 + * Created at: Sat Jun 10 20:02:35 2000 + * Modified at: + * Modified by: + * + * Note: very thanks to Mr. Maru Wang <maru@mobileaction.com.tw> for providing + * information on the MA600 dongle + * + * Copyright (c) 2000 Leung, 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 + * + ********************************************************************/ + +/* define this macro for release version */ +//#define NDEBUG + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/init.h> + +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> + +#ifndef NDEBUG + #undef IRDA_DEBUG + #define IRDA_DEBUG(n, args...) (printk(KERN_DEBUG args)) + + #undef ASSERT + #define ASSERT(expr, func) \ + if(!(expr)) { \ + printk( "Assertion failed! %s,%s,%s,line=%d\n",\ + #expr,__FILE__,__FUNCTION__,__LINE__); \ + func} +#endif + +/* convert hex value to ascii hex */ +static const char hexTbl[] = "0123456789ABCDEF"; + + +static void ma600_open(dongle_t *self, struct qos_info *qos); +static void ma600_close(dongle_t *self); +static int ma600_change_speed(struct irda_task *task); +static int ma600_reset(struct irda_task *task); + +/* control byte for MA600 */ +#define MA600_9600 0x00 +#define MA600_19200 0x01 +#define MA600_38400 0x02 +#define MA600_57600 0x03 +#define MA600_115200 0x04 +#define MA600_DEV_ID1 0x05 +#define MA600_DEV_ID2 0x06 +#define MA600_2400 0x08 + +static struct dongle_reg dongle = { + .type = IRDA_MA600_DONGLE, + .open = ma600_open, + .close = ma600_close, + .reset = ma600_reset, + .change_speed = ma600_change_speed, + .owner = THIS_MODULE, +}; + +static int __init ma600_init(void) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + return irda_device_register_dongle(&dongle); +} + +static void __exit ma600_cleanup(void) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + irda_device_unregister_dongle(&dongle); +} + +/* + Power on: + (0) Clear RTS and DTR for 1 second + (1) Set RTS and DTR for 1 second + (2) 9600 bps now + Note: assume RTS, DTR are clear before +*/ +static void ma600_open(dongle_t *self, struct qos_info *qos) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + qos->baud_rate.bits &= IR_2400|IR_9600|IR_19200|IR_38400 + |IR_57600|IR_115200; + qos->min_turn_time.bits = 0x01; /* Needs at least 1 ms */ + irda_qos_bits_to_value(qos); + + //self->set_dtr_rts(self->dev, FALSE, FALSE); + // should wait 1 second + + self->set_dtr_rts(self->dev, TRUE, TRUE); + // should wait 1 second +} + +static void ma600_close(dongle_t *self) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* Power off dongle */ + self->set_dtr_rts(self->dev, FALSE, FALSE); +} + +static __u8 get_control_byte(__u32 speed) +{ + __u8 byte; + + switch (speed) { + default: + case 115200: + byte = MA600_115200; + break; + case 57600: + byte = MA600_57600; + break; + case 38400: + byte = MA600_38400; + break; + case 19200: + byte = MA600_19200; + break; + case 9600: + byte = MA600_9600; + break; + case 2400: + byte = MA600_2400; + break; + } + + return byte; +} + +/* + * Function ma600_change_speed (dev, state, speed) + * + * Set the speed for the MA600 type dongle. Warning, this + * function must be called with a process context! + * + * Algorithm + * 1. Reset + * 2. clear RTS, set DTR and wait for 1ms + * 3. send Control Byte to the MA600 through TXD to set new baud rate + * wait until the stop bit of Control Byte is sent (for 9600 baud rate, + * it takes about 10 msec) + * 4. set RTS, set DTR (return to NORMAL Operation) + * 5. wait at least 10 ms, new setting (baud rate, etc) takes effect here + * after + */ +static int ma600_change_speed(struct irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + __u32 speed = (__u32) task->param; + static __u8 byte; + __u8 byte_echo; + int ret = 0; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + ASSERT(task != NULL, return -1;); + + if (self->speed_task && self->speed_task != task) { + IRDA_DEBUG(0, "%s(), busy!\n", __FUNCTION__); + return msecs_to_jiffies(10); + } else { + self->speed_task = task; + } + + switch (task->state) { + case IRDA_TASK_INIT: + case IRDA_TASK_CHILD_INIT: + /* + * Need to reset the dongle and go to 9600 bps before + * programming + */ + if (irda_task_execute(self, ma600_reset, NULL, task, + (void *) speed)) { + /* Dongle need more time to reset */ + irda_task_next_state(task, IRDA_TASK_CHILD_WAIT); + + /* give 1 second to finish */ + ret = msecs_to_jiffies(1000); + } else { + irda_task_next_state(task, IRDA_TASK_CHILD_DONE); + } + break; + + case IRDA_TASK_CHILD_WAIT: + IRDA_WARNING("%s(), resetting dongle timed out!\n", + __FUNCTION__); + ret = -1; + break; + + case IRDA_TASK_CHILD_DONE: + /* Set DTR, Clear RTS */ + self->set_dtr_rts(self->dev, TRUE, FALSE); + + ret = msecs_to_jiffies(1); /* Sleep 1 ms */ + irda_task_next_state(task, IRDA_TASK_WAIT); + break; + + case IRDA_TASK_WAIT: + speed = (__u32) task->param; + byte = get_control_byte(speed); + + /* Write control byte */ + self->write(self->dev, &byte, sizeof(byte)); + + irda_task_next_state(task, IRDA_TASK_WAIT1); + + /* Wait at least 10 ms */ + ret = msecs_to_jiffies(15); + break; + + case IRDA_TASK_WAIT1: + /* Read control byte echo */ + self->read(self->dev, &byte_echo, sizeof(byte_echo)); + + if(byte != byte_echo) { + /* if control byte != echo, I don't know what to do */ + printk(KERN_WARNING "%s() control byte written != read!\n", __FUNCTION__); + printk(KERN_WARNING "control byte = 0x%c%c\n", + hexTbl[(byte>>4)&0x0f], hexTbl[byte&0x0f]); + printk(KERN_WARNING "byte echo = 0x%c%c\n", + hexTbl[(byte_echo>>4) & 0x0f], + hexTbl[byte_echo & 0x0f]); + #ifndef NDEBUG + } else { + IRDA_DEBUG(2, "%s() control byte write read OK\n", __FUNCTION__); + #endif + } + + /* Set DTR, Set RTS */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + + irda_task_next_state(task, IRDA_TASK_WAIT2); + + /* Wait at least 10 ms */ + ret = msecs_to_jiffies(10); + break; + + case IRDA_TASK_WAIT2: + irda_task_next_state(task, IRDA_TASK_DONE); + self->speed_task = NULL; + break; + + default: + IRDA_ERROR("%s(), unknown state %d\n", + __FUNCTION__, task->state); + irda_task_next_state(task, IRDA_TASK_DONE); + self->speed_task = NULL; + ret = -1; + break; + } + return ret; +} + +/* + * Function ma600_reset (driver) + * + * This function resets the ma600 dongle. Warning, this function + * must be called with a process context!! + * + * Algorithm: + * 0. DTR=0, RTS=1 and wait 10 ms + * 1. DTR=1, RTS=1 and wait 10 ms + * 2. 9600 bps now + */ +int ma600_reset(struct irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + int ret = 0; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + ASSERT(task != NULL, return -1;); + + if (self->reset_task && self->reset_task != task) { + IRDA_DEBUG(0, "%s(), busy!\n", __FUNCTION__); + return msecs_to_jiffies(10); + } else + self->reset_task = task; + + switch (task->state) { + case IRDA_TASK_INIT: + /* Clear DTR and Set RTS */ + self->set_dtr_rts(self->dev, FALSE, TRUE); + irda_task_next_state(task, IRDA_TASK_WAIT1); + ret = msecs_to_jiffies(10); /* Sleep 10 ms */ + break; + case IRDA_TASK_WAIT1: + /* Set DTR and RTS */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + irda_task_next_state(task, IRDA_TASK_WAIT2); + ret = msecs_to_jiffies(10); /* Sleep 10 ms */ + break; + case IRDA_TASK_WAIT2: + irda_task_next_state(task, IRDA_TASK_DONE); + self->reset_task = NULL; + break; + default: + IRDA_ERROR("%s(), unknown state %d\n", + __FUNCTION__, task->state); + irda_task_next_state(task, IRDA_TASK_DONE); + self->reset_task = NULL; + ret = -1; + } + return ret; +} + +MODULE_AUTHOR("Leung <95Etwl@alumni.ee.ust.hk> http://www.engsvr.ust/~eetwl95"); +MODULE_DESCRIPTION("MA600 dongle driver version 0.1"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-11"); /* IRDA_MA600_DONGLE */ + +/* + * Function init_module (void) + * + * Initialize MA600 module + * + */ +module_init(ma600_init); + +/* + * Function cleanup_module (void) + * + * Cleanup MA600 module + * + */ +module_exit(ma600_cleanup); + diff --git a/drivers/net/irda/mcp2120-sir.c b/drivers/net/irda/mcp2120-sir.c new file mode 100644 index 000000000000..67bd016e4df8 --- /dev/null +++ b/drivers/net/irda/mcp2120-sir.c @@ -0,0 +1,230 @@ +/********************************************************************* + * + * + * Filename: mcp2120.c + * Version: 1.0 + * Description: Implementation for the MCP2120 (Microchip) + * Status: Experimental. + * Author: Felix Tang (tangf@eyetap.org) + * Created at: Sun Mar 31 19:32:12 EST 2002 + * Based on code by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 2002 Felix Tang, 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. + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> + +#include <net/irda/irda.h> + +#include "sir-dev.h" + +static int mcp2120_reset(struct sir_dev *dev); +static int mcp2120_open(struct sir_dev *dev); +static int mcp2120_close(struct sir_dev *dev); +static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed); + +#define MCP2120_9600 0x87 +#define MCP2120_19200 0x8B +#define MCP2120_38400 0x85 +#define MCP2120_57600 0x83 +#define MCP2120_115200 0x81 + +#define MCP2120_COMMIT 0x11 + +static struct dongle_driver mcp2120 = { + .owner = THIS_MODULE, + .driver_name = "Microchip MCP2120", + .type = IRDA_MCP2120_DONGLE, + .open = mcp2120_open, + .close = mcp2120_close, + .reset = mcp2120_reset, + .set_speed = mcp2120_change_speed, +}; + +static int __init mcp2120_sir_init(void) +{ + return irda_register_dongle(&mcp2120); +} + +static void __exit mcp2120_sir_cleanup(void) +{ + irda_unregister_dongle(&mcp2120); +} + +static int mcp2120_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* seems no explicit power-on required here and reset switching it on anyway */ + + qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + qos->min_turn_time.bits = 0x01; + irda_qos_bits_to_value(qos); + + return 0; +} + +static int mcp2120_close(struct sir_dev *dev) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* Power off dongle */ + /* reset and inhibit mcp2120 */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + // sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function mcp2120_change_speed (dev, speed) + * + * Set the speed for the MCP2120. + * + */ + +#define MCP2120_STATE_WAIT_SPEED (SIRDEV_STATE_DONGLE_SPEED+1) + +static int mcp2120_change_speed(struct sir_dev *dev, unsigned speed) +{ + unsigned state = dev->fsm.substate; + unsigned delay = 0; + u8 control[2]; + static int ret = 0; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + switch (state) { + case SIRDEV_STATE_DONGLE_SPEED: + /* Set DTR to enter command mode */ + sirdev_set_dtr_rts(dev, TRUE, FALSE); + udelay(500); + + ret = 0; + switch (speed) { + default: + speed = 9600; + ret = -EINVAL; + /* fall through */ + case 9600: + control[0] = MCP2120_9600; + //printk("mcp2120 9600\n"); + break; + case 19200: + control[0] = MCP2120_19200; + //printk("mcp2120 19200\n"); + break; + case 34800: + control[0] = MCP2120_38400; + //printk("mcp2120 38400\n"); + break; + case 57600: + control[0] = MCP2120_57600; + //printk("mcp2120 57600\n"); + break; + case 115200: + control[0] = MCP2120_115200; + //printk("mcp2120 115200\n"); + break; + } + control[1] = MCP2120_COMMIT; + + /* Write control bytes */ + sirdev_raw_write(dev, control, 2); + dev->speed = speed; + + state = MCP2120_STATE_WAIT_SPEED; + delay = 100; + //printk("mcp2120_change_speed: dongle_speed\n"); + break; + + case MCP2120_STATE_WAIT_SPEED: + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + //printk("mcp2120_change_speed: mcp_wait\n"); + break; + + default: + IRDA_ERROR("%s(), undefine state %d\n", __FUNCTION__, state); + ret = -EINVAL; + break; + } + dev->fsm.substate = state; + return (delay > 0) ? delay : ret; +} + +/* + * Function mcp2120_reset (driver) + * + * This function resets the mcp2120 dongle. + * + * Info: -set RTS to reset mcp2120 + * -set DTR to set mcp2120 software command mode + * -mcp2120 defaults to 9600 baud after reset + * + * Algorithm: + * 0. Set RTS to reset mcp2120. + * 1. Clear RTS and wait for device reset timer of 30 ms (max). + * + */ + +#define MCP2120_STATE_WAIT1_RESET (SIRDEV_STATE_DONGLE_RESET+1) +#define MCP2120_STATE_WAIT2_RESET (SIRDEV_STATE_DONGLE_RESET+2) + +static int mcp2120_reset(struct sir_dev *dev) +{ + unsigned state = dev->fsm.substate; + unsigned delay = 0; + int ret = 0; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + switch (state) { + case SIRDEV_STATE_DONGLE_RESET: + //printk("mcp2120_reset: dongle_reset\n"); + /* Reset dongle by setting RTS*/ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + state = MCP2120_STATE_WAIT1_RESET; + delay = 50; + break; + + case MCP2120_STATE_WAIT1_RESET: + //printk("mcp2120_reset: mcp2120_wait1\n"); + /* clear RTS and wait for at least 30 ms. */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + state = MCP2120_STATE_WAIT2_RESET; + delay = 50; + break; + + case MCP2120_STATE_WAIT2_RESET: + //printk("mcp2120_reset mcp2120_wait2\n"); + /* Go back to normal mode */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + break; + + default: + IRDA_ERROR("%s(), undefined state %d\n", __FUNCTION__, state); + ret = -EINVAL; + break; + } + dev->fsm.substate = state; + return (delay > 0) ? delay : ret; +} + +MODULE_AUTHOR("Felix Tang <tangf@eyetap.org>"); +MODULE_DESCRIPTION("Microchip MCP2120"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-9"); /* IRDA_MCP2120_DONGLE */ + +module_init(mcp2120_sir_init); +module_exit(mcp2120_sir_cleanup); diff --git a/drivers/net/irda/mcp2120.c b/drivers/net/irda/mcp2120.c new file mode 100644 index 000000000000..5e6199eeef4f --- /dev/null +++ b/drivers/net/irda/mcp2120.c @@ -0,0 +1,240 @@ +/********************************************************************* + * + * + * Filename: mcp2120.c + * Version: 1.0 + * Description: Implementation for the MCP2120 (Microchip) + * Status: Experimental. + * Author: Felix Tang (tangf@eyetap.org) + * Created at: Sun Mar 31 19:32:12 EST 2002 + * Based on code by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 2002 Felix Tang, 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. + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/init.h> + +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> + +static int mcp2120_reset(struct irda_task *task); +static void mcp2120_open(dongle_t *self, struct qos_info *qos); +static void mcp2120_close(dongle_t *self); +static int mcp2120_change_speed(struct irda_task *task); + +#define MCP2120_9600 0x87 +#define MCP2120_19200 0x8B +#define MCP2120_38400 0x85 +#define MCP2120_57600 0x83 +#define MCP2120_115200 0x81 + +#define MCP2120_COMMIT 0x11 + +static struct dongle_reg dongle = { + .type = IRDA_MCP2120_DONGLE, + .open = mcp2120_open, + .close = mcp2120_close, + .reset = mcp2120_reset, + .change_speed = mcp2120_change_speed, + .owner = THIS_MODULE, +}; + +static int __init mcp2120_init(void) +{ + return irda_device_register_dongle(&dongle); +} + +static void __exit mcp2120_cleanup(void) +{ + irda_device_unregister_dongle(&dongle); +} + +static void mcp2120_open(dongle_t *self, struct qos_info *qos) +{ + qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + qos->min_turn_time.bits = 0x01; +} + +static void mcp2120_close(dongle_t *self) +{ + /* Power off dongle */ + /* reset and inhibit mcp2120 */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + //self->set_dtr_rts(self->dev, FALSE, FALSE); +} + +/* + * Function mcp2120_change_speed (dev, speed) + * + * Set the speed for the MCP2120. + * + */ +static int mcp2120_change_speed(struct irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + __u32 speed = (__u32) task->param; + __u8 control[2]; + int ret = 0; + + self->speed_task = task; + + switch (task->state) { + case IRDA_TASK_INIT: + /* Need to reset the dongle and go to 9600 bps before + programming */ + //printk("Dmcp2120_change_speed irda_task_init\n"); + if (irda_task_execute(self, mcp2120_reset, NULL, task, + (void *) speed)) + { + /* Dongle need more time to reset */ + irda_task_next_state(task, IRDA_TASK_CHILD_WAIT); + + /* Give reset 1 sec to finish */ + ret = msecs_to_jiffies(1000); + } + break; + case IRDA_TASK_CHILD_WAIT: + IRDA_WARNING("%s(), resetting dongle timed out!\n", + __FUNCTION__); + ret = -1; + break; + case IRDA_TASK_CHILD_DONE: + /* Set DTR to enter command mode */ + self->set_dtr_rts(self->dev, TRUE, FALSE); + udelay(500); + + switch (speed) { + case 9600: + default: + control[0] = MCP2120_9600; + //printk("mcp2120 9600\n"); + break; + case 19200: + control[0] = MCP2120_19200; + //printk("mcp2120 19200\n"); + break; + case 34800: + control[0] = MCP2120_38400; + //printk("mcp2120 38400\n"); + break; + case 57600: + control[0] = MCP2120_57600; + //printk("mcp2120 57600\n"); + break; + case 115200: + control[0] = MCP2120_115200; + //printk("mcp2120 115200\n"); + break; + } + control[1] = MCP2120_COMMIT; + + /* Write control bytes */ + self->write(self->dev, control, 2); + + irda_task_next_state(task, IRDA_TASK_WAIT); + ret = msecs_to_jiffies(100); + //printk("mcp2120_change_speed irda_child_done\n"); + break; + case IRDA_TASK_WAIT: + /* Go back to normal mode */ + self->set_dtr_rts(self->dev, FALSE, FALSE); + irda_task_next_state(task, IRDA_TASK_DONE); + self->speed_task = NULL; + //printk("mcp2120_change_speed irda_task_wait\n"); + break; + default: + IRDA_ERROR("%s(), unknown state %d\n", + __FUNCTION__, task->state); + irda_task_next_state(task, IRDA_TASK_DONE); + self->speed_task = NULL; + ret = -1; + break; + } + return ret; +} + +/* + * Function mcp2120_reset (driver) + * + * This function resets the mcp2120 dongle. + * + * Info: -set RTS to reset mcp2120 + * -set DTR to set mcp2120 software command mode + * -mcp2120 defaults to 9600 baud after reset + * + * Algorithm: + * 0. Set RTS to reset mcp2120. + * 1. Clear RTS and wait for device reset timer of 30 ms (max). + * + */ + + +static int mcp2120_reset(struct irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + int ret = 0; + + self->reset_task = task; + + switch (task->state) { + case IRDA_TASK_INIT: + //printk("mcp2120_reset irda_task_init\n"); + /* Reset dongle by setting RTS*/ + self->set_dtr_rts(self->dev, TRUE, TRUE); + irda_task_next_state(task, IRDA_TASK_WAIT1); + ret = msecs_to_jiffies(50); + break; + case IRDA_TASK_WAIT1: + //printk("mcp2120_reset irda_task_wait1\n"); + /* clear RTS and wait for at least 30 ms. */ + self->set_dtr_rts(self->dev, FALSE, FALSE); + irda_task_next_state(task, IRDA_TASK_WAIT2); + ret = msecs_to_jiffies(50); + break; + case IRDA_TASK_WAIT2: + //printk("mcp2120_reset irda_task_wait2\n"); + /* Go back to normal mode */ + self->set_dtr_rts(self->dev, FALSE, FALSE); + irda_task_next_state(task, IRDA_TASK_DONE); + self->reset_task = NULL; + break; + default: + IRDA_ERROR("%s(), unknown state %d\n", + __FUNCTION__, task->state); + irda_task_next_state(task, IRDA_TASK_DONE); + self->reset_task = NULL; + ret = -1; + break; + } + return ret; +} + +MODULE_AUTHOR("Felix Tang <tangf@eyetap.org>"); +MODULE_DESCRIPTION("Microchip MCP2120"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-9"); /* IRDA_MCP2120_DONGLE */ + +/* + * Function init_module (void) + * + * Initialize MCP2120 module + * + */ +module_init(mcp2120_init); + +/* + * Function cleanup_module (void) + * + * Cleanup MCP2120 module + * + */ +module_exit(mcp2120_cleanup); diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c new file mode 100644 index 000000000000..805714ec9a8a --- /dev/null +++ b/drivers/net/irda/nsc-ircc.c @@ -0,0 +1,2222 @@ +/********************************************************************* + * + * Filename: nsc-ircc.c + * Version: 1.0 + * Description: Driver for the NSC PC'108 and PC'338 IrDA chipsets + * Status: Stable. + * Author: Dag Brattli <dagb@cs.uit.no> + * Created at: Sat Nov 7 21:43:15 1998 + * Modified at: Wed Mar 1 11:29:34 2000 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no> + * Copyright (c) 1998 Lichen Wang, <lwang@actisys.com> + * Copyright (c) 1998 Actisys Corp., www.actisys.com + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsų admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + * Notice that all functions that needs to access the chip in _any_ + * way, must save BSR register on entry, and restore it on exit. + * It is _very_ important to follow this policy! + * + * __u8 bank; + * + * bank = inb(iobase+BSR); + * + * do_your_stuff_here(); + * + * outb(bank, iobase+BSR); + * + * If you find bugs in this file, its very likely that the same bug + * will also be in w83977af_ir.c since the implementations are quite + * similar. + * + ********************************************************************/ + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/rtnetlink.h> +#include <linux/dma-mapping.h> + +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/byteorder.h> + +#include <linux/pm.h> + +#include <net/irda/wrapper.h> +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> + +#include "nsc-ircc.h" + +#define CHIP_IO_EXTENT 8 +#define BROKEN_DONGLE_ID + +static char *driver_name = "nsc-ircc"; + +/* Module parameters */ +static int qos_mtt_bits = 0x07; /* 1 ms or more */ +static int dongle_id; + +/* Use BIOS settions by default, but user may supply module parameters */ +static unsigned int io[] = { ~0, ~0, ~0, ~0 }; +static unsigned int irq[] = { 0, 0, 0, 0, 0 }; +static unsigned int dma[] = { 0, 0, 0, 0, 0 }; + +static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info); + +/* These are the known NSC chips */ +static nsc_chip_t chips[] = { +/* Name, {cfg registers}, chip id index reg, chip id expected value, revision mask */ + { "PC87108", { 0x150, 0x398, 0xea }, 0x05, 0x10, 0xf0, + nsc_ircc_probe_108, nsc_ircc_init_108 }, + { "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf8, + nsc_ircc_probe_338, nsc_ircc_init_338 }, + /* Contributed by Steffen Pingel - IBM X40 */ + { "PC8738x", { 0x164e, 0x4e, 0x0 }, 0x20, 0xf4, 0xff, + nsc_ircc_probe_39x, nsc_ircc_init_39x }, + /* Contributed by Jan Frey - IBM A30/A31 */ + { "PC8739x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xea, 0xff, + nsc_ircc_probe_39x, nsc_ircc_init_39x }, + { NULL } +}; + +/* Max 4 instances for now */ +static struct nsc_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL }; + +static char *dongle_types[] = { + "Differential serial interface", + "Differential serial interface", + "Reserved", + "Reserved", + "Sharp RY5HD01", + "Reserved", + "Single-ended serial interface", + "Consumer-IR only", + "HP HSDL-2300, HP HSDL-3600/HSDL-3610", + "IBM31T1100 or Temic TFDS6000/TFDS6500", + "Reserved", + "Reserved", + "HP HSDL-1100/HSDL-2100", + "HP HSDL-1100/HSDL-2100", + "Supports SIR Mode only", + "No dongle connected", +}; + +/* Some prototypes */ +static int nsc_ircc_open(int i, chipio_t *info); +static int nsc_ircc_close(struct nsc_ircc_cb *self); +static int nsc_ircc_setup(chipio_t *info); +static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self); +static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self); +static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase); +static int nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev); +static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev); +static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size); +static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase); +static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 baud); +static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self); +static int nsc_ircc_read_dongle_id (int iobase); +static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id); + +static int nsc_ircc_net_open(struct net_device *dev); +static int nsc_ircc_net_close(struct net_device *dev); +static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev); +static int nsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data); + +/* + * Function nsc_ircc_init () + * + * Initialize chip. Just try to find out how many chips we are dealing with + * and where they are + */ +static int __init nsc_ircc_init(void) +{ + chipio_t info; + nsc_chip_t *chip; + int ret = -ENODEV; + int cfg_base; + int cfg, id; + int reg; + int i = 0; + + /* Probe for all the NSC chipsets we know about */ + for (chip=chips; chip->name ; chip++) { + IRDA_DEBUG(2, "%s(), Probing for %s ...\n", __FUNCTION__, + chip->name); + + /* Try all config registers for this chip */ + for (cfg=0; cfg<3; cfg++) { + cfg_base = chip->cfg[cfg]; + if (!cfg_base) + continue; + + memset(&info, 0, sizeof(chipio_t)); + info.cfg_base = cfg_base; + info.fir_base = io[i]; + info.dma = dma[i]; + info.irq = irq[i]; + + /* Read index register */ + reg = inb(cfg_base); + if (reg == 0xff) { + IRDA_DEBUG(2, "%s() no chip at 0x%03x\n", __FUNCTION__, cfg_base); + continue; + } + + /* Read chip identification register */ + outb(chip->cid_index, cfg_base); + id = inb(cfg_base+1); + if ((id & chip->cid_mask) == chip->cid_value) { + IRDA_DEBUG(2, "%s() Found %s chip, revision=%d\n", + __FUNCTION__, chip->name, id & ~chip->cid_mask); + /* + * If the user supplies the base address, then + * we init the chip, if not we probe the values + * set by the BIOS + */ + if (io[i] < 0x2000) { + chip->init(chip, &info); + } else + chip->probe(chip, &info); + + if (nsc_ircc_open(i, &info) == 0) + ret = 0; + i++; + } else { + IRDA_DEBUG(2, "%s(), Wrong chip id=0x%02x\n", __FUNCTION__, id); + } + } + + } + + return ret; +} + +/* + * Function nsc_ircc_cleanup () + * + * Close all configured chips + * + */ +static void __exit nsc_ircc_cleanup(void) +{ + int i; + + pm_unregister_all(nsc_ircc_pmproc); + + for (i=0; i < 4; i++) { + if (dev_self[i]) + nsc_ircc_close(dev_self[i]); + } +} + +/* + * Function nsc_ircc_open (iobase, irq) + * + * Open driver instance + * + */ +static int __init nsc_ircc_open(int i, chipio_t *info) +{ + struct net_device *dev; + struct nsc_ircc_cb *self; + struct pm_dev *pmdev; + void *ret; + int err; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + IRDA_MESSAGE("%s, Found chip at base=0x%03x\n", driver_name, + info->cfg_base); + + if ((nsc_ircc_setup(info)) == -1) + return -1; + + IRDA_MESSAGE("%s, driver loaded (Dag Brattli)\n", driver_name); + + dev = alloc_irdadev(sizeof(struct nsc_ircc_cb)); + if (dev == NULL) { + IRDA_ERROR("%s(), can't allocate memory for " + "control block!\n", __FUNCTION__); + return -ENOMEM; + } + + self = dev->priv; + self->netdev = dev; + spin_lock_init(&self->lock); + + /* Need to store self somewhere */ + dev_self[i] = self; + self->index = i; + + /* Initialize IO */ + self->io.cfg_base = info->cfg_base; + self->io.fir_base = info->fir_base; + self->io.irq = info->irq; + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.dma = info->dma; + self->io.fifo_size = 32; + + /* Reserve the ioports that we need */ + ret = request_region(self->io.fir_base, self->io.fir_ext, driver_name); + if (!ret) { + IRDA_WARNING("%s(), can't get iobase of 0x%03x\n", + __FUNCTION__, self->io.fir_base); + err = -ENODEV; + goto out1; + } + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + /* The only value we must override it the baudrate */ + 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 = qos_mtt_bits; + irda_qos_bits_to_value(&self->qos); + + /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ + self->rx_buff.truesize = 14384; + self->tx_buff.truesize = 14384; + + /* Allocate memory if needed */ + self->rx_buff.head = + dma_alloc_coherent(NULL, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); + if (self->rx_buff.head == NULL) { + err = -ENOMEM; + goto out2; + + } + memset(self->rx_buff.head, 0, self->rx_buff.truesize); + + self->tx_buff.head = + dma_alloc_coherent(NULL, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); + if (self->tx_buff.head == NULL) { + err = -ENOMEM; + goto out3; + } + 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; + + /* Reset Tx queue info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + + /* Override the network functions we need to use */ + SET_MODULE_OWNER(dev); + dev->hard_start_xmit = nsc_ircc_hard_xmit_sir; + dev->open = nsc_ircc_net_open; + dev->stop = nsc_ircc_net_close; + dev->do_ioctl = nsc_ircc_net_ioctl; + dev->get_stats = nsc_ircc_net_get_stats; + + err = register_netdev(dev); + if (err) { + IRDA_ERROR("%s(), register_netdev() failed!\n", __FUNCTION__); + goto out4; + } + IRDA_MESSAGE("IrDA: Registered device %s\n", dev->name); + + /* Check if user has supplied a valid dongle id or not */ + if ((dongle_id <= 0) || + (dongle_id >= (sizeof(dongle_types) / sizeof(dongle_types[0]))) ) { + dongle_id = nsc_ircc_read_dongle_id(self->io.fir_base); + + IRDA_MESSAGE("%s, Found dongle: %s\n", driver_name, + dongle_types[dongle_id]); + } else { + IRDA_MESSAGE("%s, Using dongle: %s\n", driver_name, + dongle_types[dongle_id]); + } + + self->io.dongle_id = dongle_id; + nsc_ircc_init_dongle_interface(self->io.fir_base, dongle_id); + + pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, nsc_ircc_pmproc); + if (pmdev) + pmdev->data = self; + + return 0; + out4: + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + out3: + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + out2: + release_region(self->io.fir_base, self->io.fir_ext); + out1: + free_netdev(dev); + dev_self[i] = NULL; + return err; +} + +/* + * Function nsc_ircc_close (self) + * + * Close driver instance + * + */ +static int __exit nsc_ircc_close(struct nsc_ircc_cb *self) +{ + int iobase; + + IRDA_DEBUG(4, "%s()\n", __FUNCTION__); + + IRDA_ASSERT(self != NULL, return -1;); + + iobase = self->io.fir_base; + + /* Remove netdevice */ + unregister_netdev(self->netdev); + + /* Release the PORT that this driver is using */ + IRDA_DEBUG(4, "%s(), Releasing Region %03x\n", + __FUNCTION__, self->io.fir_base); + release_region(self->io.fir_base, self->io.fir_ext); + + if (self->tx_buff.head) + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + + if (self->rx_buff.head) + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + + dev_self[self->index] = NULL; + free_netdev(self->netdev); + + return 0; +} + +/* + * Function nsc_ircc_init_108 (iobase, cfg_base, irq, dma) + * + * Initialize the NSC '108 chip + * + */ +static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + __u8 temp=0; + + outb(2, cfg_base); /* Mode Control Register (MCTL) */ + outb(0x00, cfg_base+1); /* Disable device */ + + /* Base Address and Interrupt Control Register (BAIC) */ + outb(CFG_108_BAIC, cfg_base); + switch (info->fir_base) { + case 0x3e8: outb(0x14, cfg_base+1); break; + case 0x2e8: outb(0x15, cfg_base+1); break; + case 0x3f8: outb(0x16, cfg_base+1); break; + case 0x2f8: outb(0x17, cfg_base+1); break; + default: IRDA_ERROR("%s(), invalid base_address", __FUNCTION__); + } + + /* Control Signal Routing Register (CSRT) */ + switch (info->irq) { + case 3: temp = 0x01; break; + case 4: temp = 0x02; break; + case 5: temp = 0x03; break; + case 7: temp = 0x04; break; + case 9: temp = 0x05; break; + case 11: temp = 0x06; break; + case 15: temp = 0x07; break; + default: IRDA_ERROR("%s(), invalid irq", __FUNCTION__); + } + outb(CFG_108_CSRT, cfg_base); + + switch (info->dma) { + case 0: outb(0x08+temp, cfg_base+1); break; + case 1: outb(0x10+temp, cfg_base+1); break; + case 3: outb(0x18+temp, cfg_base+1); break; + default: IRDA_ERROR("%s(), invalid dma", __FUNCTION__); + } + + outb(CFG_108_MCTL, cfg_base); /* Mode Control Register (MCTL) */ + outb(0x03, cfg_base+1); /* Enable device */ + + return 0; +} + +/* + * Function nsc_ircc_probe_108 (chip, info) + * + * + * + */ +static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + int reg; + + /* Read address and interrupt control register (BAIC) */ + outb(CFG_108_BAIC, cfg_base); + reg = inb(cfg_base+1); + + switch (reg & 0x03) { + case 0: + info->fir_base = 0x3e8; + break; + case 1: + info->fir_base = 0x2e8; + break; + case 2: + info->fir_base = 0x3f8; + break; + case 3: + info->fir_base = 0x2f8; + break; + } + info->sir_base = info->fir_base; + IRDA_DEBUG(2, "%s(), probing fir_base=0x%03x\n", __FUNCTION__, + info->fir_base); + + /* Read control signals routing register (CSRT) */ + outb(CFG_108_CSRT, cfg_base); + reg = inb(cfg_base+1); + + switch (reg & 0x07) { + case 0: + info->irq = -1; + break; + case 1: + info->irq = 3; + break; + case 2: + info->irq = 4; + break; + case 3: + info->irq = 5; + break; + case 4: + info->irq = 7; + break; + case 5: + info->irq = 9; + break; + case 6: + info->irq = 11; + break; + case 7: + info->irq = 15; + break; + } + IRDA_DEBUG(2, "%s(), probing irq=%d\n", __FUNCTION__, info->irq); + + /* Currently we only read Rx DMA but it will also be used for Tx */ + switch ((reg >> 3) & 0x03) { + case 0: + info->dma = -1; + break; + case 1: + info->dma = 0; + break; + case 2: + info->dma = 1; + break; + case 3: + info->dma = 3; + break; + } + IRDA_DEBUG(2, "%s(), probing dma=%d\n", __FUNCTION__, info->dma); + + /* Read mode control register (MCTL) */ + outb(CFG_108_MCTL, cfg_base); + reg = inb(cfg_base+1); + + info->enabled = reg & 0x01; + info->suspended = !((reg >> 1) & 0x01); + + return 0; +} + +/* + * Function nsc_ircc_init_338 (chip, info) + * + * Initialize the NSC '338 chip. Remember that the 87338 needs two + * consecutive writes to the data registers while CPU interrupts are + * disabled. The 97338 does not require this, but shouldn't be any + * harm if we do it anyway. + */ +static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info) +{ + /* No init yet */ + + return 0; +} + +/* + * Function nsc_ircc_probe_338 (chip, info) + * + * + * + */ +static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + int reg, com = 0; + int pnp; + + /* Read funtion enable register (FER) */ + outb(CFG_338_FER, cfg_base); + reg = inb(cfg_base+1); + + info->enabled = (reg >> 2) & 0x01; + + /* Check if we are in Legacy or PnP mode */ + outb(CFG_338_PNP0, cfg_base); + reg = inb(cfg_base+1); + + pnp = (reg >> 3) & 0x01; + if (pnp) { + IRDA_DEBUG(2, "(), Chip is in PnP mode\n"); + outb(0x46, cfg_base); + reg = (inb(cfg_base+1) & 0xfe) << 2; + + outb(0x47, cfg_base); + reg |= ((inb(cfg_base+1) & 0xfc) << 8); + + info->fir_base = reg; + } else { + /* Read function address register (FAR) */ + outb(CFG_338_FAR, cfg_base); + reg = inb(cfg_base+1); + + switch ((reg >> 4) & 0x03) { + case 0: + info->fir_base = 0x3f8; + break; + case 1: + info->fir_base = 0x2f8; + break; + case 2: + com = 3; + break; + case 3: + com = 4; + break; + } + + if (com) { + switch ((reg >> 6) & 0x03) { + case 0: + if (com == 3) + info->fir_base = 0x3e8; + else + info->fir_base = 0x2e8; + break; + case 1: + if (com == 3) + info->fir_base = 0x338; + else + info->fir_base = 0x238; + break; + case 2: + if (com == 3) + info->fir_base = 0x2e8; + else + info->fir_base = 0x2e0; + break; + case 3: + if (com == 3) + info->fir_base = 0x220; + else + info->fir_base = 0x228; + break; + } + } + } + info->sir_base = info->fir_base; + + /* Read PnP register 1 (PNP1) */ + outb(CFG_338_PNP1, cfg_base); + reg = inb(cfg_base+1); + + info->irq = reg >> 4; + + /* Read PnP register 3 (PNP3) */ + outb(CFG_338_PNP3, cfg_base); + reg = inb(cfg_base+1); + + info->dma = (reg & 0x07) - 1; + + /* Read power and test register (PTR) */ + outb(CFG_338_PTR, cfg_base); + reg = inb(cfg_base+1); + + info->suspended = reg & 0x01; + + return 0; +} + + +/* + * Function nsc_ircc_init_39x (chip, info) + * + * Now that we know it's a '39x (see probe below), we need to + * configure it so we can use it. + * + * The NSC '338 chip is a Super I/O chip with a "bank" architecture, + * the configuration of the different functionality (serial, parallel, + * floppy...) are each in a different bank (Logical Device Number). + * The base address, irq and dma configuration registers are common + * to all functionalities (index 0x30 to 0x7F). + * There is only one configuration register specific to the + * serial port, CFG_39X_SPC. + * JeanII + * + * Note : this code was written by Jan Frey <janfrey@web.de> + */ +static int nsc_ircc_init_39x(nsc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + int enabled; + + /* User is shure about his config... accept it. */ + IRDA_DEBUG(2, "%s(): nsc_ircc_init_39x (user settings): " + "io=0x%04x, irq=%d, dma=%d\n", + __FUNCTION__, info->fir_base, info->irq, info->dma); + + /* Access bank for SP2 */ + outb(CFG_39X_LDN, cfg_base); + outb(0x02, cfg_base+1); + + /* Configure SP2 */ + + /* We want to enable the device if not enabled */ + outb(CFG_39X_ACT, cfg_base); + enabled = inb(cfg_base+1) & 0x01; + + if (!enabled) { + /* Enable the device */ + outb(CFG_39X_SIOCF1, cfg_base); + outb(0x01, cfg_base+1); + /* May want to update info->enabled. Jean II */ + } + + /* Enable UART bank switching (bit 7) ; Sets the chip to normal + * power mode (wake up from sleep mode) (bit 1) */ + outb(CFG_39X_SPC, cfg_base); + outb(0x82, cfg_base+1); + + return 0; +} + +/* + * Function nsc_ircc_probe_39x (chip, info) + * + * Test if we really have a '39x chip at the given address + * + * Note : this code was written by Jan Frey <janfrey@web.de> + */ +static int nsc_ircc_probe_39x(nsc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + int reg1, reg2, irq, irqt, dma1, dma2; + int enabled, susp; + + IRDA_DEBUG(2, "%s(), nsc_ircc_probe_39x, base=%d\n", + __FUNCTION__, cfg_base); + + /* This function should be executed with irq off to avoid + * another driver messing with the Super I/O bank - Jean II */ + + /* Access bank for SP2 */ + outb(CFG_39X_LDN, cfg_base); + outb(0x02, cfg_base+1); + + /* Read infos about SP2 ; store in info struct */ + outb(CFG_39X_BASEH, cfg_base); + reg1 = inb(cfg_base+1); + outb(CFG_39X_BASEL, cfg_base); + reg2 = inb(cfg_base+1); + info->fir_base = (reg1 << 8) | reg2; + + outb(CFG_39X_IRQNUM, cfg_base); + irq = inb(cfg_base+1); + outb(CFG_39X_IRQSEL, cfg_base); + irqt = inb(cfg_base+1); + info->irq = irq; + + outb(CFG_39X_DMA0, cfg_base); + dma1 = inb(cfg_base+1); + outb(CFG_39X_DMA1, cfg_base); + dma2 = inb(cfg_base+1); + info->dma = dma1 -1; + + outb(CFG_39X_ACT, cfg_base); + info->enabled = enabled = inb(cfg_base+1) & 0x01; + + outb(CFG_39X_SPC, cfg_base); + susp = 1 - ((inb(cfg_base+1) & 0x02) >> 1); + + IRDA_DEBUG(2, "%s(): io=0x%02x%02x, irq=%d (type %d), rxdma=%d, txdma=%d, enabled=%d (suspended=%d)\n", __FUNCTION__, reg1,reg2,irq,irqt,dma1,dma2,enabled,susp); + + /* Configure SP2 */ + + /* We want to enable the device if not enabled */ + outb(CFG_39X_ACT, cfg_base); + enabled = inb(cfg_base+1) & 0x01; + + if (!enabled) { + /* Enable the device */ + outb(CFG_39X_SIOCF1, cfg_base); + outb(0x01, cfg_base+1); + /* May want to update info->enabled. Jean II */ + } + + /* Enable UART bank switching (bit 7) ; Sets the chip to normal + * power mode (wake up from sleep mode) (bit 1) */ + outb(CFG_39X_SPC, cfg_base); + outb(0x82, cfg_base+1); + + return 0; +} + +/* + * Function nsc_ircc_setup (info) + * + * Returns non-negative on success. + * + */ +static int nsc_ircc_setup(chipio_t *info) +{ + int version; + int iobase = info->fir_base; + + /* Read the Module ID */ + switch_bank(iobase, BANK3); + version = inb(iobase+MID); + + IRDA_DEBUG(2, "%s() Driver %s Found chip version %02x\n", + __FUNCTION__, driver_name, version); + + /* Should be 0x2? */ + if (0x20 != (version & 0xf0)) { + IRDA_ERROR("%s, Wrong chip version %02x\n", + driver_name, version); + return -1; + } + + /* Switch to advanced mode */ + switch_bank(iobase, BANK2); + outb(ECR1_EXT_SL, iobase+ECR1); + switch_bank(iobase, BANK0); + + /* Set FIFO threshold to TX17, RX16, reset and enable FIFO's */ + switch_bank(iobase, BANK0); + outb(FCR_RXTH|FCR_TXTH|FCR_TXSR|FCR_RXSR|FCR_FIFO_EN, iobase+FCR); + + outb(0x03, iobase+LCR); /* 8 bit word length */ + outb(MCR_SIR, iobase+MCR); /* Start at SIR-mode, also clears LSR*/ + + /* Set FIFO size to 32 */ + switch_bank(iobase, BANK2); + outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2); + + /* IRCR2: FEND_MD is not set */ + switch_bank(iobase, BANK5); + outb(0x02, iobase+4); + + /* Make sure that some defaults are OK */ + switch_bank(iobase, BANK6); + outb(0x20, iobase+0); /* Set 32 bits FIR CRC */ + outb(0x0a, iobase+1); /* Set MIR pulse width */ + outb(0x0d, iobase+2); /* Set SIR pulse width to 1.6us */ + outb(0x2a, iobase+4); /* Set beginning frag, and preamble length */ + + /* Enable receive interrupts */ + switch_bank(iobase, BANK0); + outb(IER_RXHDL_IE, iobase+IER); + + return 0; +} + +/* + * Function nsc_ircc_read_dongle_id (void) + * + * Try to read dongle indentification. This procedure needs to be executed + * once after power-on/reset. It also needs to be used whenever you suspect + * that the user may have plugged/unplugged the IrDA Dongle. + */ +static int nsc_ircc_read_dongle_id (int iobase) +{ + int dongle_id; + __u8 bank; + + bank = inb(iobase+BSR); + + /* Select Bank 7 */ + switch_bank(iobase, BANK7); + + /* IRCFG4: IRSL0_DS and IRSL21_DS are cleared */ + outb(0x00, iobase+7); + + /* ID0, 1, and 2 are pulled up/down very slowly */ + udelay(50); + + /* IRCFG1: read the ID bits */ + dongle_id = inb(iobase+4) & 0x0f; + +#ifdef BROKEN_DONGLE_ID + if (dongle_id == 0x0a) + dongle_id = 0x09; +#endif + /* Go back to bank 0 before returning */ + switch_bank(iobase, BANK0); + + outb(bank, iobase+BSR); + + return dongle_id; +} + +/* + * Function nsc_ircc_init_dongle_interface (iobase, dongle_id) + * + * This function initializes the dongle for the transceiver that is + * used. This procedure needs to be executed once after + * power-on/reset. It also needs to be used whenever you suspect that + * the dongle is changed. + */ +static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id) +{ + int bank; + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* Select Bank 7 */ + switch_bank(iobase, BANK7); + + /* IRCFG4: set according to dongle_id */ + switch (dongle_id) { + case 0x00: /* same as */ + case 0x01: /* Differential serial interface */ + IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n", + __FUNCTION__, dongle_types[dongle_id]); + break; + case 0x02: /* same as */ + case 0x03: /* Reserved */ + IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n", + __FUNCTION__, dongle_types[dongle_id]); + break; + case 0x04: /* Sharp RY5HD01 */ + break; + case 0x05: /* Reserved, but this is what the Thinkpad reports */ + IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n", + __FUNCTION__, dongle_types[dongle_id]); + break; + case 0x06: /* Single-ended serial interface */ + IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n", + __FUNCTION__, dongle_types[dongle_id]); + break; + case 0x07: /* Consumer-IR only */ + IRDA_DEBUG(0, "%s(), %s is not for IrDA mode\n", + __FUNCTION__, dongle_types[dongle_id]); + break; + case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ + IRDA_DEBUG(0, "%s(), %s\n", + __FUNCTION__, dongle_types[dongle_id]); + break; + case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ + outb(0x28, iobase+7); /* Set irsl[0-2] as output */ + break; + case 0x0A: /* same as */ + case 0x0B: /* Reserved */ + IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n", + __FUNCTION__, dongle_types[dongle_id]); + break; + case 0x0C: /* same as */ + case 0x0D: /* HP HSDL-1100/HSDL-2100 */ + /* + * Set irsl0 as input, irsl[1-2] as output, and separate + * inputs are used for SIR and MIR/FIR + */ + outb(0x48, iobase+7); + break; + case 0x0E: /* Supports SIR Mode only */ + outb(0x28, iobase+7); /* Set irsl[0-2] as output */ + break; + case 0x0F: /* No dongle connected */ + IRDA_DEBUG(0, "%s(), %s\n", + __FUNCTION__, dongle_types[dongle_id]); + + switch_bank(iobase, BANK0); + outb(0x62, iobase+MCR); + break; + default: + IRDA_DEBUG(0, "%s(), invalid dongle_id %#x", + __FUNCTION__, dongle_id); + } + + /* IRCFG1: IRSL1 and 2 are set to IrDA mode */ + outb(0x00, iobase+4); + + /* Restore bank register */ + outb(bank, iobase+BSR); + +} /* set_up_dongle_interface */ + +/* + * Function nsc_ircc_change_dongle_speed (iobase, speed, dongle_id) + * + * Change speed of the attach dongle + * + */ +static void nsc_ircc_change_dongle_speed(int iobase, int speed, int dongle_id) +{ + __u8 bank; + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* Select Bank 7 */ + switch_bank(iobase, BANK7); + + /* IRCFG1: set according to dongle_id */ + switch (dongle_id) { + case 0x00: /* same as */ + case 0x01: /* Differential serial interface */ + IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n", + __FUNCTION__, dongle_types[dongle_id]); + break; + case 0x02: /* same as */ + case 0x03: /* Reserved */ + IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n", + __FUNCTION__, dongle_types[dongle_id]); + break; + case 0x04: /* Sharp RY5HD01 */ + break; + case 0x05: /* Reserved */ + IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n", + __FUNCTION__, dongle_types[dongle_id]); + break; + case 0x06: /* Single-ended serial interface */ + IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n", + __FUNCTION__, dongle_types[dongle_id]); + break; + case 0x07: /* Consumer-IR only */ + IRDA_DEBUG(0, "%s(), %s is not for IrDA mode\n", + __FUNCTION__, dongle_types[dongle_id]); + break; + case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ + IRDA_DEBUG(0, "%s(), %s\n", + __FUNCTION__, dongle_types[dongle_id]); + outb(0x00, iobase+4); + if (speed > 115200) + outb(0x01, iobase+4); + break; + case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ + outb(0x01, iobase+4); + + if (speed == 4000000) { + /* There was a cli() there, but we now are already + * under spin_lock_irqsave() - JeanII */ + outb(0x81, iobase+4); + outb(0x80, iobase+4); + } else + outb(0x00, iobase+4); + break; + case 0x0A: /* same as */ + case 0x0B: /* Reserved */ + IRDA_DEBUG(0, "%s(), %s not defined by irda yet\n", + __FUNCTION__, dongle_types[dongle_id]); + break; + case 0x0C: /* same as */ + case 0x0D: /* HP HSDL-1100/HSDL-2100 */ + break; + case 0x0E: /* Supports SIR Mode only */ + break; + case 0x0F: /* No dongle connected */ + IRDA_DEBUG(0, "%s(), %s is not for IrDA mode\n", + __FUNCTION__, dongle_types[dongle_id]); + + switch_bank(iobase, BANK0); + outb(0x62, iobase+MCR); + break; + default: + IRDA_DEBUG(0, "%s(), invalid data_rate\n", __FUNCTION__); + } + /* Restore bank register */ + outb(bank, iobase+BSR); +} + +/* + * Function nsc_ircc_change_speed (self, baud) + * + * Change the speed of the device + * + * This function *must* be called with irq off and spin-lock. + */ +static __u8 nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 speed) +{ + struct net_device *dev = self->netdev; + __u8 mcr = MCR_SIR; + int iobase; + __u8 bank; + __u8 ier; /* Interrupt enable register */ + + IRDA_DEBUG(2, "%s(), speed=%d\n", __FUNCTION__, speed); + + IRDA_ASSERT(self != NULL, return 0;); + + iobase = self->io.fir_base; + + /* Update accounting for new speed */ + self->io.speed = speed; + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* Disable interrupts */ + switch_bank(iobase, BANK0); + outb(0, iobase+IER); + + /* Select Bank 2 */ + switch_bank(iobase, BANK2); + + outb(0x00, iobase+BGDH); + switch (speed) { + case 9600: outb(0x0c, iobase+BGDL); break; + case 19200: outb(0x06, iobase+BGDL); break; + case 38400: outb(0x03, iobase+BGDL); break; + case 57600: outb(0x02, iobase+BGDL); break; + case 115200: outb(0x01, iobase+BGDL); break; + case 576000: + switch_bank(iobase, BANK5); + + /* IRCR2: MDRS is set */ + outb(inb(iobase+4) | 0x04, iobase+4); + + mcr = MCR_MIR; + IRDA_DEBUG(0, "%s(), handling baud of 576000\n", __FUNCTION__); + break; + case 1152000: + mcr = MCR_MIR; + IRDA_DEBUG(0, "%s(), handling baud of 1152000\n", __FUNCTION__); + break; + case 4000000: + mcr = MCR_FIR; + IRDA_DEBUG(0, "%s(), handling baud of 4000000\n", __FUNCTION__); + break; + default: + mcr = MCR_FIR; + IRDA_DEBUG(0, "%s(), unknown baud rate of %d\n", + __FUNCTION__, speed); + break; + } + + /* Set appropriate speed mode */ + switch_bank(iobase, BANK0); + outb(mcr | MCR_TX_DFR, iobase+MCR); + + /* Give some hits to the transceiver */ + nsc_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id); + + /* Set FIFO threshold to TX17, RX16 */ + switch_bank(iobase, BANK0); + outb(0x00, iobase+FCR); + outb(FCR_FIFO_EN, iobase+FCR); + outb(FCR_RXTH| /* Set Rx FIFO threshold */ + FCR_TXTH| /* Set Tx FIFO threshold */ + FCR_TXSR| /* Reset Tx FIFO */ + FCR_RXSR| /* Reset Rx FIFO */ + FCR_FIFO_EN, /* Enable FIFOs */ + iobase+FCR); + + /* Set FIFO size to 32 */ + switch_bank(iobase, BANK2); + outb(EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2); + + /* Enable some interrupts so we can receive frames */ + switch_bank(iobase, BANK0); + if (speed > 115200) { + /* Install FIR xmit handler */ + dev->hard_start_xmit = nsc_ircc_hard_xmit_fir; + ier = IER_SFIF_IE; + nsc_ircc_dma_receive(self); + } else { + /* Install SIR xmit handler */ + dev->hard_start_xmit = nsc_ircc_hard_xmit_sir; + ier = IER_RXHDL_IE; + } + /* Set our current interrupt mask */ + outb(ier, iobase+IER); + + /* Restore BSR */ + outb(bank, iobase+BSR); + + /* Make sure interrupt handlers keep the proper interrupt mask */ + return(ier); +} + +/* + * Function nsc_ircc_hard_xmit (skb, dev) + * + * Transmit the frame! + * + */ +static int nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev) +{ + struct nsc_ircc_cb *self; + unsigned long flags; + int iobase; + __s32 speed; + __u8 bank; + + self = (struct nsc_ircc_cb *) dev->priv; + + IRDA_ASSERT(self != NULL, return 0;); + + iobase = self->io.fir_base; + + netif_stop_queue(dev); + + /* Make sure tests *& 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) { + /* If we just sent a frame, we get called before + * the last bytes get out (because of the SIR FIFO). + * If this is the case, let interrupt handler change + * the speed itself... Jean II */ + if (self->io.direction == IO_RECV) { + nsc_ircc_change_speed(self, speed); + /* TODO : For SIR->SIR, the next packet + * may get corrupted - Jean II */ + netif_wake_queue(dev); + } else { + self->new_speed = speed; + /* Queue will be restarted after speed change + * 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; + } else + self->new_speed = speed; + } + + /* Save current bank */ + bank = inb(iobase+BSR); + + self->tx_buff.data = self->tx_buff.head; + + self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, + self->tx_buff.truesize); + + self->stats.tx_bytes += self->tx_buff.len; + + /* Add interrupt on tx low level (will fire immediately) */ + switch_bank(iobase, BANK0); + outb(IER_TXLDL_IE, iobase+IER); + + /* Restore bank register */ + outb(bank, iobase+BSR); + + dev->trans_start = jiffies; + spin_unlock_irqrestore(&self->lock, flags); + + dev_kfree_skb(skb); + + return 0; +} + +static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) +{ + struct nsc_ircc_cb *self; + unsigned long flags; + int iobase; + __s32 speed; + __u8 bank; + int mtt, diff; + + self = (struct nsc_ircc_cb *) dev->priv; + iobase = self->io.fir_base; + + netif_stop_queue(dev); + + /* Make sure tests *& 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) { + /* If we are currently transmitting, defer to + * interrupt handler. - Jean II */ + if(self->tx_fifo.len == 0) { + nsc_ircc_change_speed(self, speed); + netif_wake_queue(dev); + } else { + self->new_speed = speed; + /* Keep queue stopped : + * the speed change operation may change the + * xmit handler, and we want to make sure + * the next packet get through the proper + * Tx path, so block the Tx queue until + * the speed change has been done. + * Jean II */ + } + dev->trans_start = jiffies; + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + return 0; + } else { + /* Change speed after current frame */ + self->new_speed = speed; + } + } + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* Register and copy this frame to DMA memory */ + self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail; + self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; + self->tx_fifo.tail += skb->len; + + self->stats.tx_bytes += skb->len; + + memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, + skb->len); + + self->tx_fifo.len++; + self->tx_fifo.free++; + + /* Start transmit only if there is currently no transmit going on */ + if (self->tx_fifo.len == 1) { + /* Check if we must wait the min turn time or not */ + mtt = irda_get_mtt(skb); + if (mtt) { + /* Check how much time we have used already */ + do_gettimeofday(&self->now); + diff = self->now.tv_usec - self->stamp.tv_usec; + if (diff < 0) + diff += 1000000; + + /* Check if the mtt is larger than the time we have + * already used by all the protocol processing + */ + if (mtt > diff) { + mtt -= diff; + + /* + * Use timer if delay larger than 125 us, and + * use udelay for smaller values which should + * be acceptable + */ + if (mtt > 125) { + /* Adjust for timer resolution */ + mtt = mtt / 125; + + /* Setup timer */ + switch_bank(iobase, BANK4); + outb(mtt & 0xff, iobase+TMRL); + outb((mtt >> 8) & 0x0f, iobase+TMRH); + + /* Start timer */ + outb(IRCR1_TMR_EN, iobase+IRCR1); + self->io.direction = IO_XMIT; + + /* Enable timer interrupt */ + switch_bank(iobase, BANK0); + outb(IER_TMR_IE, iobase+IER); + + /* Timer will take care of the rest */ + goto out; + } else + udelay(mtt); + } + } + /* Enable DMA interrupt */ + switch_bank(iobase, BANK0); + outb(IER_DMA_IE, iobase+IER); + + /* Transmit frame */ + nsc_ircc_dma_xmit(self, iobase); + } + out: + /* Not busy transmitting anymore if window is not full, + * and if we don't need to change speed */ + if ((self->tx_fifo.free < MAX_TX_WINDOW) && (self->new_speed == 0)) + netif_wake_queue(self->netdev); + + /* Restore bank register */ + outb(bank, iobase+BSR); + + dev->trans_start = jiffies; + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function nsc_ircc_dma_xmit (self, iobase) + * + * Transmit data using DMA + * + */ +static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase) +{ + int bsr; + + /* Save current bank */ + bsr = inb(iobase+BSR); + + /* Disable DMA */ + switch_bank(iobase, BANK0); + outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR); + + self->io.direction = IO_XMIT; + + /* Choose transmit DMA channel */ + switch_bank(iobase, BANK2); + outb(ECR1_DMASWP|ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1); + + irda_setup_dma(self->io.dma, + ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start - + self->tx_buff.head) + self->tx_buff_dma, + self->tx_fifo.queue[self->tx_fifo.ptr].len, + DMA_TX_MODE); + + /* Enable DMA and SIR interaction pulse */ + switch_bank(iobase, BANK0); + outb(inb(iobase+MCR)|MCR_TX_DFR|MCR_DMA_EN|MCR_IR_PLS, iobase+MCR); + + /* Restore bank register */ + outb(bsr, iobase+BSR); +} + +/* + * Function nsc_ircc_pio_xmit (self, iobase) + * + * Transmit data using PIO. Returns the number of bytes that actually + * got transferred + * + */ +static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size) +{ + int actual = 0; + __u8 bank; + + IRDA_DEBUG(4, "%s()\n", __FUNCTION__); + + /* Save current bank */ + bank = inb(iobase+BSR); + + switch_bank(iobase, BANK0); + if (!(inb_p(iobase+LSR) & LSR_TXEMP)) { + IRDA_DEBUG(4, "%s(), warning, FIFO not empty yet!\n", + __FUNCTION__); + + /* FIFO may still be filled to the Tx interrupt threshold */ + fifo_size -= 17; + } + + /* Fill FIFO with current frame */ + while ((fifo_size-- > 0) && (actual < len)) { + /* Transmit next byte */ + outb(buf[actual++], iobase+TXD); + } + + IRDA_DEBUG(4, "%s(), fifo_size %d ; %d sent of %d\n", + __FUNCTION__, fifo_size, actual, len); + + /* Restore bank */ + outb(bank, iobase+BSR); + + return actual; +} + +/* + * Function nsc_ircc_dma_xmit_complete (self) + * + * The transfer of a frame in finished. This function will only be called + * by the interrupt handler + * + */ +static int nsc_ircc_dma_xmit_complete(struct nsc_ircc_cb *self) +{ + int iobase; + __u8 bank; + int ret = TRUE; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + iobase = self->io.fir_base; + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* Disable DMA */ + switch_bank(iobase, BANK0); + outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR); + + /* Check for underrrun! */ + if (inb(iobase+ASCR) & ASCR_TXUR) { + self->stats.tx_errors++; + self->stats.tx_fifo_errors++; + + /* Clear bit, by writing 1 into it */ + outb(ASCR_TXUR, iobase+ASCR); + } else { + self->stats.tx_packets++; + } + + /* Finished with this frame, so prepare for next */ + self->tx_fifo.ptr++; + self->tx_fifo.len--; + + /* Any frames to be sent back-to-back? */ + if (self->tx_fifo.len) { + nsc_ircc_dma_xmit(self, iobase); + + /* Not finished yet! */ + ret = FALSE; + } else { + /* Reset Tx FIFO info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + } + + /* Make sure we have room for more frames and + * that we don't need to change speed */ + if ((self->tx_fifo.free < MAX_TX_WINDOW) && (self->new_speed == 0)) { + /* Not busy transmitting anymore */ + /* Tell the network layer, that we can accept more frames */ + netif_wake_queue(self->netdev); + } + + /* Restore bank */ + outb(bank, iobase+BSR); + + return ret; +} + +/* + * Function nsc_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 nsc_ircc_dma_receive(struct nsc_ircc_cb *self) +{ + int iobase; + __u8 bsr; + + iobase = self->io.fir_base; + + /* Reset Tx FIFO info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + + /* Save current bank */ + bsr = inb(iobase+BSR); + + /* Disable DMA */ + switch_bank(iobase, BANK0); + outb(inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR); + + /* Choose DMA Rx, DMA Fairness, and Advanced mode */ + switch_bank(iobase, BANK2); + outb(ECR1_DMANF|ECR1_EXT_SL, iobase+ECR1); + + self->io.direction = IO_RECV; + self->rx_buff.data = self->rx_buff.head; + + /* Reset Rx FIFO. This will also flush the ST_FIFO */ + switch_bank(iobase, BANK0); + outb(FCR_RXSR|FCR_FIFO_EN, iobase+FCR); + + self->st_fifo.len = self->st_fifo.pending_bytes = 0; + self->st_fifo.tail = self->st_fifo.head = 0; + + irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, + DMA_RX_MODE); + + /* Enable DMA */ + switch_bank(iobase, BANK0); + outb(inb(iobase+MCR)|MCR_DMA_EN, iobase+MCR); + + /* Restore bank register */ + outb(bsr, iobase+BSR); + + return 0; +} + +/* + * Function nsc_ircc_dma_receive_complete (self) + * + * Finished with receiving frames + * + * + */ +static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase) +{ + struct st_fifo *st_fifo; + struct sk_buff *skb; + __u8 status; + __u8 bank; + int len; + + st_fifo = &self->st_fifo; + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* Read all entries in status FIFO */ + switch_bank(iobase, BANK5); + while ((status = inb(iobase+FRM_ST)) & FRM_ST_VLD) { + /* We must empty the status FIFO no matter what */ + len = inb(iobase+RFLFL) | ((inb(iobase+RFLFH) & 0x1f) << 8); + + if (st_fifo->tail >= MAX_RX_WINDOW) { + IRDA_DEBUG(0, "%s(), window is full!\n", __FUNCTION__); + continue; + } + + st_fifo->entries[st_fifo->tail].status = status; + st_fifo->entries[st_fifo->tail].len = len; + st_fifo->pending_bytes += len; + st_fifo->tail++; + st_fifo->len++; + } + /* Try to process all entries in status FIFO */ + while (st_fifo->len > 0) { + /* Get first entry */ + status = st_fifo->entries[st_fifo->head].status; + len = st_fifo->entries[st_fifo->head].len; + st_fifo->pending_bytes -= len; + st_fifo->head++; + st_fifo->len--; + + /* Check for errors */ + if (status & FRM_ST_ERR_MSK) { + if (status & FRM_ST_LOST_FR) { + /* Add number of lost frames to stats */ + self->stats.rx_errors += len; + } else { + /* Skip frame */ + self->stats.rx_errors++; + + self->rx_buff.data += len; + + if (status & FRM_ST_MAX_LEN) + self->stats.rx_length_errors++; + + if (status & FRM_ST_PHY_ERR) + self->stats.rx_frame_errors++; + + if (status & FRM_ST_BAD_CRC) + self->stats.rx_crc_errors++; + } + /* The errors below can be reported in both cases */ + if (status & FRM_ST_OVR1) + self->stats.rx_fifo_errors++; + + if (status & FRM_ST_OVR2) + self->stats.rx_fifo_errors++; + } else { + /* + * First we must make sure that the frame we + * want to deliver is all in main memory. If we + * cannot tell, then we check if the Rx FIFO is + * empty. If not then we will have to take a nap + * and try again later. + */ + if (st_fifo->pending_bytes < self->io.fifo_size) { + switch_bank(iobase, BANK0); + if (inb(iobase+LSR) & LSR_RXDA) { + /* Put this entry back in fifo */ + st_fifo->head--; + st_fifo->len++; + st_fifo->pending_bytes += len; + st_fifo->entries[st_fifo->head].status = status; + st_fifo->entries[st_fifo->head].len = len; + /* + * DMA not finished yet, so try again + * later, set timer value, resolution + * 125 us + */ + switch_bank(iobase, BANK4); + outb(0x02, iobase+TMRL); /* x 125 us */ + outb(0x00, iobase+TMRH); + + /* Start timer */ + outb(IRCR1_TMR_EN, iobase+IRCR1); + + /* Restore bank register */ + outb(bank, iobase+BSR); + + return FALSE; /* I'll be back! */ + } + } + + /* + * Remember the time we received this frame, so we can + * reduce the min turn time a bit since we will know + * how much time we have used for protocol processing + */ + do_gettimeofday(&self->stamp); + + skb = dev_alloc_skb(len+1); + if (skb == NULL) { + IRDA_WARNING("%s(), memory squeeze, " + "dropping frame.\n", + __FUNCTION__); + self->stats.rx_dropped++; + + /* Restore bank register */ + outb(bank, iobase+BSR); + + return FALSE; + } + + /* Make sure IP header gets aligned */ + skb_reserve(skb, 1); + + /* Copy frame without CRC */ + if (self->io.speed < 4000000) { + skb_put(skb, len-2); + memcpy(skb->data, self->rx_buff.data, len-2); + } else { + skb_put(skb, len-4); + memcpy(skb->data, self->rx_buff.data, len-4); + } + + /* Move to next frame */ + self->rx_buff.data += len; + self->stats.rx_bytes += len; + self->stats.rx_packets++; + + skb->dev = self->netdev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + self->netdev->last_rx = jiffies; + } + } + /* Restore bank register */ + outb(bank, iobase+BSR); + + return TRUE; +} + +/* + * Function nsc_ircc_pio_receive (self) + * + * Receive all data in receiver FIFO + * + */ +static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self) +{ + __u8 byte; + int iobase; + + iobase = self->io.fir_base; + + /* Receive all characters in Rx FIFO */ + do { + byte = inb(iobase+RXD); + async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, + byte); + } while (inb(iobase+LSR) & LSR_RXDA); /* Data available */ +} + +/* + * Function nsc_ircc_sir_interrupt (self, eir) + * + * Handle SIR interrupt + * + */ +static void nsc_ircc_sir_interrupt(struct nsc_ircc_cb *self, int eir) +{ + int actual; + + /* Check if transmit FIFO is low on data */ + if (eir & EIR_TXLDL_EV) { + /* Write data left in transmit buffer */ + actual = nsc_ircc_pio_write(self->io.fir_base, + self->tx_buff.data, + self->tx_buff.len, + self->io.fifo_size); + self->tx_buff.data += actual; + self->tx_buff.len -= actual; + + self->io.direction = IO_XMIT; + + /* Check if finished */ + if (self->tx_buff.len > 0) + self->ier = IER_TXLDL_IE; + else { + + self->stats.tx_packets++; + netif_wake_queue(self->netdev); + self->ier = IER_TXEMP_IE; + } + + } + /* Check if transmission has completed */ + if (eir & EIR_TXEMP_EV) { + /* Turn around and get ready to receive some data */ + self->io.direction = IO_RECV; + self->ier = IER_RXHDL_IE; + /* Check if we need to change the speed? + * Need to be after self->io.direction to avoid race with + * nsc_ircc_hard_xmit_sir() - Jean II */ + if (self->new_speed) { + IRDA_DEBUG(2, "%s(), Changing speed!\n", __FUNCTION__); + self->ier = nsc_ircc_change_speed(self, + self->new_speed); + self->new_speed = 0; + netif_wake_queue(self->netdev); + + /* Check if we are going to FIR */ + if (self->io.speed > 115200) { + /* No need to do anymore SIR stuff */ + return; + } + } + } + + /* Rx FIFO threshold or timeout */ + if (eir & EIR_RXHDL_EV) { + nsc_ircc_pio_receive(self); + + /* Keep receiving */ + self->ier = IER_RXHDL_IE; + } +} + +/* + * Function nsc_ircc_fir_interrupt (self, eir) + * + * Handle MIR/FIR interrupt + * + */ +static void nsc_ircc_fir_interrupt(struct nsc_ircc_cb *self, int iobase, + int eir) +{ + __u8 bank; + + bank = inb(iobase+BSR); + + /* Status FIFO event*/ + if (eir & EIR_SFIF_EV) { + /* Check if DMA has finished */ + if (nsc_ircc_dma_receive_complete(self, iobase)) { + /* Wait for next status FIFO interrupt */ + self->ier = IER_SFIF_IE; + } else { + self->ier = IER_SFIF_IE | IER_TMR_IE; + } + } else if (eir & EIR_TMR_EV) { /* Timer finished */ + /* Disable timer */ + switch_bank(iobase, BANK4); + outb(0, iobase+IRCR1); + + /* Clear timer event */ + switch_bank(iobase, BANK0); + outb(ASCR_CTE, iobase+ASCR); + + /* Check if this is a Tx timer interrupt */ + if (self->io.direction == IO_XMIT) { + nsc_ircc_dma_xmit(self, iobase); + + /* Interrupt on DMA */ + self->ier = IER_DMA_IE; + } else { + /* Check (again) if DMA has finished */ + if (nsc_ircc_dma_receive_complete(self, iobase)) { + self->ier = IER_SFIF_IE; + } else { + self->ier = IER_SFIF_IE | IER_TMR_IE; + } + } + } else if (eir & EIR_DMA_EV) { + /* Finished with all transmissions? */ + if (nsc_ircc_dma_xmit_complete(self)) { + if(self->new_speed != 0) { + /* As we stop the Tx queue, the speed change + * need to be done when the Tx fifo is + * empty. Ask for a Tx done interrupt */ + self->ier = IER_TXEMP_IE; + } else { + /* Check if there are more frames to be + * transmitted */ + if (irda_device_txqueue_empty(self->netdev)) { + /* Prepare for receive */ + nsc_ircc_dma_receive(self); + self->ier = IER_SFIF_IE; + } else + IRDA_WARNING("%s(), potential " + "Tx queue lockup !\n", + __FUNCTION__); + } + } else { + /* Not finished yet, so interrupt on DMA again */ + self->ier = IER_DMA_IE; + } + } else if (eir & EIR_TXEMP_EV) { + /* The Tx FIFO has totally drained out, so now we can change + * the speed... - Jean II */ + self->ier = nsc_ircc_change_speed(self, self->new_speed); + self->new_speed = 0; + netif_wake_queue(self->netdev); + /* Note : nsc_ircc_change_speed() restarted Rx fifo */ + } + + outb(bank, iobase+BSR); +} + +/* + * Function nsc_ircc_interrupt (irq, dev_id, regs) + * + * An interrupt from the chip has arrived. Time to do some work + * + */ +static irqreturn_t nsc_ircc_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct nsc_ircc_cb *self; + __u8 bsr, eir; + int iobase; + + if (!dev) { + IRDA_WARNING("%s: irq %d for unknown device.\n", + driver_name, irq); + return IRQ_NONE; + } + self = (struct nsc_ircc_cb *) dev->priv; + + spin_lock(&self->lock); + + iobase = self->io.fir_base; + + bsr = inb(iobase+BSR); /* Save current bank */ + + switch_bank(iobase, BANK0); + self->ier = inb(iobase+IER); + eir = inb(iobase+EIR) & self->ier; /* Mask out the interesting ones */ + + outb(0, iobase+IER); /* Disable interrupts */ + + if (eir) { + /* Dispatch interrupt handler for the current speed */ + if (self->io.speed > 115200) + nsc_ircc_fir_interrupt(self, iobase, eir); + else + nsc_ircc_sir_interrupt(self, eir); + } + + outb(self->ier, iobase+IER); /* Restore interrupts */ + outb(bsr, iobase+BSR); /* Restore bank register */ + + spin_unlock(&self->lock); + return IRQ_RETVAL(eir); +} + +/* + * Function nsc_ircc_is_receiving (self) + * + * Return TRUE is we are currently receiving a frame + * + */ +static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self) +{ + unsigned long flags; + int status = FALSE; + int iobase; + __u8 bank; + + IRDA_ASSERT(self != NULL, return FALSE;); + + spin_lock_irqsave(&self->lock, flags); + + if (self->io.speed > 115200) { + iobase = self->io.fir_base; + + /* Check if rx FIFO is not empty */ + bank = inb(iobase+BSR); + switch_bank(iobase, BANK2); + if ((inb(iobase+RXFLV) & 0x3f) != 0) { + /* We are receiving something */ + status = TRUE; + } + outb(bank, iobase+BSR); + } else + status = (self->rx_buff.state != OUTSIDE_FRAME); + + spin_unlock_irqrestore(&self->lock, flags); + + return status; +} + +/* + * Function nsc_ircc_net_open (dev) + * + * Start the device + * + */ +static int nsc_ircc_net_open(struct net_device *dev) +{ + struct nsc_ircc_cb *self; + int iobase; + char hwname[32]; + __u8 bank; + + IRDA_DEBUG(4, "%s()\n", __FUNCTION__); + + IRDA_ASSERT(dev != NULL, return -1;); + self = (struct nsc_ircc_cb *) dev->priv; + + IRDA_ASSERT(self != NULL, return 0;); + + iobase = self->io.fir_base; + + if (request_irq(self->io.irq, nsc_ircc_interrupt, 0, dev->name, dev)) { + IRDA_WARNING("%s, unable to allocate irq=%d\n", + driver_name, self->io.irq); + return -EAGAIN; + } + /* + * Always allocate the DMA channel after the IRQ, and clean up on + * failure. + */ + if (request_dma(self->io.dma, dev->name)) { + IRDA_WARNING("%s, unable to allocate dma=%d\n", + driver_name, self->io.dma); + free_irq(self->io.irq, dev); + return -EAGAIN; + } + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* turn on interrupts */ + switch_bank(iobase, BANK0); + outb(IER_LS_IE | IER_RXHDL_IE, iobase+IER); + + /* Restore bank register */ + outb(bank, iobase+BSR); + + /* Ready to play! */ + netif_start_queue(dev); + + /* Give self a hardware name */ + sprintf(hwname, "NSC-FIR @ 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); + + return 0; +} + +/* + * Function nsc_ircc_net_close (dev) + * + * Stop the device + * + */ +static int nsc_ircc_net_close(struct net_device *dev) +{ + struct nsc_ircc_cb *self; + int iobase; + __u8 bank; + + IRDA_DEBUG(4, "%s()\n", __FUNCTION__); + + IRDA_ASSERT(dev != NULL, return -1;); + + self = (struct nsc_ircc_cb *) dev->priv; + IRDA_ASSERT(self != NULL, return 0;); + + /* Stop device */ + netif_stop_queue(dev); + + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close(self->irlap); + self->irlap = NULL; + + iobase = self->io.fir_base; + + disable_dma(self->io.dma); + + /* Save current bank */ + bank = inb(iobase+BSR); + + /* Disable interrupts */ + switch_bank(iobase, BANK0); + outb(0, iobase+IER); + + free_irq(self->io.irq, dev); + free_dma(self->io.dma); + + /* Restore bank register */ + outb(bank, iobase+BSR); + + return 0; +} + +/* + * Function nsc_ircc_net_ioctl (dev, rq, cmd) + * + * Process IOCTL commands for this device + * + */ +static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct nsc_ircc_cb *self; + unsigned long flags; + int ret = 0; + + IRDA_ASSERT(dev != NULL, return -1;); + + self = dev->priv; + + IRDA_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; + break; + } + spin_lock_irqsave(&self->lock, flags); + nsc_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 */ + /* This is already protected */ + irq->ifr_receiving = nsc_ircc_is_receiving(self); + break; + default: + ret = -EOPNOTSUPP; + } + return ret; +} + +static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev) +{ + struct nsc_ircc_cb *self = (struct nsc_ircc_cb *) dev->priv; + + return &self->stats; +} + +static void nsc_ircc_suspend(struct nsc_ircc_cb *self) +{ + IRDA_MESSAGE("%s, Suspending\n", driver_name); + + if (self->io.suspended) + return; + + nsc_ircc_net_close(self->netdev); + + self->io.suspended = 1; +} + +static void nsc_ircc_wakeup(struct nsc_ircc_cb *self) +{ + if (!self->io.suspended) + return; + + nsc_ircc_setup(&self->io); + nsc_ircc_net_open(self->netdev); + + IRDA_MESSAGE("%s, Waking up\n", driver_name); + + self->io.suspended = 0; +} + +static int nsc_ircc_pmproc(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct nsc_ircc_cb *self = (struct nsc_ircc_cb*) dev->data; + if (self) { + switch (rqst) { + case PM_SUSPEND: + nsc_ircc_suspend(self); + break; + case PM_RESUME: + nsc_ircc_wakeup(self); + break; + } + } + return 0; +} + +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("NSC IrDA Device Driver"); +MODULE_LICENSE("GPL"); + + +module_param(qos_mtt_bits, int, 0); +MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); +module_param_array(io, int, NULL, 0); +MODULE_PARM_DESC(io, "Base I/O addresses"); +module_param_array(irq, int, NULL, 0); +MODULE_PARM_DESC(irq, "IRQ lines"); +module_param_array(dma, int, NULL, 0); +MODULE_PARM_DESC(dma, "DMA channels"); +module_param(dongle_id, int, 0); +MODULE_PARM_DESC(dongle_id, "Type-id of used dongle"); + +module_init(nsc_ircc_init); +module_exit(nsc_ircc_cleanup); + diff --git a/drivers/net/irda/nsc-ircc.h b/drivers/net/irda/nsc-ircc.h new file mode 100644 index 000000000000..6edf7e514624 --- /dev/null +++ b/drivers/net/irda/nsc-ircc.h @@ -0,0 +1,280 @@ +/********************************************************************* + * + * Filename: nsc-ircc.h + * Version: + * Description: + * Status: Experimental. + * Author: Dag Brattli <dagb@cs.uit.no> + * Created at: Fri Nov 13 14:37:40 1998 + * Modified at: Sun Jan 23 17:47:00 2000 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no> + * Copyright (c) 1998 Lichen Wang, <lwang@actisys.com> + * Copyright (c) 1998 Actisys Corp., www.actisys.com + * 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. + * + ********************************************************************/ + +#ifndef NSC_IRCC_H +#define NSC_IRCC_H + +#include <linux/time.h> + +#include <linux/spinlock.h> +#include <linux/pm.h> +#include <linux/types.h> +#include <asm/io.h> + +/* DMA modes needed */ +#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */ +#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */ + +/* Config registers for the '108 */ +#define CFG_108_BAIC 0x00 +#define CFG_108_CSRT 0x01 +#define CFG_108_MCTL 0x02 + +/* Config registers for the '338 */ +#define CFG_338_FER 0x00 +#define CFG_338_FAR 0x01 +#define CFG_338_PTR 0x02 +#define CFG_338_PNP0 0x1b +#define CFG_338_PNP1 0x1c +#define CFG_338_PNP3 0x4f + +/* Config registers for the '39x (in the logical device bank) */ +#define CFG_39X_LDN 0x07 /* Logical device number (Super I/O bank) */ +#define CFG_39X_SIOCF1 0x21 /* SuperI/O Config */ +#define CFG_39X_ACT 0x30 /* Device activation */ +#define CFG_39X_BASEH 0x60 /* Device base address (high bits) */ +#define CFG_39X_BASEL 0x61 /* Device base address (low bits) */ +#define CFG_39X_IRQNUM 0x70 /* Interrupt number & wake up enable */ +#define CFG_39X_IRQSEL 0x71 /* Interrupt select (edge/level + polarity) */ +#define CFG_39X_DMA0 0x74 /* DMA 0 configuration */ +#define CFG_39X_DMA1 0x75 /* DMA 1 configuration */ +#define CFG_39X_SPC 0xF0 /* Serial port configuration register */ + +/* Flags for configuration register CRF0 */ +#define APEDCRC 0x02 +#define ENBNKSEL 0x01 + +/* Set 0 */ +#define TXD 0x00 /* Transmit data port */ +#define RXD 0x00 /* Receive data port */ + +/* Register 1 */ +#define IER 0x01 /* Interrupt Enable Register*/ +#define IER_RXHDL_IE 0x01 /* Receiver high data level interrupt */ +#define IER_TXLDL_IE 0x02 /* Transeiver low data level interrupt */ +#define IER_LS_IE 0x04//* Link Status Interrupt */ +#define IER_ETXURI 0x04 /* Tx underrun */ +#define IER_DMA_IE 0x10 /* DMA finished interrupt */ +#define IER_TXEMP_IE 0x20 +#define IER_SFIF_IE 0x40 /* Frame status FIFO intr */ +#define IER_TMR_IE 0x80 /* Timer event */ + +#define FCR 0x02 /* (write only) */ +#define FCR_FIFO_EN 0x01 /* Enable FIFO's */ +#define FCR_RXSR 0x02 /* Rx FIFO soft reset */ +#define FCR_TXSR 0x04 /* Tx FIFO soft reset */ +#define FCR_RXTH 0x40 /* Rx FIFO threshold (set to 16) */ +#define FCR_TXTH 0x20 /* Tx FIFO threshold (set to 17) */ + +#define EIR 0x02 /* (read only) */ +#define EIR_RXHDL_EV 0x01 +#define EIR_TXLDL_EV 0x02 +#define EIR_LS_EV 0x04 +#define EIR_DMA_EV 0x10 +#define EIR_TXEMP_EV 0x20 +#define EIR_SFIF_EV 0x40 +#define EIR_TMR_EV 0x80 + +#define LCR 0x03 /* Link control register */ +#define LCR_WLS_8 0x03 /* 8 bits */ + +#define BSR 0x03 /* Bank select register */ +#define BSR_BKSE 0x80 +#define BANK0 LCR_WLS_8 /* Must make sure that we set 8N1 */ +#define BANK1 0x80 +#define BANK2 0xe0 +#define BANK3 0xe4 +#define BANK4 0xe8 +#define BANK5 0xec +#define BANK6 0xf0 +#define BANK7 0xf4 + +#define MCR 0x04 /* Mode Control Register */ +#define MCR_MODE_MASK ~(0xd0) +#define MCR_UART 0x00 +#define MCR_RESERVED 0x20 +#define MCR_SHARP_IR 0x40 +#define MCR_SIR 0x60 +#define MCR_MIR 0x80 +#define MCR_FIR 0xa0 +#define MCR_CEIR 0xb0 +#define MCR_IR_PLS 0x10 +#define MCR_DMA_EN 0x04 +#define MCR_EN_IRQ 0x08 +#define MCR_TX_DFR 0x08 + +#define LSR 0x05 /* Link status register */ +#define LSR_RXDA 0x01 /* Receiver data available */ +#define LSR_TXRDY 0x20 /* Transmitter ready */ +#define LSR_TXEMP 0x40 /* Transmitter empty */ + +#define ASCR 0x07 /* Auxillary Status and Control Register */ +#define ASCR_RXF_TOUT 0x01 /* Rx FIFO timeout */ +#define ASCR_FEND_INF 0x02 /* Frame end bytes in rx FIFO */ +#define ASCR_S_EOT 0x04 /* Set end of transmission */ +#define ASCT_RXBSY 0x20 /* Rx busy */ +#define ASCR_TXUR 0x40 /* Transeiver underrun */ +#define ASCR_CTE 0x80 /* Clear timer event */ + +/* Bank 2 */ +#define BGDL 0x00 /* Baud Generator Divisor Port (Low Byte) */ +#define BGDH 0x01 /* Baud Generator Divisor Port (High Byte) */ + +#define ECR1 0x02 /* Extended Control Register 1 */ +#define ECR1_EXT_SL 0x01 /* Extended Mode Select */ +#define ECR1_DMANF 0x02 /* DMA Fairness */ +#define ECR1_DMATH 0x04 /* DMA Threshold */ +#define ECR1_DMASWP 0x08 /* DMA Swap */ + +#define EXCR2 0x04 +#define EXCR2_TFSIZ 0x01 /* Rx FIFO size = 32 */ +#define EXCR2_RFSIZ 0x04 /* Tx FIFO size = 32 */ + +#define TXFLV 0x06 /* Tx FIFO level */ +#define RXFLV 0x07 /* Rx FIFO level */ + +/* Bank 3 */ +#define MID 0x00 + +/* Bank 4 */ +#define TMRL 0x00 /* Timer low byte */ +#define TMRH 0x01 /* Timer high byte */ +#define IRCR1 0x02 /* Infrared control register 1 */ +#define IRCR1_TMR_EN 0x01 /* Timer enable */ + +#define TFRLL 0x04 +#define TFRLH 0x05 +#define RFRLL 0x06 +#define RFRLH 0x07 + +/* Bank 5 */ +#define IRCR2 0x04 /* Infrared control register 2 */ +#define IRCR2_MDRS 0x04 /* MIR data rate select */ +#define IRCR2_FEND_MD 0x20 /* */ + +#define FRM_ST 0x05 /* Frame status FIFO */ +#define FRM_ST_VLD 0x80 /* Frame status FIFO data valid */ +#define FRM_ST_ERR_MSK 0x5f +#define FRM_ST_LOST_FR 0x40 /* Frame lost */ +#define FRM_ST_MAX_LEN 0x10 /* Max frame len exceeded */ +#define FRM_ST_PHY_ERR 0x08 /* Physical layer error */ +#define FRM_ST_BAD_CRC 0x04 +#define FRM_ST_OVR1 0x02 /* Rx FIFO overrun */ +#define FRM_ST_OVR2 0x01 /* Frame status FIFO overrun */ + +#define RFLFL 0x06 +#define RFLFH 0x07 + +/* Bank 6 */ +#define IR_CFG2 0x00 +#define IR_CFG2_DIS_CRC 0x02 + +/* Bank 7 */ +#define IRM_CR 0x07 /* Infrared module control register */ +#define IRM_CR_IRX_MSL 0x40 +#define IRM_CR_AF_MNT 0x80 /* Automatic format */ + +/* NSC chip information */ +struct nsc_chip { + char *name; /* Name of chipset */ + int cfg[3]; /* Config registers */ + u_int8_t cid_index; /* Chip identification index reg */ + u_int8_t cid_value; /* Chip identification expected value */ + u_int8_t cid_mask; /* Chip identification revision mask */ + + /* Functions for probing and initializing the specific chip */ + int (*probe)(struct nsc_chip *chip, chipio_t *info); + int (*init)(struct nsc_chip *chip, chipio_t *info); +}; +typedef struct nsc_chip nsc_chip_t; + +/* For storing entries in the status FIFO */ +struct st_fifo_entry { + int status; + int len; +}; + +#define MAX_TX_WINDOW 7 +#define MAX_RX_WINDOW 7 + +struct st_fifo { + struct st_fifo_entry entries[MAX_RX_WINDOW]; + int pending_bytes; + int head; + int tail; + int len; +}; + +struct frame_cb { + void *start; /* Start of frame in DMA mem */ + int len; /* Lenght of frame in DMA mem */ +}; + +struct tx_fifo { + struct frame_cb queue[MAX_TX_WINDOW]; /* Info about frames in queue */ + int ptr; /* Currently being sent */ + int len; /* Lenght of queue */ + int free; /* Next free slot */ + void *tail; /* Next free start in DMA mem */ +}; + +/* Private data for each instance */ +struct nsc_ircc_cb { + struct st_fifo st_fifo; /* Info about received frames */ + struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ + + 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 */ + struct qos_info qos; /* QoS capabilities for this device */ + + chipio_t io; /* IrDA controller information */ + iobuff_t tx_buff; /* Transmit buffer */ + iobuff_t rx_buff; /* Receive buffer */ + dma_addr_t tx_buff_dma; + dma_addr_t rx_buff_dma; + + __u8 ier; /* Interrupt enable register */ + + struct timeval stamp; + struct timeval now; + + spinlock_t lock; /* For serializing operations */ + + __u32 new_speed; + int index; /* Instance index */ + + struct pm_dev *dev; +}; + +static inline void switch_bank(int iobase, int bank) +{ + outb(bank, iobase+BSR); +} + +#endif /* NSC_IRCC_H */ diff --git a/drivers/net/irda/old_belkin-sir.c b/drivers/net/irda/old_belkin-sir.c new file mode 100644 index 000000000000..8c22c7374a23 --- /dev/null +++ b/drivers/net/irda/old_belkin-sir.c @@ -0,0 +1,156 @@ +/********************************************************************* + * + * Filename: old_belkin.c + * Version: 1.1 + * Description: Driver for the Belkin (old) SmartBeam dongle + * Status: Experimental... + * Author: Jean Tourrilhes <jt@hpl.hp.com> + * Created at: 22/11/99 + * Modified at: Fri Dec 17 09:13:32 1999 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1999 Jean Tourrilhes, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> + +#include <net/irda/irda.h> +// #include <net/irda/irda_device.h> + +#include "sir-dev.h" + +/* + * Belkin is selling a dongle called the SmartBeam. + * In fact, there is two hardware version of this dongle, of course with + * the same name and looking the exactly same (grrr...). + * I guess that I've got the old one, because inside I don't have + * a jumper for IrDA/ASK... + * + * As far as I can make it from info on their web site, the old dongle + * support only 9600 b/s, which make our life much simpler as far as + * the driver is concerned, but you might not like it very much ;-) + * The new SmartBeam does 115 kb/s, and I've not tested it... + * + * Belkin claim that the correct driver for the old dongle (in Windows) + * is the generic Parallax 9500a driver, but the Linux LiteLink driver + * fails for me (probably because Linux-IrDA doesn't rate fallback), + * so I created this really dumb driver... + * + * In fact, this driver doesn't do much. The only thing it does is to + * prevent Linux-IrDA to use any other speed than 9600 b/s ;-) This + * driver is called "old_belkin" so that when the new SmartBeam is supported + * its driver can be called "belkin" instead of "new_belkin". + * + * Note : this driver was written without any info/help from Belkin, + * so a lot of info here might be totally wrong. Blame me ;-) + */ + +static int old_belkin_open(struct sir_dev *dev); +static int old_belkin_close(struct sir_dev *dev); +static int old_belkin_change_speed(struct sir_dev *dev, unsigned speed); +static int old_belkin_reset(struct sir_dev *dev); + +static struct dongle_driver old_belkin = { + .owner = THIS_MODULE, + .driver_name = "Old Belkin SmartBeam", + .type = IRDA_OLD_BELKIN_DONGLE, + .open = old_belkin_open, + .close = old_belkin_close, + .reset = old_belkin_reset, + .set_speed = old_belkin_change_speed, +}; + +static int __init old_belkin_sir_init(void) +{ + return irda_register_dongle(&old_belkin); +} + +static void __exit old_belkin_sir_cleanup(void) +{ + irda_unregister_dongle(&old_belkin); +} + +static int old_belkin_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* Power on dongle */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Not too fast, please... */ + qos->baud_rate.bits &= IR_9600; + /* Needs at least 10 ms (totally wild guess, can do probably better) */ + qos->min_turn_time.bits = 0x01; + irda_qos_bits_to_value(qos); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int old_belkin_close(struct sir_dev *dev) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* Power off dongle */ + sirdev_set_dtr_rts(dev, FALSE, FALSE); + + return 0; +} + +/* + * Function old_belkin_change_speed (task) + * + * With only one speed available, not much to do... + */ +static int old_belkin_change_speed(struct sir_dev *dev, unsigned speed) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + dev->speed = 9600; + return (speed==dev->speed) ? 0 : -EINVAL; +} + +/* + * Function old_belkin_reset (task) + * + * Reset the Old-Belkin type dongle. + * + */ +static int old_belkin_reset(struct sir_dev *dev) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* This dongles speed "defaults" to 9600 bps ;-) */ + dev->speed = 9600; + + return 0; +} + +MODULE_AUTHOR("Jean Tourrilhes <jt@hpl.hp.com>"); +MODULE_DESCRIPTION("Belkin (old) SmartBeam dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-7"); /* IRDA_OLD_BELKIN_DONGLE */ + +module_init(old_belkin_sir_init); +module_exit(old_belkin_sir_cleanup); diff --git a/drivers/net/irda/old_belkin.c b/drivers/net/irda/old_belkin.c new file mode 100644 index 000000000000..26f81fd28371 --- /dev/null +++ b/drivers/net/irda/old_belkin.c @@ -0,0 +1,164 @@ +/********************************************************************* + * + * Filename: old_belkin.c + * Version: 1.1 + * Description: Driver for the Belkin (old) SmartBeam dongle + * Status: Experimental... + * Author: Jean Tourrilhes <jt@hpl.hp.com> + * Created at: 22/11/99 + * Modified at: Fri Dec 17 09:13:32 1999 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1999 Jean Tourrilhes, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/init.h> + +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> + +/* + * Belkin is selling a dongle called the SmartBeam. + * In fact, there is two hardware version of this dongle, of course with + * the same name and looking the exactly same (grrr...). + * I guess that I've got the old one, because inside I don't have + * a jumper for IrDA/ASK... + * + * As far as I can make it from info on their web site, the old dongle + * support only 9600 b/s, which make our life much simpler as far as + * the driver is concerned, but you might not like it very much ;-) + * The new SmartBeam does 115 kb/s, and I've not tested it... + * + * Belkin claim that the correct driver for the old dongle (in Windows) + * is the generic Parallax 9500a driver, but the Linux LiteLink driver + * fails for me (probably because Linux-IrDA doesn't rate fallback), + * so I created this really dumb driver... + * + * In fact, this driver doesn't do much. The only thing it does is to + * prevent Linux-IrDA to use any other speed than 9600 b/s ;-) This + * driver is called "old_belkin" so that when the new SmartBeam is supported + * its driver can be called "belkin" instead of "new_belkin". + * + * Note : this driver was written without any info/help from Belkin, + * so a lot of info here might be totally wrong. Blame me ;-) + */ + +/* Let's guess */ +#define MIN_DELAY 25 /* 15 us, but wait a little more to be sure */ + +static void old_belkin_open(dongle_t *self, struct qos_info *qos); +static void old_belkin_close(dongle_t *self); +static int old_belkin_change_speed(struct irda_task *task); +static int old_belkin_reset(struct irda_task *task); + +/* These are the baudrates supported */ +/* static __u32 baud_rates[] = { 9600 }; */ + +static struct dongle_reg dongle = { + .type = IRDA_OLD_BELKIN_DONGLE, + .open = old_belkin_open, + .close = old_belkin_close, + .reset = old_belkin_reset, + .change_speed = old_belkin_change_speed, + .owner = THIS_MODULE, +}; + +static int __init old_belkin_init(void) +{ + return irda_device_register_dongle(&dongle); +} + +static void __exit old_belkin_cleanup(void) +{ + irda_device_unregister_dongle(&dongle); +} + +static void old_belkin_open(dongle_t *self, struct qos_info *qos) +{ + /* Not too fast, please... */ + qos->baud_rate.bits &= IR_9600; + /* Needs at least 10 ms (totally wild guess, can do probably better) */ + qos->min_turn_time.bits = 0x01; +} + +static void old_belkin_close(dongle_t *self) +{ + /* Power off dongle */ + self->set_dtr_rts(self->dev, FALSE, FALSE); +} + +/* + * Function old_belkin_change_speed (task) + * + * With only one speed available, not much to do... + */ +static int old_belkin_change_speed(struct irda_task *task) +{ + irda_task_next_state(task, IRDA_TASK_DONE); + + return 0; +} + +/* + * Function old_belkin_reset (task) + * + * Reset the Old-Belkin type dongle. + * + */ +static int old_belkin_reset(struct irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + + /* Power on dongle */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + + /* Sleep a minimum of 15 us */ + udelay(MIN_DELAY); + + /* This dongles speed "defaults" to 9600 bps ;-) */ + self->speed = 9600; + + irda_task_next_state(task, IRDA_TASK_DONE); + + return 0; +} + +MODULE_AUTHOR("Jean Tourrilhes <jt@hpl.hp.com>"); +MODULE_DESCRIPTION("Belkin (old) SmartBeam dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-7"); /* IRDA_OLD_BELKIN_DONGLE */ + +/* + * Function init_module (void) + * + * Initialize Old-Belkin module + * + */ +module_init(old_belkin_init); + +/* + * Function cleanup_module (void) + * + * Cleanup Old-Belkin module + * + */ +module_exit(old_belkin_cleanup); diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c new file mode 100644 index 000000000000..89f5096cab74 --- /dev/null +++ b/drivers/net/irda/sa1100_ir.c @@ -0,0 +1,1045 @@ +/* + * linux/drivers/net/irda/sa1100_ir.c + * + * Copyright (C) 2000-2001 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Infra-red driver for the StrongARM SA1100 embedded microprocessor + * + * Note that we don't have to worry about the SA1111's DMA bugs in here, + * so we use the straight forward dma_map_* functions with a null pointer. + * + * This driver takes one kernel command line parameter, sa1100ir=, with + * the following options: + * max_rate:baudrate - set the maximum baud rate + * power_leve:level - set the transmitter power level + * tx_lpm:0|1 - set transmit low power mode + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/slab.h> +#include <linux/rtnetlink.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> + +#include <net/irda/irda.h> +#include <net/irda/wrapper.h> +#include <net/irda/irda_device.h> + +#include <asm/irq.h> +#include <asm/dma.h> +#include <asm/hardware.h> +#include <asm/mach/irda.h> + +static int power_level = 3; +static int tx_lpm; +static int max_rate = 4000000; + +struct sa1100_irda { + unsigned char hscr0; + unsigned char utcr4; + unsigned char power; + unsigned char open; + + int speed; + int newspeed; + + struct sk_buff *txskb; + struct sk_buff *rxskb; + dma_addr_t txbuf_dma; + dma_addr_t rxbuf_dma; + dma_regs_t *txdma; + dma_regs_t *rxdma; + + struct net_device_stats stats; + struct device *dev; + struct irda_platform_data *pdata; + struct irlap_cb *irlap; + struct qos_info qos; + + iobuff_t tx_buff; + iobuff_t rx_buff; +}; + +#define IS_FIR(si) ((si)->speed >= 4000000) + +#define HPSIR_MAX_RXLEN 2047 + +/* + * Allocate and map the receive buffer, unless it is already allocated. + */ +static int sa1100_irda_rx_alloc(struct sa1100_irda *si) +{ + if (si->rxskb) + return 0; + + si->rxskb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC); + + if (!si->rxskb) { + printk(KERN_ERR "sa1100_ir: out of memory for RX SKB\n"); + return -ENOMEM; + } + + /* + * Align any IP headers that may be contained + * within the frame. + */ + skb_reserve(si->rxskb, 1); + + si->rxbuf_dma = dma_map_single(si->dev, si->rxskb->data, + HPSIR_MAX_RXLEN, + DMA_FROM_DEVICE); + return 0; +} + +/* + * We want to get here as soon as possible, and get the receiver setup. + * We use the existing buffer. + */ +static void sa1100_irda_rx_dma_start(struct sa1100_irda *si) +{ + if (!si->rxskb) { + printk(KERN_ERR "sa1100_ir: rx buffer went missing\n"); + return; + } + + /* + * First empty receive FIFO + */ + Ser2HSCR0 = si->hscr0 | HSCR0_HSSP; + + /* + * Enable the DMA, receiver and receive interrupt. + */ + sa1100_clear_dma(si->rxdma); + sa1100_start_dma(si->rxdma, si->rxbuf_dma, HPSIR_MAX_RXLEN); + Ser2HSCR0 = si->hscr0 | HSCR0_HSSP | HSCR0_RXE; +} + +/* + * Set the IrDA communications speed. + */ +static int sa1100_irda_set_speed(struct sa1100_irda *si, int speed) +{ + unsigned long flags; + int brd, ret = -EINVAL; + + switch (speed) { + case 9600: case 19200: case 38400: + case 57600: case 115200: + brd = 3686400 / (16 * speed) - 1; + + /* + * Stop the receive DMA. + */ + if (IS_FIR(si)) + sa1100_stop_dma(si->rxdma); + + local_irq_save(flags); + + Ser2UTCR3 = 0; + Ser2HSCR0 = HSCR0_UART; + + Ser2UTCR1 = brd >> 8; + Ser2UTCR2 = brd; + + /* + * Clear status register + */ + Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; + Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; + + if (si->pdata->set_speed) + si->pdata->set_speed(si->dev, speed); + + si->speed = speed; + + local_irq_restore(flags); + ret = 0; + break; + + case 4000000: + local_irq_save(flags); + + si->hscr0 = 0; + + Ser2HSSR0 = 0xff; + Ser2HSCR0 = si->hscr0 | HSCR0_HSSP; + Ser2UTCR3 = 0; + + si->speed = speed; + + if (si->pdata->set_speed) + si->pdata->set_speed(si->dev, speed); + + sa1100_irda_rx_alloc(si); + sa1100_irda_rx_dma_start(si); + + local_irq_restore(flags); + + break; + + default: + break; + } + + return ret; +} + +/* + * Control the power state of the IrDA transmitter. + * State: + * 0 - off + * 1 - short range, lowest power + * 2 - medium range, medium power + * 3 - maximum range, high power + * + * Currently, only assabet is known to support this. + */ +static int +__sa1100_irda_set_power(struct sa1100_irda *si, unsigned int state) +{ + int ret = 0; + if (si->pdata->set_power) + ret = si->pdata->set_power(si->dev, state); + return ret; +} + +static inline int +sa1100_set_power(struct sa1100_irda *si, unsigned int state) +{ + int ret; + + ret = __sa1100_irda_set_power(si, state); + if (ret == 0) + si->power = state; + + return ret; +} + +static int sa1100_irda_startup(struct sa1100_irda *si) +{ + int ret; + + /* + * Ensure that the ports for this device are setup correctly. + */ + if (si->pdata->startup) + si->pdata->startup(si->dev); + + /* + * Configure PPC for IRDA - we want to drive TXD2 low. + * We also want to drive this pin low during sleep. + */ + PPSR &= ~PPC_TXD2; + PSDR &= ~PPC_TXD2; + PPDR |= PPC_TXD2; + + /* + * Enable HP-SIR modulation, and ensure that the port is disabled. + */ + Ser2UTCR3 = 0; + Ser2HSCR0 = HSCR0_UART; + Ser2UTCR4 = si->utcr4; + Ser2UTCR0 = UTCR0_8BitData; + Ser2HSCR2 = HSCR2_TrDataH | HSCR2_RcDataL; + + /* + * Clear status register + */ + Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; + + ret = sa1100_irda_set_speed(si, si->speed = 9600); + if (ret) { + Ser2UTCR3 = 0; + Ser2HSCR0 = 0; + + if (si->pdata->shutdown) + si->pdata->shutdown(si->dev); + } + + return ret; +} + +static void sa1100_irda_shutdown(struct sa1100_irda *si) +{ + /* + * Stop all DMA activity. + */ + sa1100_stop_dma(si->rxdma); + sa1100_stop_dma(si->txdma); + + /* Disable the port. */ + Ser2UTCR3 = 0; + Ser2HSCR0 = 0; + + if (si->pdata->shutdown) + si->pdata->shutdown(si->dev); +} + +#ifdef CONFIG_PM +/* + * Suspend the IrDA interface. + */ +static int sa1100_irda_suspend(struct device *_dev, u32 state, u32 level) +{ + struct net_device *dev = dev_get_drvdata(_dev); + struct sa1100_irda *si; + + if (!dev || level != SUSPEND_DISABLE) + return 0; + + si = dev->priv; + if (si->open) { + /* + * Stop the transmit queue + */ + netif_device_detach(dev); + disable_irq(dev->irq); + sa1100_irda_shutdown(si); + __sa1100_irda_set_power(si, 0); + } + + return 0; +} + +/* + * Resume the IrDA interface. + */ +static int sa1100_irda_resume(struct device *_dev, u32 level) +{ + struct net_device *dev = dev_get_drvdata(_dev); + struct sa1100_irda *si; + + if (!dev || level != RESUME_ENABLE) + return 0; + + si = dev->priv; + if (si->open) { + /* + * If we missed a speed change, initialise at the new speed + * directly. It is debatable whether this is actually + * required, but in the interests of continuing from where + * we left off it is desireable. The converse argument is + * that we should re-negotiate at 9600 baud again. + */ + if (si->newspeed) { + si->speed = si->newspeed; + si->newspeed = 0; + } + + sa1100_irda_startup(si); + __sa1100_irda_set_power(si, si->power); + enable_irq(dev->irq); + + /* + * This automatically wakes up the queue + */ + netif_device_attach(dev); + } + + return 0; +} +#else +#define sa1100_irda_suspend NULL +#define sa1100_irda_resume NULL +#endif + +/* + * HP-SIR format interrupt service routines. + */ +static void sa1100_irda_hpsir_irq(struct net_device *dev) +{ + struct sa1100_irda *si = dev->priv; + int status; + + status = Ser2UTSR0; + + /* + * Deal with any receive errors first. The bytes in error may be + * the only bytes in the receive FIFO, so we do this first. + */ + while (status & UTSR0_EIF) { + int stat, data; + + stat = Ser2UTSR1; + data = Ser2UTDR; + + if (stat & (UTSR1_FRE | UTSR1_ROR)) { + si->stats.rx_errors++; + if (stat & UTSR1_FRE) + si->stats.rx_frame_errors++; + if (stat & UTSR1_ROR) + si->stats.rx_fifo_errors++; + } else + async_unwrap_char(dev, &si->stats, &si->rx_buff, data); + + status = Ser2UTSR0; + } + + /* + * We must clear certain bits. + */ + Ser2UTSR0 = status & (UTSR0_RID | UTSR0_RBB | UTSR0_REB); + + if (status & UTSR0_RFS) { + /* + * There are at least 4 bytes in the FIFO. Read 3 bytes + * and leave the rest to the block below. + */ + async_unwrap_char(dev, &si->stats, &si->rx_buff, Ser2UTDR); + async_unwrap_char(dev, &si->stats, &si->rx_buff, Ser2UTDR); + async_unwrap_char(dev, &si->stats, &si->rx_buff, Ser2UTDR); + } + + if (status & (UTSR0_RFS | UTSR0_RID)) { + /* + * Fifo contains more than 1 character. + */ + do { + async_unwrap_char(dev, &si->stats, &si->rx_buff, + Ser2UTDR); + } while (Ser2UTSR1 & UTSR1_RNE); + + dev->last_rx = jiffies; + } + + if (status & UTSR0_TFS && si->tx_buff.len) { + /* + * Transmitter FIFO is not full + */ + do { + Ser2UTDR = *si->tx_buff.data++; + si->tx_buff.len -= 1; + } while (Ser2UTSR1 & UTSR1_TNF && si->tx_buff.len); + + if (si->tx_buff.len == 0) { + si->stats.tx_packets++; + si->stats.tx_bytes += si->tx_buff.data - + si->tx_buff.head; + + /* + * We need to ensure that the transmitter has + * finished. + */ + do + rmb(); + while (Ser2UTSR1 & UTSR1_TBY); + + /* + * Ok, we've finished transmitting. Now enable + * the receiver. Sometimes we get a receive IRQ + * immediately after a transmit... + */ + Ser2UTSR0 = UTSR0_REB | UTSR0_RBB | UTSR0_RID; + Ser2UTCR3 = UTCR3_RIE | UTCR3_RXE | UTCR3_TXE; + + if (si->newspeed) { + sa1100_irda_set_speed(si, si->newspeed); + si->newspeed = 0; + } + + /* I'm hungry! */ + netif_wake_queue(dev); + } + } +} + +static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev) +{ + struct sk_buff *skb = si->rxskb; + dma_addr_t dma_addr; + unsigned int len, stat, data; + + if (!skb) { + printk(KERN_ERR "sa1100_ir: SKB is NULL!\n"); + return; + } + + /* + * Get the current data position. + */ + dma_addr = sa1100_get_dma_pos(si->rxdma); + len = dma_addr - si->rxbuf_dma; + if (len > HPSIR_MAX_RXLEN) + len = HPSIR_MAX_RXLEN; + dma_unmap_single(si->dev, si->rxbuf_dma, len, DMA_FROM_DEVICE); + + do { + /* + * Read Status, and then Data. + */ + stat = Ser2HSSR1; + rmb(); + data = Ser2HSDR; + + if (stat & (HSSR1_CRE | HSSR1_ROR)) { + si->stats.rx_errors++; + if (stat & HSSR1_CRE) + si->stats.rx_crc_errors++; + if (stat & HSSR1_ROR) + si->stats.rx_frame_errors++; + } else + skb->data[len++] = data; + + /* + * If we hit the end of frame, there's + * no point in continuing. + */ + if (stat & HSSR1_EOF) + break; + } while (Ser2HSSR0 & HSSR0_EIF); + + if (stat & HSSR1_EOF) { + si->rxskb = NULL; + + skb_put(skb, len); + skb->dev = dev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + si->stats.rx_packets++; + si->stats.rx_bytes += len; + + /* + * Before we pass the buffer up, allocate a new one. + */ + sa1100_irda_rx_alloc(si); + + netif_rx(skb); + dev->last_rx = jiffies; + } else { + /* + * Remap the buffer. + */ + si->rxbuf_dma = dma_map_single(si->dev, si->rxskb->data, + HPSIR_MAX_RXLEN, + DMA_FROM_DEVICE); + } +} + +/* + * FIR format interrupt service routine. We only have to + * handle RX events; transmit events go via the TX DMA handler. + * + * No matter what, we disable RX, process, and the restart RX. + */ +static void sa1100_irda_fir_irq(struct net_device *dev) +{ + struct sa1100_irda *si = dev->priv; + + /* + * Stop RX DMA + */ + sa1100_stop_dma(si->rxdma); + + /* + * Framing error - we throw away the packet completely. + * Clearing RXE flushes the error conditions and data + * from the fifo. + */ + if (Ser2HSSR0 & (HSSR0_FRE | HSSR0_RAB)) { + si->stats.rx_errors++; + + if (Ser2HSSR0 & HSSR0_FRE) + si->stats.rx_frame_errors++; + + /* + * Clear out the DMA... + */ + Ser2HSCR0 = si->hscr0 | HSCR0_HSSP; + + /* + * Clear selected status bits now, so we + * don't miss them next time around. + */ + Ser2HSSR0 = HSSR0_FRE | HSSR0_RAB; + } + + /* + * Deal with any receive errors. The any of the lowest + * 8 bytes in the FIFO may contain an error. We must read + * them one by one. The "error" could even be the end of + * packet! + */ + if (Ser2HSSR0 & HSSR0_EIF) + sa1100_irda_fir_error(si, dev); + + /* + * No matter what happens, we must restart reception. + */ + sa1100_irda_rx_dma_start(si); +} + +static irqreturn_t sa1100_irda_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + if (IS_FIR(((struct sa1100_irda *)dev->priv))) + sa1100_irda_fir_irq(dev); + else + sa1100_irda_hpsir_irq(dev); + return IRQ_HANDLED; +} + +/* + * TX DMA completion handler. + */ +static void sa1100_irda_txdma_irq(void *id) +{ + struct net_device *dev = id; + struct sa1100_irda *si = dev->priv; + struct sk_buff *skb = si->txskb; + + si->txskb = NULL; + + /* + * Wait for the transmission to complete. Unfortunately, + * the hardware doesn't give us an interrupt to indicate + * "end of frame". + */ + do + rmb(); + while (!(Ser2HSSR0 & HSSR0_TUR) || Ser2HSSR1 & HSSR1_TBY); + + /* + * Clear the transmit underrun bit. + */ + Ser2HSSR0 = HSSR0_TUR; + + /* + * Do we need to change speed? Note that we're lazy + * here - we don't free the old rxskb. We don't need + * to allocate a buffer either. + */ + if (si->newspeed) { + sa1100_irda_set_speed(si, si->newspeed); + si->newspeed = 0; + } + + /* + * Start reception. This disables the transmitter for + * us. This will be using the existing RX buffer. + */ + sa1100_irda_rx_dma_start(si); + + /* + * Account and free the packet. + */ + if (skb) { + dma_unmap_single(si->dev, si->txbuf_dma, skb->len, DMA_TO_DEVICE); + si->stats.tx_packets ++; + si->stats.tx_bytes += skb->len; + dev_kfree_skb_irq(skb); + } + + /* + * Make sure that the TX queue is available for sending + * (for retries). TX has priority over RX at all times. + */ + netif_wake_queue(dev); +} + +static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct sa1100_irda *si = dev->priv; + int speed = irda_get_next_speed(skb); + + /* + * Does this packet contain a request to change the interface + * speed? If so, remember it until we complete the transmission + * of this frame. + */ + if (speed != si->speed && speed != -1) + si->newspeed = speed; + + /* + * If this is an empty frame, we can bypass a lot. + */ + if (skb->len == 0) { + if (si->newspeed) { + si->newspeed = 0; + sa1100_irda_set_speed(si, speed); + } + dev_kfree_skb(skb); + return 0; + } + + if (!IS_FIR(si)) { + netif_stop_queue(dev); + + si->tx_buff.data = si->tx_buff.head; + si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, + si->tx_buff.truesize); + + /* + * Set the transmit interrupt enable. This will fire + * off an interrupt immediately. Note that we disable + * the receiver so we won't get spurious characteres + * received. + */ + Ser2UTCR3 = UTCR3_TIE | UTCR3_TXE; + + dev_kfree_skb(skb); + } else { + int mtt = irda_get_mtt(skb); + + /* + * We must not be transmitting... + */ + if (si->txskb) + BUG(); + + netif_stop_queue(dev); + + si->txskb = skb; + si->txbuf_dma = dma_map_single(si->dev, skb->data, + skb->len, DMA_TO_DEVICE); + + sa1100_start_dma(si->txdma, si->txbuf_dma, skb->len); + + /* + * If we have a mean turn-around time, impose the specified + * specified delay. We could shorten this by timing from + * the point we received the packet. + */ + if (mtt) + udelay(mtt); + + Ser2HSCR0 = si->hscr0 | HSCR0_HSSP | HSCR0_TXE; + } + + dev->trans_start = jiffies; + + return 0; +} + +static int +sa1100_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) +{ + struct if_irda_req *rq = (struct if_irda_req *)ifreq; + struct sa1100_irda *si = dev->priv; + int ret = -EOPNOTSUPP; + + switch (cmd) { + case SIOCSBANDWIDTH: + if (capable(CAP_NET_ADMIN)) { + /* + * We are unable to set the speed if the + * device is not running. + */ + if (si->open) { + ret = sa1100_irda_set_speed(si, + rq->ifr_baudrate); + } else { + printk("sa1100_irda_ioctl: SIOCSBANDWIDTH: !netif_running\n"); + ret = 0; + } + } + break; + + case SIOCSMEDIABUSY: + ret = -EPERM; + if (capable(CAP_NET_ADMIN)) { + irda_device_set_media_busy(dev, TRUE); + ret = 0; + } + break; + + case SIOCGRECEIVING: + rq->ifr_receiving = IS_FIR(si) ? 0 + : si->rx_buff.state != OUTSIDE_FRAME; + break; + + default: + break; + } + + return ret; +} + +static struct net_device_stats *sa1100_irda_stats(struct net_device *dev) +{ + struct sa1100_irda *si = dev->priv; + return &si->stats; +} + +static int sa1100_irda_start(struct net_device *dev) +{ + struct sa1100_irda *si = dev->priv; + int err; + + si->speed = 9600; + + err = request_irq(dev->irq, sa1100_irda_irq, 0, dev->name, dev); + if (err) + goto err_irq; + + err = sa1100_request_dma(DMA_Ser2HSSPRd, "IrDA receive", + NULL, NULL, &si->rxdma); + if (err) + goto err_rx_dma; + + err = sa1100_request_dma(DMA_Ser2HSSPWr, "IrDA transmit", + sa1100_irda_txdma_irq, dev, &si->txdma); + if (err) + goto err_tx_dma; + + /* + * The interrupt must remain disabled for now. + */ + disable_irq(dev->irq); + + /* + * Setup the serial port for the specified speed. + */ + err = sa1100_irda_startup(si); + if (err) + goto err_startup; + + /* + * Open a new IrLAP layer instance. + */ + si->irlap = irlap_open(dev, &si->qos, "sa1100"); + err = -ENOMEM; + if (!si->irlap) + goto err_irlap; + + /* + * Now enable the interrupt and start the queue + */ + si->open = 1; + sa1100_set_power(si, power_level); /* low power mode */ + enable_irq(dev->irq); + netif_start_queue(dev); + return 0; + +err_irlap: + si->open = 0; + sa1100_irda_shutdown(si); +err_startup: + sa1100_free_dma(si->txdma); +err_tx_dma: + sa1100_free_dma(si->rxdma); +err_rx_dma: + free_irq(dev->irq, dev); +err_irq: + return err; +} + +static int sa1100_irda_stop(struct net_device *dev) +{ + struct sa1100_irda *si = dev->priv; + + disable_irq(dev->irq); + sa1100_irda_shutdown(si); + + /* + * If we have been doing DMA receive, make sure we + * tidy that up cleanly. + */ + if (si->rxskb) { + dma_unmap_single(si->dev, si->rxbuf_dma, HPSIR_MAX_RXLEN, + DMA_FROM_DEVICE); + dev_kfree_skb(si->rxskb); + si->rxskb = NULL; + } + + /* Stop IrLAP */ + if (si->irlap) { + irlap_close(si->irlap); + si->irlap = NULL; + } + + netif_stop_queue(dev); + si->open = 0; + + /* + * Free resources + */ + sa1100_free_dma(si->txdma); + sa1100_free_dma(si->rxdma); + free_irq(dev->irq, dev); + + sa1100_set_power(si, 0); + + return 0; +} + +static int sa1100_irda_init_iobuf(iobuff_t *io, int size) +{ + io->head = kmalloc(size, GFP_KERNEL | GFP_DMA); + if (io->head != NULL) { + io->truesize = size; + io->in_frame = FALSE; + io->state = OUTSIDE_FRAME; + io->data = io->head; + } + return io->head ? 0 : -ENOMEM; +} + +static int sa1100_irda_probe(struct device *_dev) +{ + struct platform_device *pdev = to_platform_device(_dev); + struct net_device *dev; + struct sa1100_irda *si; + unsigned int baudrate_mask; + int err; + + if (!pdev->dev.platform_data) + return -EINVAL; + + err = request_mem_region(__PREG(Ser2UTCR0), 0x24, "IrDA") ? 0 : -EBUSY; + if (err) + goto err_mem_1; + err = request_mem_region(__PREG(Ser2HSCR0), 0x1c, "IrDA") ? 0 : -EBUSY; + if (err) + goto err_mem_2; + err = request_mem_region(__PREG(Ser2HSCR2), 0x04, "IrDA") ? 0 : -EBUSY; + if (err) + goto err_mem_3; + + dev = alloc_irdadev(sizeof(struct sa1100_irda)); + if (!dev) + goto err_mem_4; + + si = dev->priv; + si->dev = &pdev->dev; + si->pdata = pdev->dev.platform_data; + + /* + * Initialise the HP-SIR buffers + */ + err = sa1100_irda_init_iobuf(&si->rx_buff, 14384); + if (err) + goto err_mem_5; + err = sa1100_irda_init_iobuf(&si->tx_buff, 4000); + if (err) + goto err_mem_5; + + dev->hard_start_xmit = sa1100_irda_hard_xmit; + dev->open = sa1100_irda_start; + dev->stop = sa1100_irda_stop; + dev->do_ioctl = sa1100_irda_ioctl; + dev->get_stats = sa1100_irda_stats; + dev->irq = IRQ_Ser2ICP; + + irda_init_max_qos_capabilies(&si->qos); + + /* + * We support original IRDA up to 115k2. (we don't currently + * support 4Mbps). Min Turn Time set to 1ms or greater. + */ + baudrate_mask = IR_9600; + + switch (max_rate) { + case 4000000: baudrate_mask |= IR_4000000 << 8; + case 115200: baudrate_mask |= IR_115200; + case 57600: baudrate_mask |= IR_57600; + case 38400: baudrate_mask |= IR_38400; + case 19200: baudrate_mask |= IR_19200; + } + + si->qos.baud_rate.bits &= baudrate_mask; + si->qos.min_turn_time.bits = 7; + + irda_qos_bits_to_value(&si->qos); + + si->utcr4 = UTCR4_HPSIR; + if (tx_lpm) + si->utcr4 |= UTCR4_Z1_6us; + + /* + * Initially enable HP-SIR modulation, and ensure that the port + * is disabled. + */ + Ser2UTCR3 = 0; + Ser2UTCR4 = si->utcr4; + Ser2HSCR0 = HSCR0_UART; + + err = register_netdev(dev); + if (err == 0) + dev_set_drvdata(&pdev->dev, dev); + + if (err) { + err_mem_5: + kfree(si->tx_buff.head); + kfree(si->rx_buff.head); + free_netdev(dev); + err_mem_4: + release_mem_region(__PREG(Ser2HSCR2), 0x04); + err_mem_3: + release_mem_region(__PREG(Ser2HSCR0), 0x1c); + err_mem_2: + release_mem_region(__PREG(Ser2UTCR0), 0x24); + } + err_mem_1: + return err; +} + +static int sa1100_irda_remove(struct device *_dev) +{ + struct net_device *dev = dev_get_drvdata(_dev); + + if (dev) { + struct sa1100_irda *si = dev->priv; + unregister_netdev(dev); + kfree(si->tx_buff.head); + kfree(si->rx_buff.head); + free_netdev(dev); + } + + release_mem_region(__PREG(Ser2HSCR2), 0x04); + release_mem_region(__PREG(Ser2HSCR0), 0x1c); + release_mem_region(__PREG(Ser2UTCR0), 0x24); + + return 0; +} + +static struct device_driver sa1100ir_driver = { + .name = "sa11x0-ir", + .bus = &platform_bus_type, + .probe = sa1100_irda_probe, + .remove = sa1100_irda_remove, + .suspend = sa1100_irda_suspend, + .resume = sa1100_irda_resume, +}; + +static int __init sa1100_irda_init(void) +{ + /* + * Limit power level a sensible range. + */ + if (power_level < 1) + power_level = 1; + if (power_level > 3) + power_level = 3; + + return driver_register(&sa1100ir_driver); +} + +static void __exit sa1100_irda_exit(void) +{ + driver_unregister(&sa1100ir_driver); +} + +module_init(sa1100_irda_init); +module_exit(sa1100_irda_exit); +module_param(power_level, int, 0); +module_param(tx_lpm, int, 0); +module_param(max_rate, int, 0); + +MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); +MODULE_DESCRIPTION("StrongARM SA1100 IrDA driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM_DESC(power_level, "IrDA power level, 1 (low) to 3 (high)"); +MODULE_PARM_DESC(tx_lpm, "Enable transmitter low power (1.6us) mode"); +MODULE_PARM_DESC(max_rate, "Maximum baud rate (4000000, 115200, 57600, 38400, 19200, 9600)"); diff --git a/drivers/net/irda/sir-dev.h b/drivers/net/irda/sir-dev.h new file mode 100644 index 000000000000..f0b8bc3637e5 --- /dev/null +++ b/drivers/net/irda/sir-dev.h @@ -0,0 +1,202 @@ +/********************************************************************* + * + * 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 <linux/netdevice.h> + +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> // 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); + +/* low level helpers for SIR device/dongle setup */ +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 int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts); + +/* not exported */ + +extern int sirdev_get_dongle(struct sir_dev *self, IRDA_DONGLE type); +extern int sirdev_put_dongle(struct sir_dev *self); + +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; + int raw_tx; + 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; + +}; + +#endif /* IRDA_SIR_H */ diff --git a/drivers/net/irda/sir_core.c b/drivers/net/irda/sir_core.c new file mode 100644 index 000000000000..a49f910c835b --- /dev/null +++ b/drivers/net/irda/sir_core.c @@ -0,0 +1,56 @@ +/********************************************************************* + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <net/irda/irda.h> + +#include "sir-dev.h" + +/***************************************************************************/ + +MODULE_AUTHOR("Martin Diehl <info@mdiehl.de>"); +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); + +EXPORT_SYMBOL(sirdev_raw_write); +EXPORT_SYMBOL(sirdev_raw_read); +EXPORT_SYMBOL(sirdev_set_dtr_rts); + +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 --git a/drivers/net/irda/sir_dev.c b/drivers/net/irda/sir_dev.c new file mode 100644 index 000000000000..efc5a8870565 --- /dev/null +++ b/drivers/net/irda/sir_dev.c @@ -0,0 +1,677 @@ +/********************************************************************* + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/smp_lock.h> +#include <linux/delay.h> + +#include <net/irda/irda.h> +#include <net/irda/wrapper.h> +#include <net/irda/irda_device.h> + +#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->rx_buff.len = 0; + dev->rx_buff.in_frame = FALSE; + dev->rx_buff.state = OUTSIDE_FRAME; + 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) +{ + unsigned long flags; + int ret; + + if (unlikely(len > dev->tx_buff.truesize)) + return -ENOSPC; + + spin_lock_irqsave(&dev->tx_lock, flags); /* serialize with other tx operations */ + while (dev->tx_buff.len > 0) { /* wait until tx idle */ + spin_unlock_irqrestore(&dev->tx_lock, flags); + msleep(10); + spin_lock_irqsave(&dev->tx_lock, flags); + } + + dev->tx_buff.data = dev->tx_buff.head; + memcpy(dev->tx_buff.data, buf, len); + dev->tx_buff.len = len; + + ret = dev->drv->do_write(dev, dev->tx_buff.data, dev->tx_buff.len); + if (ret > 0) { + IRDA_DEBUG(3, "%s(), raw-tx started\n", __FUNCTION__); + + dev->tx_buff.data += ret; + dev->tx_buff.len -= ret; + dev->raw_tx = 1; + ret = len; /* all data is going to be sent */ + } + spin_unlock_irqrestore(&dev->tx_lock, flags); + 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.data, count); + dev->rx_buff.data += count; + dev->rx_buff.len -= count; + } + + /* remaining stuff gets flushed when re-enabling normal rx */ + + return count; +} + +int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts) +{ + int ret = -ENXIO; + if (dev->drv->set_dtr_rts != 0) + ret = dev->drv->set_dtr_rts(dev, dtr, rts); + return ret; +} + +/**********************************************************************/ + +/* 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) +{ + unsigned long flags; + struct sk_buff *skb; + int actual = 0; + int err; + + spin_lock_irqsave(&dev->tx_lock, flags); + + 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 */ + IRDA_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) + goto done; /* more data to send later */ + } + + if (unlikely(dev->raw_tx != 0)) { + /* in raw mode we are just done now after the buffer was sent + * completely. Since this was requested by some dongle driver + * running under the control of the irda-thread we must take + * care here not to re-enable the queue. The queue will be + * restarted when the irda-thread has completed the request. + */ + + IRDA_DEBUG(3, "%s(), raw-tx done\n", __FUNCTION__); + dev->raw_tx = 0; + goto done; /* no post-frame handling in raw mode */ + } + + /* 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 + */ + IRDA_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); + } + +done: + spin_unlock_irqrestore(&dev->tx_lock, flags); +} + +/* 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_WARNING("%s(), not ready yet!\n", __FUNCTION__); + return -1; + } + + if (!dev->irlap) { + IRDA_WARNING("%s - too early: %p / %zd!\n", + __FUNCTION__, cp, count); + 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++; + IRDA_DEBUG(0, "%s; rx-drop: %zd\n", __FUNCTION__, count); + return 0; + } + + /* Read the characters into the buffer */ + if (likely(atomic_read(&dev->enable_rx))) { + while (count--) + /* Unwrap and destuff one byte */ + async_unwrap_char(dev->netdev, &dev->stats, + &dev->rx_buff, *cp++); + } else { + while (count--) { + /* 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; + unsigned long flags; + int actual = 0; + int err; + s32 speed; + + IRDA_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_irqsave(&dev->tx_lock, flags); + + /* 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 */ + IRDA_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_irqrestore(&dev->tx_lock, flags); + + 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; + + IRDA_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->tx_buff.truesize = SIRBUF_ALLOCSIZE; + dev->rx_buff.truesize = IRDA_SKB_MAX_MTU; + + /* Bootstrap ZeroCopy Rx */ + dev->rx_buff.skb = __dev_alloc_skb(dev->rx_buff.truesize, GFP_KERNEL); + if (dev->rx_buff.skb == NULL) + return -ENOMEM; + skb_reserve(dev->rx_buff.skb, 1); + dev->rx_buff.head = dev->rx_buff.skb->data; + + dev->tx_buff.head = kmalloc(dev->tx_buff.truesize, GFP_KERNEL); + if (dev->tx_buff.head == NULL) { + kfree_skb(dev->rx_buff.skb); + dev->rx_buff.skb = NULL; + dev->rx_buff.head = NULL; + return -ENOMEM; + } + + 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.skb) + kfree_skb(dev->rx_buff.skb); + if (dev->tx_buff.head) + kfree(dev->tx_buff.head); + dev->rx_buff.head = dev->tx_buff.head = NULL; + dev->rx_buff.skb = 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; + + /* increase the reference count of the driver module before doing serious stuff */ + if (!try_module_get(drv->owner)) + return -ESTALE; + + 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); + dev->raw_tx = 0; + + netif_start_queue(ndev); + dev->irlap = irlap_open(ndev, &dev->qos, dev->hwname); + if (!dev->irlap) + goto errout_stop; + + netif_wake_queue(ndev); + + IRDA_DEBUG(2, "%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: + module_put(drv->owner); + return -EAGAIN; +} + +static int sirdev_close(struct net_device *ndev) +{ + struct sir_dev *dev = ndev->priv; + const struct sir_driver *drv; + +// IRDA_DEBUG(0, "%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); + module_put(drv->owner); + +out: + dev->speed = 0; + up(&dev->fsm.sem); + return 0; +} + +/* ----------------------------------------------------------------------------- */ + +struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *name) +{ + struct net_device *ndev; + struct sir_dev *dev; + + IRDA_DEBUG(0, "%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 don't implement do_write. + */ + if (!drv || !drv->do_write) + return NULL; + + /* + * Allocate new instance of the device + */ + ndev = alloc_irdadev(sizeof(*dev)); + if (ndev == NULL) { + IRDA_ERROR("%s - Can't allocate memory for IrDA control block!\n", __FUNCTION__); + goto out; + } + dev = ndev->priv; + + 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); + + 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; + + SET_MODULE_OWNER(ndev); + + /* 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; + + if (register_netdev(ndev)) { + IRDA_ERROR("%s(), register_netdev() failed!\n", __FUNCTION__); + goto out_freenetdev; + } + + return dev; + +out_freenetdev: + free_netdev(ndev); +out: + return NULL; +} + +int sirdev_put_instance(struct sir_dev *dev) +{ + int err = 0; + + IRDA_DEBUG(0, "%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) + IRDA_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 */ + unregister_netdev(dev->netdev); + + free_netdev(dev->netdev); + + return 0; +} + diff --git a/drivers/net/irda/sir_dongle.c b/drivers/net/irda/sir_dongle.c new file mode 100644 index 000000000000..c5b76746e72b --- /dev/null +++ b/drivers/net/irda/sir_dongle.c @@ -0,0 +1,134 @@ +/********************************************************************* + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/smp_lock.h> +#include <linux/kmod.h> + +#include <net/irda/irda.h> + +#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 + request_module("irda-dongle-%d", type); +#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_module_get() below will fail + * 3) the try_module_get() below succeeds before the module is marked + * deleted - then sys_delete_module() fails and prevents the removal + * because the module is in use. + */ + + if (!try_module_get(drv->owner)) { + err = -ESTALE; + goto out_unlock; /* rmmod already pending */ + } + 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; + module_put(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 */ + module_put(drv->owner);/* decrement driver's module refcount */ + } + + return 0; +} diff --git a/drivers/net/irda/sir_kthread.c b/drivers/net/irda/sir_kthread.c new file mode 100644 index 000000000000..18cea1099530 --- /dev/null +++ b/drivers/net/irda/sir_kthread.c @@ -0,0 +1,502 @@ +/********************************************************************* + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/smp_lock.h> +#include <linux/completion.h> +#include <linux/delay.h> + +#include <net/irda/irda.h> + +#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_thread(void *startup) +{ + DECLARE_WAITQUEUE(wait, current); + + daemonize("kIrDAd"); + + irda_rq_queue.thread = current; + + complete((struct completion *)startup); + + while (irda_rq_queue.thread != NULL) { + + /* We use TASK_INTERRUPTIBLE, rather than + * TASK_UNINTERRUPTIBLE. Andrew Morton made this + * change ; he told me that it is safe, because "signal + * blocking is now handled in daemonize()", he added + * that the problem is that "uninterruptible sleep + * contributes to load average", making user worry. + * Jean II */ + set_task_state(current, TASK_INTERRUPTIBLE); + add_wait_queue(&irda_rq_queue.kick, &wait); + if (list_empty(&irda_rq_queue.request_list)) + schedule(); + else + __set_task_state(current, TASK_RUNNING); + remove_wait_queue(&irda_rq_queue.kick, &wait); + + /* make swsusp happy with our thread */ + if (current->flags & PF_FREEZE) + refrigerator(PF_FREEZE); + + 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: + IRDA_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: + ret = sirdev_set_dtr_rts(dev, + (fsm->param&0x02) ? TRUE : FALSE, + (fsm->param&0x01) ? TRUE : FALSE); + 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: + IRDA_ERROR("%s - undefined state\n", __FUNCTION__); + fsm->result = -EINVAL; + /* fall thru */ + + case SIRDEV_STATE_ERROR: + IRDA_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); + 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); + + if (down_trylock(&fsm->sem)) { + if (in_interrupt() || in_atomic() || irqs_disabled()) { + IRDA_DEBUG(1, "%s(), state machine busy!\n", __FUNCTION__); + return -EWOULDBLOCK; + } else + down(&fsm->sem); + } + + if (fsm->state == SIRDEV_STATE_DEAD) { + /* race with sirdev_close should never happen */ + IRDA_ERROR("%s(), instance staled!\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); + 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 --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c new file mode 100644 index 000000000000..10125a1dba22 --- /dev/null +++ b/drivers/net/irda/smsc-ircc2.c @@ -0,0 +1,2396 @@ +/********************************************************************* + * $Id: smsc-ircc2.c,v 1.19.2.5 2002/10/27 11:34:26 dip Exp $ + * + * Description: Driver for the SMC Infrared Communications Controller + * Status: Experimental. + * Author: Daniele Peri (peri@csai.unipa.it) + * Created at: + * Modified at: + * Modified by: + * + * Copyright (c) 2002 Daniele Peri + * All Rights Reserved. + * Copyright (c) 2002 Jean Tourrilhes + * + * + * Based on smc-ircc.c: + * + * Copyright (c) 2001 Stefani Seibold + * Copyright (c) 1999-2001 Dag Brattli + * Copyright (c) 1998-1999 Thomas Davis, + * + * and irport.c: + * + * Copyright (c) 1997, 1998, 1999-2000 Dag Brattli, All Rights Reserved. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/rtnetlink.h> +#include <linux/serial_reg.h> +#include <linux/dma-mapping.h> + +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/byteorder.h> + +#include <linux/spinlock.h> +#include <linux/pm.h> + +#include <net/irda/wrapper.h> +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> + +#include "smsc-ircc2.h" +#include "smsc-sio.h" + +/* Types */ + +struct smsc_transceiver { + char *name; + void (*set_for_speed)(int fir_base, u32 speed); + int (*probe)(int fir_base); +}; +typedef struct smsc_transceiver smsc_transceiver_t; + +#if 0 +struct smc_chip { + char *name; + u16 flags; + u8 devid; + u8 rev; +}; +typedef struct smc_chip smc_chip_t; +#endif + +struct smsc_chip { + char *name; + #if 0 + u8 type; + #endif + u16 flags; + u8 devid; + u8 rev; +}; +typedef struct smsc_chip smsc_chip_t; + +struct smsc_chip_address { + unsigned int cfg_base; + unsigned int type; +}; +typedef struct smsc_chip_address smsc_chip_address_t; + +/* Private data for each instance */ +struct smsc_ircc_cb { + struct net_device *netdev; /* Yes! we are some kind of netdevice */ + struct net_device_stats stats; + struct irlap_cb *irlap; /* The link layer we are binded to */ + + chipio_t io; /* IrDA controller information */ + iobuff_t tx_buff; /* Transmit buffer */ + iobuff_t rx_buff; /* Receive buffer */ + dma_addr_t tx_buff_dma; + dma_addr_t rx_buff_dma; + + 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 void smsc_ircc_setup_io(struct smsc_ircc_cb *self, unsigned int fir_base, unsigned int sir_base, u8 dma, u8 irq); +static void smsc_ircc_setup_qos(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 irqreturn_t smsc_ircc_interrupt_sir(struct net_device *dev); +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_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 */ + +static 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 + */ +static 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)) { + IRDA_MESSAGE(" Overriding FIR address 0x%04x\n", ircc_fir); + IRDA_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) { + IRDA_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; + struct net_device *dev; + int err; + + IRDA_DEBUG(1, "%s\n", __FUNCTION__); + + err = smsc_ircc_present(fir_base, sir_base); + if(err) + goto err_out; + + err = -ENOMEM; + if (dev_count > DIM(dev_self)) { + IRDA_WARNING("%s(), too many devices!\n", __FUNCTION__); + goto err_out1; + } + + /* + * Allocate new instance of the driver + */ + dev = alloc_irdadev(sizeof(struct smsc_ircc_cb)); + if (!dev) { + IRDA_WARNING("%s() can't allocate net device\n", __FUNCTION__); + goto err_out1; + } + + SET_MODULE_OWNER(dev); + + 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; + + self = dev->priv; + self->netdev = dev; + + /* Make ifconfig display some details */ + dev->base_addr = self->io.fir_base = fir_base; + dev->irq = self->io.irq = irq; + + /* Need to store self somewhere */ + dev_self[dev_count++] = self; + spin_lock_init(&self->lock); + + self->rx_buff.truesize = SMSC_IRCC2_RX_BUFF_TRUESIZE; + self->tx_buff.truesize = SMSC_IRCC2_TX_BUFF_TRUESIZE; + + self->rx_buff.head = + dma_alloc_coherent(NULL, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); + if (self->rx_buff.head == NULL) { + IRDA_ERROR("%s, Can't allocate memory for receive buffer!\n", + driver_name); + goto err_out2; + } + + self->tx_buff.head = + dma_alloc_coherent(NULL, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); + if (self->tx_buff.head == NULL) { + IRDA_ERROR("%s, Can't allocate memory for transmit buffer!\n", + driver_name); + goto err_out3; + } + + 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; + + smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq); + + smsc_ircc_setup_qos(self); + + 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 = register_netdev(self->netdev); + if(err) { + IRDA_ERROR("%s, Network device registration failed!\n", + driver_name); + goto err_out4; + } + + self->pmdev = pm_register(PM_SYS_DEV, PM_SYS_IRDA, smsc_ircc_pmproc); + if (self->pmdev) + self->pmdev->data = self; + + IRDA_MESSAGE("IrDA: Registered device %s\n", dev->name); + + return 0; + err_out4: + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + err_out3: + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + err_out2: + free_netdev(self->netdev); + dev_self[--dev_count] = NULL; + err_out1: + release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT); + release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT); + err_out: + return err; +} + +/* + * 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 (!request_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT, + driver_name)) { + IRDA_WARNING("%s: can't get fir_base of 0x%03x\n", + __FUNCTION__, fir_base); + goto out1; + } + + if (!request_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT, + driver_name)) { + IRDA_WARNING("%s: can't get sir_base of 0x%03x\n", + __FUNCTION__, sir_base); + goto out2; + } + + 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)) { + IRDA_WARNING("%s(), addr 0x%04x - no device found!\n", + __FUNCTION__, fir_base); + goto out3; + } + IRDA_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; + out3: + release_region(sir_base, SMSC_IRCC2_SIR_CHIP_IO_EXTENT); + out2: + release_region(fir_base, SMSC_IRCC2_FIR_CHIP_IO_EXTENT); + out1: + return -ENODEV; +} + +/* + * Function smsc_ircc_setup_io(self, fir_base, sir_base, dma, irq) + * + * Setup I/O + * + */ +static void 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; + + 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) + IRDA_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) + IRDA_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; + +} + +/* + * 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; + + IRDA_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_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; + + IRDA_ASSERT(dev != NULL, return -1;); + + self = dev->priv; + + IRDA_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; + + IRDA_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__); + + IRDA_ASSERT(dev != NULL, return 0;); + + self = (struct smsc_ircc_cb *) dev->priv; + IRDA_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; + + IRDA_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__); + + IRDA_ASSERT(self != NULL, return;); + dev = self->netdev; + IRDA_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__); + + IRDA_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); + + IRDA_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); + + IRDA_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; + + IRDA_ASSERT(dev != NULL, return 0;); + self = (struct smsc_ircc_cb *) dev->priv; + IRDA_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) */ + irda_setup_dma(self->io.dma, self->tx_buff_dma, 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 */ + irda_setup_dma(self->io.dma, self->rx_buff_dma, 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)) { + IRDA_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) { + IRDA_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; + + IRDA_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; + irqreturn_t ret = IRQ_NONE; + + if (dev == NULL) { + printk(KERN_WARNING "%s: irq %d for unknown device.\n", + driver_name, irq); + goto irq_ret; + } + self = (struct smsc_ircc_cb *) dev->priv; + IRDA_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) { + ret = smsc_ircc_interrupt_sir(dev); + goto irq_ret_unlock; + } + + iobase = self->io.fir_base; + + register_bank(iobase, 0); + iir = inb(iobase+IRCC_IIR); + if (iir == 0) + goto irq_ret_unlock; + ret = IRQ_HANDLED; + + /* 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 "%s(): Active Frame\n", __FUNCTION__);*/ + } + + /* Enable interrupts again */ + + register_bank(iobase, 0); + outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, iobase+IRCC_IER); + + irq_ret_unlock: + spin_unlock(&self->lock); + irq_ret: + return ret; +} + +/* + * Function irport_interrupt_sir (irq, dev_id, regs) + * + * Interrupt handler for SIR modes + */ +static irqreturn_t smsc_ircc_interrupt_sir(struct net_device *dev) +{ + struct smsc_ircc_cb *self = dev->priv; + int boguscount = 0; + int iobase; + int iir, lsr; + + /* 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; + if (iir == 0) + return IRQ_NONE; + 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);*/ + return IRQ_HANDLED; +} + + +#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__); + + IRDA_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 */ + + +/* + * 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__); + + IRDA_ASSERT(dev != NULL, return -1;); + self = (struct smsc_ircc_cb *) dev->priv; + IRDA_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); + + IRDA_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__); + + IRDA_ASSERT(dev != NULL, return -1;); + self = (struct smsc_ircc_cb *) dev->priv; + IRDA_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) +{ + IRDA_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); + + IRDA_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__); + + IRDA_ASSERT(self != NULL, return -1;); + + iobase = self->io.fir_base; + + if (self->pmdev) + pm_unregister(self->pmdev); + + /* Remove netdevice */ + unregister_netdev(self->netdev); + + /* 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) + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + + if (self->rx_buff.head) + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + + free_netdev(self->netdev); + + return 0; +} + +static 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__); + + IRDA_ASSERT(self != NULL, return;); + dev= self->netdev; + IRDA_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; + + IRDA_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)) { + IRDA_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; + + IRDA_ASSERT(self != NULL, return;); + + for(i=0; smsc_transceivers[i].name!=NULL; i++) + if((*smsc_transceivers[i].probe)(self->io.fir_base)) { + IRDA_MESSAGE(" %s transceiver found\n", + smsc_transceivers[i].name); + self->transceiver= i+1; + return; + } + IRDA_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 "%s(): probing: 0x%02x for: 0x%02x\n", __FUNCTION__, 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 "%s(): mode: 0x%02x\n", __FUNCTION__, mode);*/ + + if(!(mode & SMSCSIOFLAT_UART2MODE_VAL_IRDA)) + 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; + + IRDA_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; + } + + IRDA_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){ + IRDA_MESSAGE("Revision higher than expected\n"); + return NULL; + } + + if (chip->flags&NoIRDA) + IRDA_MESSAGE("chipset does not support IRDA\n"); + + return chip; +} + +static int __init smsc_superio_fdc(unsigned short cfg_base) +{ + int ret = -1; + + if (!request_region(cfg_base, 2, driver_name)) { + IRDA_WARNING("%s: can't get cfg_base of 0x%03x\n", + __FUNCTION__, cfg_base); + } else { + if (!smsc_superio_flat(fdc_chips_flat,cfg_base,"FDC") + ||!smsc_superio_paged(fdc_chips_paged,cfg_base,"FDC")) + ret = 0; + + release_region(cfg_base, 2); + } + + return ret; +} + +static int __init smsc_superio_lpc(unsigned short cfg_base) +{ + int ret = -1; + + if (!request_region(cfg_base, 2, driver_name)) { + IRDA_WARNING("%s: can't get cfg_base of 0x%03x\n", + __FUNCTION__, cfg_base); + } else { + if (!smsc_superio_flat(lpc_chips_flat,cfg_base,"LPC") + ||!smsc_superio_paged(lpc_chips_paged,cfg_base,"LPC")) + ret = 0; + release_region(cfg_base, 2); + } + return ret; +} + +/************************************************ + * + * 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) + IRDA_WARNING("%s(): ATC: 0x%02x\n", __FUNCTION__, + inb(fir_base+IRCC_ATC)); +} + +/* + * Function smsc_ircc_probe_transceiver_smsc_ircc_atc(fir_base) + * + * Probe transceiver smsc-ircc ATC circuitry + * + */ + +static int smsc_ircc_probe_transceiver_smsc_ircc_atc(int fir_base) +{ + return 0; +} + +/* + * Function smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(self, speed) + * + * Set transceiver + * + */ + +static void smsc_ircc_set_transceiver_smsc_ircc_fast_pin_select(int fir_base, u32 speed) +{ + u8 fast_mode; + + switch(speed) + { + default: + case 576000 : + fast_mode = 0; + break; + case 1152000 : + case 4000000 : + fast_mode = IRCC_LCR_A_FAST; + break; + + } + register_bank(fir_base, 0); + outb((inb(fir_base+IRCC_LCR_A) & 0xbf) | fast_mode, fir_base+IRCC_LCR_A); +} + +/* + * Function smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(fir_base) + * + * Probe transceiver + * + */ + +static int smsc_ircc_probe_transceiver_smsc_ircc_fast_pin_select(int fir_base) +{ + return 0; +} + +/* + * Function smsc_ircc_set_transceiver_toshiba_sat1800(fir_base, speed) + * + * Set transceiver + * + */ + +static void smsc_ircc_set_transceiver_toshiba_sat1800(int fir_base, u32 speed) +{ + u8 fast_mode; + + switch(speed) + { + default: + case 576000 : + fast_mode = 0; + break; + case 1152000 : + case 4000000 : + fast_mode = /*IRCC_LCR_A_FAST |*/ IRCC_LCR_A_GP_DATA; + break; + + } + /* This causes an interrupt */ + register_bank(fir_base, 0); + outb((inb(fir_base+IRCC_LCR_A) & 0xbf) | fast_mode, fir_base+IRCC_LCR_A); +} + +/* + * Function smsc_ircc_probe_transceiver_toshiba_sat1800(fir_base) + * + * Probe transceiver + * + */ + +static int smsc_ircc_probe_transceiver_toshiba_sat1800(int fir_base) +{ + return 0; +} + + +module_init(smsc_ircc_init); +module_exit(smsc_ircc_cleanup); + +MODULE_AUTHOR("Daniele Peri <peri@csai.unipa.it>"); +MODULE_DESCRIPTION("SMC IrCC SIR/FIR controller driver"); +MODULE_LICENSE("GPL"); + +module_param(ircc_dma, int, 0); +MODULE_PARM_DESC(ircc_dma, "DMA channel"); +module_param(ircc_irq, int, 0); +MODULE_PARM_DESC(ircc_irq, "IRQ line"); +module_param(ircc_fir, int, 0); +MODULE_PARM_DESC(ircc_fir, "FIR Base Address"); +module_param(ircc_sir, int, 0); +MODULE_PARM_DESC(ircc_sir, "SIR Base Address"); +module_param(ircc_cfg, int, 0); +MODULE_PARM_DESC(ircc_cfg, "Configuration register base address"); +module_param(ircc_transceiver, int, 0); +MODULE_PARM_DESC(ircc_transceiver, "Transceiver type"); diff --git a/drivers/net/irda/smsc-ircc2.h b/drivers/net/irda/smsc-ircc2.h new file mode 100644 index 000000000000..458611cc0d40 --- /dev/null +++ b/drivers/net/irda/smsc-ircc2.h @@ -0,0 +1,194 @@ +/********************************************************************* + * $Id: smsc-ircc2.h,v 1.12.2.1 2002/10/27 10:52:37 dip Exp $ + * + * Description: Definitions for the SMC IrCC chipset + * Status: Experimental. + * Author: Daniele Peri (peri@csai.unipa.it) + * + * Copyright (c) 2002 Daniele Peri + * All Rights Reserved. + * + * Based on smc-ircc.h: + * + * Copyright (c) 1999-2000, Dag Brattli <dagb@cs.uit.no> + * Copyright (c) 1998-1999, Thomas Davis (tadavis@jps.net> + * All Rights Reserved + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ********************************************************************/ + +#ifndef SMSC_IRCC2_H +#define SMSC_IRCC2_H + +/* DMA modes needed */ +#define DMA_TX_MODE 0x08 /* Mem to I/O, ++, demand. */ +#define DMA_RX_MODE 0x04 /* I/O to mem, ++, demand. */ + +/* Master Control Register */ +#define IRCC_MASTER 0x07 +#define IRCC_MASTER_POWERDOWN 0x80 +#define IRCC_MASTER_RESET 0x40 +#define IRCC_MASTER_INT_EN 0x20 +#define IRCC_MASTER_ERROR_RESET 0x10 + +/* Register block 0 */ + +/* Interrupt Identification */ +#define IRCC_IIR 0x01 +#define IRCC_IIR_ACTIVE_FRAME 0x80 +#define IRCC_IIR_EOM 0x40 +#define IRCC_IIR_RAW_MODE 0x20 +#define IRCC_IIR_FIFO 0x10 + +/* Interrupt Enable */ +#define IRCC_IER 0x02 +#define IRCC_IER_ACTIVE_FRAME 0x80 +#define IRCC_IER_EOM 0x40 +#define IRCC_IER_RAW_MODE 0x20 +#define IRCC_IER_FIFO 0x10 + +/* Line Status Register */ +#define IRCC_LSR 0x03 +#define IRCC_LSR_UNDERRUN 0x80 +#define IRCC_LSR_OVERRUN 0x40 +#define IRCC_LSR_FRAME_ERROR 0x20 +#define IRCC_LSR_SIZE_ERROR 0x10 +#define IRCC_LSR_CRC_ERROR 0x80 +#define IRCC_LSR_FRAME_ABORT 0x40 + +/* Line Status Address Register */ +#define IRCC_LSAR 0x03 +#define IRCC_LSAR_ADDRESS_MASK 0x07 + +/* Line Control Register A */ +#define IRCC_LCR_A 0x04 +#define IRCC_LCR_A_FIFO_RESET 0x80 +#define IRCC_LCR_A_FAST 0x40 +#define IRCC_LCR_A_GP_DATA 0x20 +#define IRCC_LCR_A_RAW_TX 0x10 +#define IRCC_LCR_A_RAW_RX 0x08 +#define IRCC_LCR_A_ABORT 0x04 +#define IRCC_LCR_A_DATA_DONE 0x02 + +/* Line Control Register B */ +#define IRCC_LCR_B 0x05 +#define IRCC_LCR_B_SCE_DISABLED 0x00 +#define IRCC_LCR_B_SCE_TRANSMIT 0x40 +#define IRCC_LCR_B_SCE_RECEIVE 0x80 +#define IRCC_LCR_B_SCE_UNDEFINED 0xc0 +#define IRCC_LCR_B_SIP_ENABLE 0x20 +#define IRCC_LCR_B_BRICK_WALL 0x10 + +/* Bus Status Register */ +#define IRCC_BSR 0x06 +#define IRCC_BSR_NOT_EMPTY 0x80 +#define IRCC_BSR_FIFO_FULL 0x40 +#define IRCC_BSR_TIMEOUT 0x20 + +/* Register block 1 */ + +#define IRCC_FIFO_THRESHOLD 0x02 + +#define IRCC_SCE_CFGA 0x00 +#define IRCC_CFGA_AUX_IR 0x80 +#define IRCC_CFGA_HALF_DUPLEX 0x04 +#define IRCC_CFGA_TX_POLARITY 0x02 +#define IRCC_CFGA_RX_POLARITY 0x01 + +#define IRCC_CFGA_COM 0x00 +#define IRCC_SCE_CFGA_BLOCK_CTRL_BITS_MASK 0x87 +#define IRCC_CFGA_IRDA_SIR_A 0x08 +#define IRCC_CFGA_ASK_SIR 0x10 +#define IRCC_CFGA_IRDA_SIR_B 0x18 +#define IRCC_CFGA_IRDA_HDLC 0x20 +#define IRCC_CFGA_IRDA_4PPM 0x28 +#define IRCC_CFGA_CONSUMER 0x30 +#define IRCC_CFGA_RAW_IR 0x38 +#define IRCC_CFGA_OTHER 0x40 + +#define IRCC_IR_HDLC 0x04 +#define IRCC_IR_4PPM 0x01 +#define IRCC_IR_CONSUMER 0x02 + +#define IRCC_SCE_CFGB 0x01 +#define IRCC_CFGB_LOOPBACK 0x20 +#define IRCC_CFGB_LPBCK_TX_CRC 0x10 +#define IRCC_CFGB_NOWAIT 0x08 +#define IRCC_CFGB_STRING_MOVE 0x04 +#define IRCC_CFGB_DMA_BURST 0x02 +#define IRCC_CFGB_DMA_ENABLE 0x01 + +#define IRCC_CFGB_MUX_COM 0x00 +#define IRCC_CFGB_MUX_IR 0x40 +#define IRCC_CFGB_MUX_AUX 0x80 +#define IRCC_CFGB_MUX_INACTIVE 0xc0 + +/* Register block 3 - Identification Registers! */ +#define IRCC_ID_HIGH 0x00 /* 0x10 */ +#define IRCC_ID_LOW 0x01 /* 0xB8 */ +#define IRCC_CHIP_ID 0x02 /* 0xF1 */ +#define IRCC_VERSION 0x03 /* 0x01 */ +#define IRCC_INTERFACE 0x04 /* low 4 = DMA, high 4 = IRQ */ +#define IRCC_INTERFACE_DMA_MASK 0x0F /* low 4 = DMA, high 4 = IRQ */ +#define IRCC_INTERFACE_IRQ_MASK 0xF0 /* low 4 = DMA, high 4 = IRQ */ + +/* Register block 4 - IrDA */ +#define IRCC_CONTROL 0x00 +#define IRCC_BOF_COUNT_LO 0x01 /* Low byte */ +#define IRCC_BOF_COUNT_HI 0x00 /* High nibble (bit 0-3) */ +#define IRCC_BRICKWALL_CNT_LO 0x02 /* Low byte */ +#define IRCC_BRICKWALL_CNT_HI 0x03 /* High nibble (bit 4-7) */ +#define IRCC_TX_SIZE_LO 0x04 /* Low byte */ +#define IRCC_TX_SIZE_HI 0x03 /* High nibble (bit 0-3) */ +#define IRCC_RX_SIZE_HI 0x05 /* High nibble (bit 0-3) */ +#define IRCC_RX_SIZE_LO 0x06 /* Low byte */ + +#define IRCC_1152 0x80 +#define IRCC_CRC 0x40 + +/* Register block 5 - IrDA */ +#define IRCC_ATC 0x00 +#define IRCC_ATC_nPROGREADY 0x80 +#define IRCC_ATC_SPEED 0x40 +#define IRCC_ATC_ENABLE 0x20 +#define IRCC_ATC_MASK 0xE0 + + +#define IRCC_IRHALFDUPLEX_TIMEOUT 0x01 + +#define IRCC_SCE_TX_DELAY_TIMER 0x02 + +/* + * Other definitions + */ + +#define SMSC_IRCC2_MAX_SIR_SPEED 115200 +#define SMSC_IRCC2_FIR_CHIP_IO_EXTENT 8 +#define SMSC_IRCC2_SIR_CHIP_IO_EXTENT 8 +#define SMSC_IRCC2_FIFO_SIZE 16 +#define SMSC_IRCC2_FIFO_THRESHOLD 64 +/* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ +#define SMSC_IRCC2_RX_BUFF_TRUESIZE 14384 +#define SMSC_IRCC2_TX_BUFF_TRUESIZE 14384 +#define SMSC_IRCC2_MIN_TURN_TIME 0x07 +#define SMSC_IRCC2_WINDOW_SIZE 0x07 +/* Maximum wait for hw transmitter to finish */ +#define SMSC_IRCC2_HW_TRANSMITTER_TIMEOUT_US 1000 /* 1 ms */ +/* Maximum wait for ATC transceiver programming to finish */ +#define SMSC_IRCC2_ATC_PROGRAMMING_TIMEOUT_JIFFIES 1 +#endif /* SMSC_IRCC2_H */ diff --git a/drivers/net/irda/smsc-sio.h b/drivers/net/irda/smsc-sio.h new file mode 100644 index 000000000000..59e20e653ebe --- /dev/null +++ b/drivers/net/irda/smsc-sio.h @@ -0,0 +1,100 @@ +#ifndef SMSC_SIO_H +#define SMSC_SIO_H + +/****************************************** + Keys. They should work with every SMsC SIO + ******************************************/ + +#define SMSCSIO_CFGACCESSKEY 0x55 +#define SMSCSIO_CFGEXITKEY 0xaa + +/***************************** + * Generic SIO Flat (!?) * + *****************************/ + +/* Register 0x0d */ +#define SMSCSIOFLAT_DEVICEID_REG 0x0d + +/* Register 0x0c */ +#define SMSCSIOFLAT_UARTMODE0C_REG 0x0c +#define SMSCSIOFLAT_UART2MODE_MASK 0x38 +#define SMSCSIOFLAT_UART2MODE_VAL_COM 0x00 +#define SMSCSIOFLAT_UART2MODE_VAL_IRDA 0x08 +#define SMSCSIOFLAT_UART2MODE_VAL_ASKIR 0x10 + +/* Register 0x25 */ +#define SMSCSIOFLAT_UART2BASEADDR_REG 0x25 + +/* Register 0x2b */ +#define SMSCSIOFLAT_FIRBASEADDR_REG 0x2b + +/* Register 0x2c */ +#define SMSCSIOFLAT_FIRDMASELECT_REG 0x2c +#define SMSCSIOFLAT_FIRDMASELECT_MASK 0x0f + +/* Register 0x28 */ +#define SMSCSIOFLAT_UARTIRQSELECT_REG 0x28 +#define SMSCSIOFLAT_UART2IRQSELECT_MASK 0x0f +#define SMSCSIOFLAT_UART1IRQSELECT_MASK 0xf0 +#define SMSCSIOFLAT_UARTIRQSELECT_VAL_NONE 0x00 + + +/********************* + * LPC47N227 * + *********************/ + +#define LPC47N227_CFGACCESSKEY 0x55 +#define LPC47N227_CFGEXITKEY 0xaa + +/* Register 0x00 */ +#define LPC47N227_FDCPOWERVALIDCONF_REG 0x00 +#define LPC47N227_FDCPOWER_MASK 0x08 +#define LPC47N227_VALID_MASK 0x80 + +/* Register 0x02 */ +#define LPC47N227_UART12POWER_REG 0x02 +#define LPC47N227_UART1POWERDOWN_MASK 0x08 +#define LPC47N227_UART2POWERDOWN_MASK 0x80 + +/* Register 0x07 */ +#define LPC47N227_APMBOOTDRIVE_REG 0x07 +#define LPC47N227_PARPORT2AUTOPWRDOWN_MASK 0x10 /* auto power down on if set */ +#define LPC47N227_UART2AUTOPWRDOWN_MASK 0x20 /* auto power down on if set */ +#define LPC47N227_UART1AUTOPWRDOWN_MASK 0x40 /* auto power down on if set */ + +/* Register 0x0c */ +#define LPC47N227_UARTMODE0C_REG 0x0c +#define LPC47N227_UART2MODE_MASK 0x38 +#define LPC47N227_UART2MODE_VAL_COM 0x00 +#define LPC47N227_UART2MODE_VAL_IRDA 0x08 +#define LPC47N227_UART2MODE_VAL_ASKIR 0x10 + +/* Register 0x0d */ +#define LPC47N227_DEVICEID_REG 0x0d +#define LPC47N227_DEVICEID_DEFVAL 0x5a + +/* Register 0x0e */ +#define LPC47N227_REVISIONID_REG 0x0e + +/* Register 0x25 */ +#define LPC47N227_UART2BASEADDR_REG 0x25 + +/* Register 0x28 */ +#define LPC47N227_UARTIRQSELECT_REG 0x28 +#define LPC47N227_UART2IRQSELECT_MASK 0x0f +#define LPC47N227_UART1IRQSELECT_MASK 0xf0 +#define LPC47N227_UARTIRQSELECT_VAL_NONE 0x00 + +/* Register 0x2b */ +#define LPC47N227_FIRBASEADDR_REG 0x2b + +/* Register 0x2c */ +#define LPC47N227_FIRDMASELECT_REG 0x2c +#define LPC47N227_FIRDMASELECT_MASK 0x0f +#define LPC47N227_FIRDMASELECT_VAL_DMA1 0x01 /* 47n227 has three dma channels */ +#define LPC47N227_FIRDMASELECT_VAL_DMA2 0x02 +#define LPC47N227_FIRDMASELECT_VAL_DMA3 0x03 +#define LPC47N227_FIRDMASELECT_VAL_NONE 0x0f + + +#endif diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c new file mode 100644 index 000000000000..83c605e8824c --- /dev/null +++ b/drivers/net/irda/stir4200.c @@ -0,0 +1,1184 @@ +/***************************************************************************** +* +* Filename: stir4200.c +* Version: 0.4 +* Description: Irda SigmaTel USB Dongle +* Status: Experimental +* Author: Stephen Hemminger <shemminger@osdl.org> +* +* Based on earlier driver by Paul Stewart <stewart@parc.com> +* +* Copyright (C) 2000, Roman Weissgaerber <weissg@vienna.at> +* Copyright (C) 2001, Dag Brattli <dag@brattli.net> +* Copyright (C) 2001, Jean Tourrilhes <jt@hpl.hp.com> +* Copyright (C) 2004, Stephen Hemminger <shemminger@osdl.org> +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + +/* + * This dongle does no framing, and requires polling to receive the + * data. The STIr4200 has bulk in and out endpoints just like + * usr-irda devices, but the data it sends and receives is raw; like + * irtty, it needs to call the wrap and unwrap functions to add and + * remove SOF/BOF and escape characters to/from the frame. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/time.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/usb.h> +#include <linux/crc32.h> +#include <net/irda/irda.h> +#include <net/irda/irlap.h> +#include <net/irda/irda_device.h> +#include <net/irda/wrapper.h> +#include <net/irda/crc.h> +#include <asm/byteorder.h> +#include <asm/unaligned.h> + +MODULE_AUTHOR("Stephen Hemminger <shemminger@osdl.org>"); +MODULE_DESCRIPTION("IrDA-USB Dongle Driver for SigmaTel STIr4200"); +MODULE_LICENSE("GPL"); + +static int qos_mtt_bits = 0x07; /* 1 ms or more */ +module_param(qos_mtt_bits, int, 0); +MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); + +static int rx_sensitivity = 1; /* FIR 0..4, SIR 0..6 */ +module_param(rx_sensitivity, int, 0); +MODULE_PARM_DESC(rx_sensitivity, "Set Receiver sensitivity (0-6, 0 is most sensitive)"); + +static int tx_power = 0; /* 0 = highest ... 3 = lowest */ +module_param(tx_power, int, 0); +MODULE_PARM_DESC(tx_power, "Set Transmitter power (0-3, 0 is highest power)"); + +#define STIR_IRDA_HEADER 4 +#define CTRL_TIMEOUT 100 /* milliseconds */ +#define TRANSMIT_TIMEOUT 200 /* milliseconds */ +#define STIR_FIFO_SIZE 4096 +#define FIFO_REGS_SIZE 3 + +enum FirChars { + FIR_CE = 0x7d, + FIR_XBOF = 0x7f, + FIR_EOF = 0x7e, +}; + +enum StirRequests { + REQ_WRITE_REG = 0x00, + REQ_READ_REG = 0x01, + REQ_READ_ROM = 0x02, + REQ_WRITE_SINGLE = 0x03, +}; + +/* Register offsets */ +enum StirRegs { + REG_RSVD=0, + REG_MODE, + REG_PDCLK, + REG_CTRL1, + REG_CTRL2, + REG_FIFOCTL, + REG_FIFOLSB, + REG_FIFOMSB, + REG_DPLL, + REG_IRDIG, + REG_TEST=15, +}; + +enum StirModeMask { + MODE_FIR = 0x80, + MODE_SIR = 0x20, + MODE_ASK = 0x10, + MODE_FASTRX = 0x08, + MODE_FFRSTEN = 0x04, + MODE_NRESET = 0x02, + MODE_2400 = 0x01, +}; + +enum StirPdclkMask { + PDCLK_4000000 = 0x02, + PDCLK_115200 = 0x09, + PDCLK_57600 = 0x13, + PDCLK_38400 = 0x1D, + PDCLK_19200 = 0x3B, + PDCLK_9600 = 0x77, + PDCLK_2400 = 0xDF, +}; + +enum StirCtrl1Mask { + CTRL1_SDMODE = 0x80, + CTRL1_RXSLOW = 0x40, + CTRL1_TXPWD = 0x10, + CTRL1_RXPWD = 0x08, + CTRL1_SRESET = 0x01, +}; + +enum StirCtrl2Mask { + CTRL2_SPWIDTH = 0x08, + CTRL2_REVID = 0x03, +}; + +enum StirFifoCtlMask { + FIFOCTL_EOF = 0x80, + FIFOCTL_UNDER = 0x40, + FIFOCTL_OVER = 0x20, + FIFOCTL_DIR = 0x10, + FIFOCTL_CLR = 0x08, + FIFOCTL_EMPTY = 0x04, + FIFOCTL_RXERR = 0x02, + FIFOCTL_TXERR = 0x01, +}; + +enum StirDiagMask { + IRDIG_RXHIGH = 0x80, + IRDIG_RXLOW = 0x40, +}; + +enum StirTestMask { + TEST_PLLDOWN = 0x80, + TEST_LOOPIR = 0x40, + TEST_LOOPUSB = 0x20, + TEST_TSTENA = 0x10, + TEST_TSTOSC = 0x0F, +}; + +struct stir_cb { + struct usb_device *usbdev; /* init: probe_irda */ + struct net_device *netdev; /* network layer */ + struct irlap_cb *irlap; /* The link layer we are binded to */ + struct net_device_stats stats; /* network statistics */ + struct qos_info qos; + unsigned speed; /* Current speed */ + + wait_queue_head_t thr_wait; /* transmit thread wakeup */ + struct completion thr_exited; + pid_t thr_pid; + + struct sk_buff *tx_pending; + void *io_buf; /* transmit/receive buffer */ + __u8 *fifo_status; + + iobuff_t rx_buff; /* receive unwrap state machine */ + struct timeval rx_time; + int receiving; + struct urb *rx_urb; +}; + + +/* These are the currently known USB ids */ +static struct usb_device_id dongles[] = { + /* SigmaTel, Inc, STIr4200 IrDA/USB Bridge */ + { USB_DEVICE(0x066f, 0x4200) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, dongles); + +/* Send control message to set dongle register */ +static int write_reg(struct stir_cb *stir, __u16 reg, __u8 value) +{ + struct usb_device *dev = stir->usbdev; + + pr_debug("%s: write reg %d = 0x%x\n", + stir->netdev->name, reg, value); + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + REQ_WRITE_SINGLE, + USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE, + value, reg, NULL, 0, + CTRL_TIMEOUT); +} + +/* Send control message to read multiple registers */ +static inline int read_reg(struct stir_cb *stir, __u16 reg, + __u8 *data, __u16 count) +{ + struct usb_device *dev = stir->usbdev; + + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + REQ_READ_REG, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, reg, data, count, + CTRL_TIMEOUT); +} + +static inline int isfir(u32 speed) +{ + return (speed == 4000000); +} + +/* + * Prepare a FIR IrDA frame for transmission to the USB dongle. The + * FIR transmit frame is documented in the datasheet. It consists of + * a two byte 0x55 0xAA sequence, two little-endian length bytes, a + * sequence of exactly 16 XBOF bytes of 0x7E, two BOF bytes of 0x7E, + * then the data escaped as follows: + * + * 0x7D -> 0x7D 0x5D + * 0x7E -> 0x7D 0x5E + * 0x7F -> 0x7D 0x5F + * + * Then, 4 bytes of little endian (stuffed) FCS follow, then two + * trailing EOF bytes of 0x7E. + */ +static inline __u8 *stuff_fir(__u8 *p, __u8 c) +{ + switch(c) { + case 0x7d: + case 0x7e: + case 0x7f: + *p++ = 0x7d; + c ^= IRDA_TRANS; + /* fall through */ + default: + *p++ = c; + } + return p; +} + +/* Take raw data in skb and put it wrapped into buf */ +static unsigned wrap_fir_skb(const struct sk_buff *skb, __u8 *buf) +{ + __u8 *ptr = buf; + __u32 fcs = ~(crc32_le(~0, skb->data, skb->len)); + __u16 wraplen; + int i; + + /* Header */ + buf[0] = 0x55; + buf[1] = 0xAA; + + ptr = buf + STIR_IRDA_HEADER; + memset(ptr, 0x7f, 16); + ptr += 16; + + /* BOF */ + *ptr++ = 0x7e; + *ptr++ = 0x7e; + + /* Address / Control / Information */ + for (i = 0; i < skb->len; i++) + ptr = stuff_fir(ptr, skb->data[i]); + + /* FCS */ + ptr = stuff_fir(ptr, fcs & 0xff); + ptr = stuff_fir(ptr, (fcs >> 8) & 0xff); + ptr = stuff_fir(ptr, (fcs >> 16) & 0xff); + ptr = stuff_fir(ptr, (fcs >> 24) & 0xff); + + /* EOFs */ + *ptr++ = 0x7e; + *ptr++ = 0x7e; + + /* Total length, minus the header */ + wraplen = (ptr - buf) - STIR_IRDA_HEADER; + buf[2] = wraplen & 0xff; + buf[3] = (wraplen >> 8) & 0xff; + + return wraplen + STIR_IRDA_HEADER; +} + +static unsigned wrap_sir_skb(struct sk_buff *skb, __u8 *buf) +{ + __u16 wraplen; + + wraplen = async_wrap_skb(skb, buf + STIR_IRDA_HEADER, + STIR_FIFO_SIZE - STIR_IRDA_HEADER); + buf[0] = 0x55; + buf[1] = 0xAA; + buf[2] = wraplen & 0xff; + buf[3] = (wraplen >> 8) & 0xff; + + return wraplen + STIR_IRDA_HEADER; +} + +/* + * Frame is fully formed in the rx_buff so check crc + * and pass up to irlap + * setup for next receive + */ +static void fir_eof(struct stir_cb *stir) +{ + iobuff_t *rx_buff = &stir->rx_buff; + int len = rx_buff->len - 4; + struct sk_buff *skb, *nskb; + __u32 fcs; + + if (unlikely(len <= 0)) { + pr_debug("%s: short frame len %d\n", + stir->netdev->name, len); + + ++stir->stats.rx_errors; + ++stir->stats.rx_length_errors; + return; + } + + fcs = ~(crc32_le(~0, rx_buff->data, len)); + if (fcs != le32_to_cpu(get_unaligned((u32 *)(rx_buff->data+len)))) { + pr_debug("crc error calc 0x%x len %d\n", fcs, len); + stir->stats.rx_errors++; + stir->stats.rx_crc_errors++; + return; + } + + /* if frame is short then just copy it */ + if (len < IRDA_RX_COPY_THRESHOLD) { + nskb = dev_alloc_skb(len + 1); + if (unlikely(!nskb)) { + ++stir->stats.rx_dropped; + return; + } + skb_reserve(nskb, 1); + skb = nskb; + memcpy(nskb->data, rx_buff->data, len); + } else { + nskb = dev_alloc_skb(rx_buff->truesize); + if (unlikely(!nskb)) { + ++stir->stats.rx_dropped; + return; + } + skb_reserve(nskb, 1); + skb = rx_buff->skb; + rx_buff->skb = nskb; + rx_buff->head = nskb->data; + } + + skb_put(skb, len); + + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + skb->dev = stir->netdev; + + netif_rx(skb); + + stir->stats.rx_packets++; + stir->stats.rx_bytes += len; + + rx_buff->data = rx_buff->head; + rx_buff->len = 0; +} + +/* Unwrap FIR stuffed data and bump it to IrLAP */ +static void stir_fir_chars(struct stir_cb *stir, + const __u8 *bytes, int len) +{ + iobuff_t *rx_buff = &stir->rx_buff; + int i; + + for (i = 0; i < len; i++) { + __u8 byte = bytes[i]; + + switch(rx_buff->state) { + case OUTSIDE_FRAME: + /* ignore garbage till start of frame */ + if (unlikely(byte != FIR_EOF)) + continue; + /* Now receiving frame */ + rx_buff->state = BEGIN_FRAME; + + /* Time to initialize receive buffer */ + rx_buff->data = rx_buff->head; + rx_buff->len = 0; + continue; + + case LINK_ESCAPE: + if (byte == FIR_EOF) { + pr_debug("%s: got EOF after escape\n", + stir->netdev->name); + goto frame_error; + } + rx_buff->state = INSIDE_FRAME; + byte ^= IRDA_TRANS; + break; + + case BEGIN_FRAME: + /* ignore multiple BOF/EOF */ + if (byte == FIR_EOF) + continue; + rx_buff->state = INSIDE_FRAME; + rx_buff->in_frame = TRUE; + + /* fall through */ + case INSIDE_FRAME: + switch(byte) { + case FIR_CE: + rx_buff->state = LINK_ESCAPE; + continue; + case FIR_XBOF: + /* 0x7f is not used in this framing */ + pr_debug("%s: got XBOF without escape\n", + stir->netdev->name); + goto frame_error; + case FIR_EOF: + rx_buff->state = OUTSIDE_FRAME; + rx_buff->in_frame = FALSE; + fir_eof(stir); + continue; + } + break; + } + + /* add byte to rx buffer */ + if (unlikely(rx_buff->len >= rx_buff->truesize)) { + pr_debug("%s: fir frame exceeds %d\n", + stir->netdev->name, rx_buff->truesize); + ++stir->stats.rx_over_errors; + goto error_recovery; + } + + rx_buff->data[rx_buff->len++] = byte; + continue; + + frame_error: + ++stir->stats.rx_frame_errors; + + error_recovery: + ++stir->stats.rx_errors; + rx_buff->state = OUTSIDE_FRAME; + rx_buff->in_frame = FALSE; + } +} + +/* Unwrap SIR stuffed data and bump it up to IrLAP */ +static void stir_sir_chars(struct stir_cb *stir, + const __u8 *bytes, int len) +{ + int i; + + for (i = 0; i < len; i++) + async_unwrap_char(stir->netdev, &stir->stats, + &stir->rx_buff, bytes[i]); +} + +static inline void unwrap_chars(struct stir_cb *stir, + const __u8 *bytes, int length) +{ + if (isfir(stir->speed)) + stir_fir_chars(stir, bytes, length); + else + stir_sir_chars(stir, bytes, length); +} + +/* Mode parameters for each speed */ +static const struct { + unsigned speed; + __u8 pdclk; +} stir_modes[] = { + { 2400, PDCLK_2400 }, + { 9600, PDCLK_9600 }, + { 19200, PDCLK_19200 }, + { 38400, PDCLK_38400 }, + { 57600, PDCLK_57600 }, + { 115200, PDCLK_115200 }, + { 4000000, PDCLK_4000000 }, +}; + + +/* + * Setup chip for speed. + * Called at startup to initialize the chip + * and on speed changes. + * + * Note: Write multiple registers doesn't appear to work + */ +static int change_speed(struct stir_cb *stir, unsigned speed) +{ + int i, err; + __u8 mode; + + for (i = 0; i < ARRAY_SIZE(stir_modes); ++i) { + if (speed == stir_modes[i].speed) + goto found; + } + + warn("%s: invalid speed %d", stir->netdev->name, speed); + return -EINVAL; + + found: + pr_debug("speed change from %d to %d\n", stir->speed, speed); + + /* Reset modulator */ + err = write_reg(stir, REG_CTRL1, CTRL1_SRESET); + if (err) + goto out; + + /* Undocumented magic to tweak the DPLL */ + err = write_reg(stir, REG_DPLL, 0x15); + if (err) + goto out; + + /* Set clock */ + err = write_reg(stir, REG_PDCLK, stir_modes[i].pdclk); + if (err) + goto out; + + mode = MODE_NRESET | MODE_FASTRX; + if (isfir(speed)) + mode |= MODE_FIR | MODE_FFRSTEN; + else + mode |= MODE_SIR; + + if (speed == 2400) + mode |= MODE_2400; + + err = write_reg(stir, REG_MODE, mode); + if (err) + goto out; + + /* This resets TEMIC style transceiver if any. */ + err = write_reg(stir, REG_CTRL1, + CTRL1_SDMODE | (tx_power & 3) << 1); + if (err) + goto out; + + err = write_reg(stir, REG_CTRL1, (tx_power & 3) << 1); + if (err) + goto out; + + /* Reset sensitivity */ + err = write_reg(stir, REG_CTRL2, (rx_sensitivity & 7) << 5); + out: + stir->speed = speed; + return err; +} + +/* + * Called from net/core when new frame is available. + */ +static int stir_hard_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct stir_cb *stir = netdev_priv(netdev); + + netif_stop_queue(netdev); + + /* the IRDA wrapping routines don't deal with non linear skb */ + SKB_LINEAR_ASSERT(skb); + + skb = xchg(&stir->tx_pending, skb); + wake_up(&stir->thr_wait); + + /* this should never happen unless stop/wakeup problem */ + if (unlikely(skb)) { + WARN_ON(1); + dev_kfree_skb(skb); + } + + return 0; +} + +/* + * Wait for the transmit FIFO to have space for next data + * + * If space < 0 then wait till FIFO completely drains. + * FYI: can take up to 13 seconds at 2400baud. + */ +static int fifo_txwait(struct stir_cb *stir, int space) +{ + int err; + unsigned long count, status; + + /* Read FIFO status and count */ + for(;;) { + err = read_reg(stir, REG_FIFOCTL, stir->fifo_status, + FIFO_REGS_SIZE); + if (unlikely(err != FIFO_REGS_SIZE)) { + warn("%s: FIFO register read error: %d", + stir->netdev->name, err); + + return err; + } + + status = stir->fifo_status[0]; + count = (unsigned)(stir->fifo_status[2] & 0x1f) << 8 + | stir->fifo_status[1]; + + pr_debug("fifo status 0x%lx count %lu\n", status, count); + + /* error when receive/transmit fifo gets confused */ + if (status & FIFOCTL_RXERR) { + stir->stats.rx_fifo_errors++; + stir->stats.rx_errors++; + break; + } + + if (status & FIFOCTL_TXERR) { + stir->stats.tx_fifo_errors++; + stir->stats.tx_errors++; + break; + } + + /* is fifo receiving already, or empty */ + if (!(status & FIFOCTL_DIR) + || (status & FIFOCTL_EMPTY)) + return 0; + + if (signal_pending(current)) + return -EINTR; + + /* shutting down? */ + if (!netif_running(stir->netdev) + || !netif_device_present(stir->netdev)) + return -ESHUTDOWN; + + /* only waiting for some space */ + if (space >= 0 && STIR_FIFO_SIZE - 4 > space + count) + return 0; + + /* estimate transfer time for remaining chars */ + msleep((count * 8000) / stir->speed); + } + + err = write_reg(stir, REG_FIFOCTL, FIFOCTL_CLR); + if (err) + return err; + err = write_reg(stir, REG_FIFOCTL, 0); + if (err) + return err; + + return 0; +} + + +/* Wait for turnaround delay before starting transmit. */ +static void turnaround_delay(const struct stir_cb *stir, long us) +{ + long ticks; + struct timeval now; + + if (us <= 0) + return; + + do_gettimeofday(&now); + if (now.tv_sec - stir->rx_time.tv_sec > 0) + us -= USEC_PER_SEC; + us -= now.tv_usec - stir->rx_time.tv_usec; + if (us < 10) + return; + + ticks = us / (1000000 / HZ); + if (ticks > 0) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1 + ticks); + } else + udelay(us); +} + +/* + * Start receiver by submitting a request to the receive pipe. + * If nothing is available it will return after rx_interval. + */ +static int receive_start(struct stir_cb *stir) +{ + /* reset state */ + stir->receiving = 1; + + stir->rx_buff.in_frame = FALSE; + stir->rx_buff.state = OUTSIDE_FRAME; + + stir->rx_urb->status = 0; + return usb_submit_urb(stir->rx_urb, GFP_KERNEL); +} + +/* Stop all pending receive Urb's */ +static void receive_stop(struct stir_cb *stir) +{ + stir->receiving = 0; + usb_kill_urb(stir->rx_urb); + + if (stir->rx_buff.in_frame) + stir->stats.collisions++; +} +/* + * Wrap data in socket buffer and send it. + */ +static void stir_send(struct stir_cb *stir, struct sk_buff *skb) +{ + unsigned wraplen; + int first_frame = 0; + + /* if receiving, need to turnaround */ + if (stir->receiving) { + receive_stop(stir); + turnaround_delay(stir, irda_get_mtt(skb)); + first_frame = 1; + } + + if (isfir(stir->speed)) + wraplen = wrap_fir_skb(skb, stir->io_buf); + else + wraplen = wrap_sir_skb(skb, stir->io_buf); + + /* check for space available in fifo */ + if (!first_frame) + fifo_txwait(stir, wraplen); + + stir->stats.tx_packets++; + stir->stats.tx_bytes += skb->len; + stir->netdev->trans_start = jiffies; + pr_debug("send %d (%d)\n", skb->len, wraplen); + + if (usb_bulk_msg(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1), + stir->io_buf, wraplen, + NULL, TRANSMIT_TIMEOUT)) + stir->stats.tx_errors++; +} + +/* + * Transmit state machine thread + */ +static int stir_transmit_thread(void *arg) +{ + struct stir_cb *stir = arg; + struct net_device *dev = stir->netdev; + struct sk_buff *skb; + + daemonize("%s", dev->name); + allow_signal(SIGTERM); + + while (netif_running(dev) + && netif_device_present(dev) + && !signal_pending(current)) + { +#ifdef CONFIG_PM + /* if suspending, then power off and wait */ + if (unlikely(current->flags & PF_FREEZE)) { + if (stir->receiving) + receive_stop(stir); + else + fifo_txwait(stir, -1); + + write_reg(stir, REG_CTRL1, CTRL1_TXPWD|CTRL1_RXPWD); + + refrigerator(PF_FREEZE); + + if (change_speed(stir, stir->speed)) + break; + } +#endif + + /* if something to send? */ + skb = xchg(&stir->tx_pending, NULL); + if (skb) { + unsigned new_speed = irda_get_next_speed(skb); + netif_wake_queue(dev); + + if (skb->len > 0) + stir_send(stir, skb); + dev_kfree_skb(skb); + + if ((new_speed != -1) && (stir->speed != new_speed)) { + if (fifo_txwait(stir, -1) || + change_speed(stir, new_speed)) + break; + } + continue; + } + + /* nothing to send? start receiving */ + if (!stir->receiving + && irda_device_txqueue_empty(dev)) { + /* Wait otherwise chip gets confused. */ + if (fifo_txwait(stir, -1)) + break; + + if (unlikely(receive_start(stir))) { + if (net_ratelimit()) + info("%s: receive usb submit failed", + stir->netdev->name); + stir->receiving = 0; + msleep(10); + continue; + } + } + + /* sleep if nothing to send */ + wait_event_interruptible(stir->thr_wait, stir->tx_pending); + } + + complete_and_exit (&stir->thr_exited, 0); +} + + +/* + * USB bulk receive completion callback. + * Wakes up every ms (usb round trip) with wrapped + * data. + */ +static void stir_rcv_irq(struct urb *urb, struct pt_regs *regs) +{ + struct stir_cb *stir = urb->context; + int err; + + /* in process of stopping, just drop data */ + if (!netif_running(stir->netdev)) + return; + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) + return; + + if (urb->actual_length > 0) { + pr_debug("receive %d\n", urb->actual_length); + unwrap_chars(stir, urb->transfer_buffer, + urb->actual_length); + + stir->netdev->last_rx = jiffies; + do_gettimeofday(&stir->rx_time); + } + + /* kernel thread is stopping receiver don't resubmit */ + if (!stir->receiving) + return; + + /* resubmit existing urb */ + err = usb_submit_urb(urb, GFP_ATOMIC); + + /* in case of error, the kernel thread will restart us */ + if (err) { + warn("%s: usb receive submit error: %d", + stir->netdev->name, err); + stir->receiving = 0; + wake_up(&stir->thr_wait); + } +} + +/* + * Function stir_net_open (dev) + * + * Network device is taken up. Usually this is done by "ifconfig irda0 up" + */ +static int stir_net_open(struct net_device *netdev) +{ + struct stir_cb *stir = netdev_priv(netdev); + int err; + char hwname[16]; + + err = usb_clear_halt(stir->usbdev, usb_sndbulkpipe(stir->usbdev, 1)); + if (err) + goto err_out1; + err = usb_clear_halt(stir->usbdev, usb_rcvbulkpipe(stir->usbdev, 2)); + if (err) + goto err_out1; + + err = change_speed(stir, 9600); + if (err) + goto err_out1; + + err = -ENOMEM; + + /* Initialize for SIR/FIR to copy data directly into skb. */ + stir->receiving = 0; + stir->rx_buff.truesize = IRDA_SKB_MAX_MTU; + stir->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); + if (!stir->rx_buff.skb) + goto err_out1; + + skb_reserve(stir->rx_buff.skb, 1); + stir->rx_buff.head = stir->rx_buff.skb->data; + do_gettimeofday(&stir->rx_time); + + stir->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!stir->rx_urb) + goto err_out2; + + stir->io_buf = kmalloc(STIR_FIFO_SIZE, GFP_KERNEL); + if (!stir->io_buf) + goto err_out3; + + usb_fill_bulk_urb(stir->rx_urb, stir->usbdev, + usb_rcvbulkpipe(stir->usbdev, 2), + stir->io_buf, STIR_FIFO_SIZE, + stir_rcv_irq, stir); + + stir->fifo_status = kmalloc(FIFO_REGS_SIZE, GFP_KERNEL); + if (!stir->fifo_status) + goto err_out4; + + /* + * Now that everything should be initialized properly, + * Open new IrLAP layer instance to take care of us... + * Note : will send immediately a speed change... + */ + sprintf(hwname, "usb#%d", stir->usbdev->devnum); + stir->irlap = irlap_open(netdev, &stir->qos, hwname); + if (!stir->irlap) { + err("stir4200: irlap_open failed"); + goto err_out5; + } + + /** Start kernel thread for transmit. */ + stir->thr_pid = kernel_thread(stir_transmit_thread, stir, + CLONE_FS|CLONE_FILES); + if (stir->thr_pid < 0) { + err = stir->thr_pid; + err("stir4200: unable to start kernel thread"); + goto err_out6; + } + + netif_start_queue(netdev); + + return 0; + + err_out6: + irlap_close(stir->irlap); + err_out5: + kfree(stir->fifo_status); + err_out4: + kfree(stir->io_buf); + err_out3: + usb_free_urb(stir->rx_urb); + err_out2: + kfree_skb(stir->rx_buff.skb); + err_out1: + return err; +} + +/* + * Function stir_net_close (stir) + * + * Network device is taken down. Usually this is done by + * "ifconfig irda0 down" + */ +static int stir_net_close(struct net_device *netdev) +{ + struct stir_cb *stir = netdev_priv(netdev); + + /* Stop transmit processing */ + netif_stop_queue(netdev); + + /* Kill transmit thread */ + kill_proc(stir->thr_pid, SIGTERM, 1); + wait_for_completion(&stir->thr_exited); + kfree(stir->fifo_status); + + /* Mop up receive urb's */ + usb_kill_urb(stir->rx_urb); + + kfree(stir->io_buf); + usb_free_urb(stir->rx_urb); + kfree_skb(stir->rx_buff.skb); + + /* Stop and remove instance of IrLAP */ + if (stir->irlap) + irlap_close(stir->irlap); + + stir->irlap = NULL; + + return 0; +} + +/* + * IOCTLs : Extra out-of-band network commands... + */ +static int stir_net_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct stir_cb *stir = netdev_priv(netdev); + int ret = 0; + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the device is still there */ + if (netif_device_present(stir->netdev)) + ret = change_speed(stir, irq->ifr_baudrate); + break; + + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the IrDA stack is still there */ + if (netif_running(stir->netdev)) + irda_device_set_media_busy(stir->netdev, TRUE); + break; + + case SIOCGRECEIVING: + /* Only approximately true */ + irq->ifr_receiving = stir->receiving; + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +/* + * Get device stats (for /proc/net/dev and ifconfig) + */ +static struct net_device_stats *stir_net_get_stats(struct net_device *netdev) +{ + struct stir_cb *stir = netdev_priv(netdev); + return &stir->stats; +} + +/* + * This routine is called by the USB subsystem for each new device + * in the system. We need to check if the device is ours, and in + * this case start handling it. + * Note : it might be worth protecting this function by a global + * spinlock... Or not, because maybe USB already deal with that... + */ +static int stir_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct stir_cb *stir = NULL; + struct net_device *net; + int ret = -ENOMEM; + + /* Allocate network device container. */ + net = alloc_irdadev(sizeof(*stir)); + if(!net) + goto err_out1; + + SET_MODULE_OWNER(net); + SET_NETDEV_DEV(net, &intf->dev); + stir = netdev_priv(net); + stir->netdev = net; + stir->usbdev = dev; + + ret = usb_reset_configuration(dev); + if (ret != 0) { + err("stir4200: usb reset configuration failed"); + goto err_out2; + } + + printk(KERN_INFO "SigmaTel STIr4200 IRDA/USB found at address %d, " + "Vendor: %x, Product: %x\n", + dev->devnum, le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&stir->qos); + + /* That's the Rx capability. */ + stir->qos.baud_rate.bits &= IR_2400 | IR_9600 | IR_19200 | + IR_38400 | IR_57600 | IR_115200 | + (IR_4000000 << 8); + stir->qos.min_turn_time.bits &= qos_mtt_bits; + irda_qos_bits_to_value(&stir->qos); + + init_completion (&stir->thr_exited); + init_waitqueue_head (&stir->thr_wait); + + /* Override the network functions we need to use */ + net->hard_start_xmit = stir_hard_xmit; + net->open = stir_net_open; + net->stop = stir_net_close; + net->get_stats = stir_net_get_stats; + net->do_ioctl = stir_net_ioctl; + + ret = register_netdev(net); + if (ret != 0) + goto err_out2; + + info("IrDA: Registered SigmaTel device %s", net->name); + + usb_set_intfdata(intf, stir); + + return 0; + +err_out2: + free_netdev(net); +err_out1: + return ret; +} + +/* + * The current device is removed, the USB layer tell us to shut it down... + */ +static void stir_disconnect(struct usb_interface *intf) +{ + struct stir_cb *stir = usb_get_intfdata(intf); + + if (!stir) + return; + + unregister_netdev(stir->netdev); + free_netdev(stir->netdev); + + usb_set_intfdata(intf, NULL); +} + +#ifdef CONFIG_PM +/* Power management suspend, so power off the transmitter/receiver */ +static int stir_suspend(struct usb_interface *intf, u32 state) +{ + struct stir_cb *stir = usb_get_intfdata(intf); + + netif_device_detach(stir->netdev); + return 0; +} + +/* Coming out of suspend, so reset hardware */ +static int stir_resume(struct usb_interface *intf) +{ + struct stir_cb *stir = usb_get_intfdata(intf); + + netif_device_attach(stir->netdev); + + /* receiver restarted when send thread wakes up */ + return 0; +} +#endif + +/* + * USB device callbacks + */ +static struct usb_driver irda_driver = { + .owner = THIS_MODULE, + .name = "stir4200", + .probe = stir_probe, + .disconnect = stir_disconnect, + .id_table = dongles, +#ifdef CONFIG_PM + .suspend = stir_suspend, + .resume = stir_resume, +#endif +}; + +/* + * Module insertion + */ +static int __init stir_init(void) +{ + return usb_register(&irda_driver); +} +module_init(stir_init); + +/* + * Module removal + */ +static void __exit stir_cleanup(void) +{ + /* Deregister the driver and remove all pending instances */ + usb_deregister(&irda_driver); +} +module_exit(stir_cleanup); diff --git a/drivers/net/irda/tekram-sir.c b/drivers/net/irda/tekram-sir.c new file mode 100644 index 000000000000..0dd6bc7af3f2 --- /dev/null +++ b/drivers/net/irda/tekram-sir.c @@ -0,0 +1,232 @@ +/********************************************************************* + * + * Filename: tekram.c + * Version: 1.3 + * Description: Implementation of the Tekram IrMate IR-210B dongle + * Status: Experimental. + * Author: Dag Brattli <dagb@cs.uit.no> + * Created at: Wed Oct 21 20:02:35 1998 + * Modified at: Sun Oct 27 22:02:38 2002 + * Modified by: Martin Diehl <mad@mdiehl.de> + * + * 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 <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> + +#include <net/irda/irda.h> + +#include "sir-dev.h" + +static int tekram_delay = 150; /* default is 150 ms */ +module_param(tekram_delay, int, 0); +MODULE_PARM_DESC(tekram_delay, "tekram dongle write complete delay"); + +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, +}; + +static int __init tekram_sir_init(void) +{ + if (tekram_delay < 1 || tekram_delay > 500) + tekram_delay = 200; + IRDA_DEBUG(1, "%s - using %d ms delay\n", + tekram.driver_name, tekram_delay); + return irda_register_dongle(&tekram); +} + +static void __exit tekram_sir_cleanup(void) +{ + irda_unregister_dongle(&tekram); +} + +static int tekram_open(struct sir_dev *dev) +{ + struct qos_info *qos = &dev->qos; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + 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); + + /* irda thread waits 50 msec for power settling */ + + return 0; +} + +static int tekram_close(struct sir_dev *dev) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* Power off dongle */ + sirdev_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 state = dev->fsm.substate; + unsigned delay = 0; + u8 byte; + static int ret = 0; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + switch(state) { + case SIRDEV_STATE_DONGLE_SPEED: + + switch (speed) { + default: + speed = 9600; + ret = -EINVAL; + /* 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 */ + sirdev_set_dtr_rts(dev, TRUE, FALSE); + + /* Wait at least 7us */ + udelay(14); + + /* Write control byte */ + sirdev_raw_write(dev, &byte, 1); + + dev->speed = speed; + + state = TEKRAM_STATE_WAIT_SPEED; + delay = tekram_delay; + break; + + case TEKRAM_STATE_WAIT_SPEED: + /* Set DTR, Set RTS */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + udelay(50); + break; + + default: + IRDA_ERROR("%s - undefined state %d\n", __FUNCTION__, state); + ret = -EINVAL; + break; + } + + dev->fsm.substate = state; + return (delay > 0) ? delay : ret; +} + +/* + * 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 + */ + +static int tekram_reset(struct sir_dev *dev) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__); + + /* Clear DTR, Set RTS */ + sirdev_set_dtr_rts(dev, FALSE, TRUE); + + /* Should sleep 1 ms */ + msleep(1); + + /* Set DTR, Set RTS */ + sirdev_set_dtr_rts(dev, TRUE, TRUE); + + /* Wait at least 50 us */ + udelay(75); + + dev->speed = 9600; + + return 0; +} + +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("Tekram IrMate IR-210B dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-0"); /* IRDA_TEKRAM_DONGLE */ + +module_init(tekram_sir_init); +module_exit(tekram_sir_cleanup); diff --git a/drivers/net/irda/tekram.c b/drivers/net/irda/tekram.c new file mode 100644 index 000000000000..8f6258221cb0 --- /dev/null +++ b/drivers/net/irda/tekram.c @@ -0,0 +1,282 @@ +/********************************************************************* + * + * Filename: tekram.c + * Version: 1.2 + * Description: Implementation of the Tekram IrMate IR-210B dongle + * Status: Experimental. + * Author: Dag Brattli <dagb@cs.uit.no> + * Created at: Wed Oct 21 20:02:35 1998 + * Modified at: Fri Dec 17 09:13:09 1999 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsų admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/tty.h> +#include <linux/init.h> + +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> + +static void tekram_open(dongle_t *self, struct qos_info *qos); +static void tekram_close(dongle_t *self); +static int tekram_change_speed(struct irda_task *task); +static int tekram_reset(struct irda_task *task); + +#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_reg dongle = { + .type = IRDA_TEKRAM_DONGLE, + .open = tekram_open, + .close = tekram_close, + .reset = tekram_reset, + .change_speed = tekram_change_speed, + .owner = THIS_MODULE, +}; + +static int __init tekram_init(void) +{ + return irda_device_register_dongle(&dongle); +} + +static void __exit tekram_cleanup(void) +{ + irda_device_unregister_dongle(&dongle); +} + +static void tekram_open(dongle_t *self, struct qos_info *qos) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); + + 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); +} + +static void tekram_close(dongle_t *self) +{ + IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); + + /* Power off dongle */ + self->set_dtr_rts(self->dev, FALSE, FALSE); + + if (self->reset_task) + irda_task_delete(self->reset_task); + if (self->speed_task) + irda_task_delete(self->speed_task); +} + +/* + * 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) + * 5. clear RTS (return to NORMAL Operation) + * 6. wait at least 50 us, new setting (baud rate, etc) takes effect here + * after + */ +static int tekram_change_speed(struct irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + __u32 speed = (__u32) task->param; + __u8 byte; + int ret = 0; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); + + IRDA_ASSERT(task != NULL, return -1;); + + if (self->speed_task && self->speed_task != task) { + IRDA_DEBUG(0, "%s(), busy!\n", __FUNCTION__ ); + return msecs_to_jiffies(10); + } else + self->speed_task = task; + + switch (speed) { + default: + 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; + } + + switch (task->state) { + case IRDA_TASK_INIT: + case IRDA_TASK_CHILD_INIT: + /* + * Need to reset the dongle and go to 9600 bps before + * programming + */ + if (irda_task_execute(self, tekram_reset, NULL, task, + (void *) speed)) + { + /* Dongle need more time to reset */ + irda_task_next_state(task, IRDA_TASK_CHILD_WAIT); + + /* Give reset 1 sec to finish */ + ret = msecs_to_jiffies(1000); + } else + irda_task_next_state(task, IRDA_TASK_CHILD_DONE); + break; + case IRDA_TASK_CHILD_WAIT: + IRDA_WARNING("%s(), resetting dongle timed out!\n", + __FUNCTION__); + ret = -1; + break; + case IRDA_TASK_CHILD_DONE: + /* Set DTR, Clear RTS */ + self->set_dtr_rts(self->dev, TRUE, FALSE); + + /* Wait at least 7us */ + udelay(14); + + /* Write control byte */ + self->write(self->dev, &byte, 1); + + irda_task_next_state(task, IRDA_TASK_WAIT); + + /* Wait at least 100 ms */ + ret = msecs_to_jiffies(150); + break; + case IRDA_TASK_WAIT: + /* Set DTR, Set RTS */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + + irda_task_next_state(task, IRDA_TASK_DONE); + self->speed_task = NULL; + break; + default: + IRDA_ERROR("%s(), unknown state %d\n", + __FUNCTION__, task->state); + irda_task_next_state(task, IRDA_TASK_DONE); + self->speed_task = NULL; + ret = -1; + break; + } + return ret; +} + +/* + * 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 + */ +int tekram_reset(struct irda_task *task) +{ + dongle_t *self = (dongle_t *) task->instance; + int ret = 0; + + IRDA_DEBUG(2, "%s()\n", __FUNCTION__ ); + + IRDA_ASSERT(task != NULL, return -1;); + + if (self->reset_task && self->reset_task != task) { + IRDA_DEBUG(0, "%s(), busy!\n", __FUNCTION__ ); + return msecs_to_jiffies(10); + } else + self->reset_task = task; + + /* Power off dongle */ + //self->set_dtr_rts(self->dev, FALSE, FALSE); + self->set_dtr_rts(self->dev, TRUE, TRUE); + + switch (task->state) { + case IRDA_TASK_INIT: + irda_task_next_state(task, IRDA_TASK_WAIT1); + + /* Sleep 50 ms */ + ret = msecs_to_jiffies(50); + break; + case IRDA_TASK_WAIT1: + /* Clear DTR, Set RTS */ + self->set_dtr_rts(self->dev, FALSE, TRUE); + + irda_task_next_state(task, IRDA_TASK_WAIT2); + + /* Should sleep 1 ms */ + ret = msecs_to_jiffies(1); + break; + case IRDA_TASK_WAIT2: + /* Set DTR, Set RTS */ + self->set_dtr_rts(self->dev, TRUE, TRUE); + + /* Wait at least 50 us */ + udelay(75); + + irda_task_next_state(task, IRDA_TASK_DONE); + self->reset_task = NULL; + break; + default: + IRDA_ERROR("%s(), unknown state %d\n", + __FUNCTION__, task->state); + irda_task_next_state(task, IRDA_TASK_DONE); + self->reset_task = NULL; + ret = -1; + } + return ret; +} + +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("Tekram IrMate IR-210B dongle driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("irda-dongle-0"); /* IRDA_TEKRAM_DONGLE */ + +/* + * Function init_module (void) + * + * Initialize Tekram module + * + */ +module_init(tekram_init); + +/* + * Function cleanup_module (void) + * + * Cleanup Tekram module + * + */ +module_exit(tekram_cleanup); diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c new file mode 100644 index 000000000000..8bafb455c102 --- /dev/null +++ b/drivers/net/irda/via-ircc.c @@ -0,0 +1,1676 @@ +/******************************************************************** + Filename: via-ircc.c + Version: 1.0 + Description: Driver for the VIA VT8231/VT8233 IrDA chipsets + Author: VIA Technologies,inc + Date : 08/06/2003 + +Copyright (c) 1998-2003 VIA Technologies, Inc. + +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, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTIES OR REPRESENTATIONS; 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. + +F01 Oct/02/02: Modify code for V0.11(move out back to back transfer) +F02 Oct/28/02: Add SB device ID for 3147 and 3177. + Comment : + jul/09/2002 : only implement two kind of dongle currently. + Oct/02/2002 : work on VT8231 and VT8233 . + Aug/06/2003 : change driver format to pci driver . + +2004-02-16: <sda@bdit.de> +- Removed unneeded 'legacy' pci stuff. +- Make sure SIR mode is set (hw_init()) before calling mode-dependant stuff. +- On speed change from core, don't send SIR frame with new speed. + Use current speed and change speeds later. +- Make module-param dongle_id actually work. +- New dongle_id 17 (0x11): TDFS4500. Single-ended SIR only. + Tested with home-grown PCB on EPIA boards. +- Code cleanup. + + ********************************************************************/ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/rtnetlink.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> + +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/byteorder.h> + +#include <linux/pm.h> + +#include <net/irda/wrapper.h> +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> + +#include "via-ircc.h" + +#define VIA_MODULE_NAME "via-ircc" +#define CHIP_IO_EXTENT 0x40 + +static char *driver_name = VIA_MODULE_NAME; + +/* Module parameters */ +static int qos_mtt_bits = 0x07; /* 1 ms or more */ +static int dongle_id = 0; /* default: probe */ + +/* We can't guess the type of connected dongle, user *must* supply it. */ +module_param(dongle_id, int, 0); + +/* FIXME : we should not need this, because instances should be automatically + * managed by the PCI layer. Especially that we seem to only be using the + * first entry. Jean II */ +/* Max 4 instances for now */ +static struct via_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL }; + +/* Some prototypes */ +static int via_ircc_open(int i, chipio_t * info, unsigned int id); +static int via_ircc_close(struct via_ircc_cb *self); +static int via_ircc_dma_receive(struct via_ircc_cb *self); +static int via_ircc_dma_receive_complete(struct via_ircc_cb *self, + int iobase); +static int via_ircc_hard_xmit_sir(struct sk_buff *skb, + struct net_device *dev); +static int via_ircc_hard_xmit_fir(struct sk_buff *skb, + struct net_device *dev); +static void via_hw_init(struct via_ircc_cb *self); +static void via_ircc_change_speed(struct via_ircc_cb *self, __u32 baud); +static irqreturn_t via_ircc_interrupt(int irq, void *dev_id, + struct pt_regs *regs); +static int via_ircc_is_receiving(struct via_ircc_cb *self); +static int via_ircc_read_dongle_id(int iobase); + +static int via_ircc_net_open(struct net_device *dev); +static int via_ircc_net_close(struct net_device *dev); +static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, + int cmd); +static struct net_device_stats *via_ircc_net_get_stats(struct net_device + *dev); +static void via_ircc_change_dongle_speed(int iobase, int speed, + int dongle_id); +static int RxTimerHandler(struct via_ircc_cb *self, int iobase); +static void hwreset(struct via_ircc_cb *self); +static int via_ircc_dma_xmit(struct via_ircc_cb *self, u16 iobase); +static int upload_rxdata(struct via_ircc_cb *self, int iobase); +static int __devinit via_init_one (struct pci_dev *pcidev, const struct pci_device_id *id); +static void __devexit via_remove_one (struct pci_dev *pdev); + +/* FIXME : Should use udelay() instead, even if we are x86 only - Jean II */ +static void iodelay(int udelay) +{ + u8 data; + int i; + + for (i = 0; i < udelay; i++) { + data = inb(0x80); + } +} + +static struct pci_device_id via_pci_tbl[] = { + { PCI_VENDOR_ID_VIA, 0x8231, PCI_ANY_ID, PCI_ANY_ID,0,0,0 }, + { PCI_VENDOR_ID_VIA, 0x3109, PCI_ANY_ID, PCI_ANY_ID,0,0,1 }, + { PCI_VENDOR_ID_VIA, 0x3074, PCI_ANY_ID, PCI_ANY_ID,0,0,2 }, + { PCI_VENDOR_ID_VIA, 0x3147, PCI_ANY_ID, PCI_ANY_ID,0,0,3 }, + { PCI_VENDOR_ID_VIA, 0x3177, PCI_ANY_ID, PCI_ANY_ID,0,0,4 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci,via_pci_tbl); + + +static struct pci_driver via_driver = { + .name = VIA_MODULE_NAME, + .id_table = via_pci_tbl, + .probe = via_init_one, + .remove = __devexit_p(via_remove_one), +}; + + +/* + * Function via_ircc_init () + * + * Initialize chip. Just find out chip type and resource. + */ +static int __init via_ircc_init(void) +{ + int rc; + + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + rc = pci_register_driver(&via_driver); + if (rc < 0) { + IRDA_DEBUG(0, "%s(): error rc = %d, returning -ENODEV...\n", + __FUNCTION__, rc); + return -ENODEV; + } + return 0; +} + +static int __devinit via_init_one (struct pci_dev *pcidev, const struct pci_device_id *id) +{ + int rc; + u8 temp,oldPCI_40,oldPCI_44,bTmp,bTmp1; + u16 Chipset,FirDRQ1,FirDRQ0,FirIRQ,FirIOBase; + chipio_t info; + + IRDA_DEBUG(2, "%s(): Device ID=(0X%X)\n", __FUNCTION__, id->device); + + rc = pci_enable_device (pcidev); + if (rc) { + IRDA_DEBUG(0, "%s(): error rc = %d\n", __FUNCTION__, rc); + return -ENODEV; + } + + // South Bridge exist + if ( ReadLPCReg(0x20) != 0x3C ) + Chipset=0x3096; + else + Chipset=0x3076; + + if (Chipset==0x3076) { + IRDA_DEBUG(2, "%s(): Chipset = 3076\n", __FUNCTION__); + + WriteLPCReg(7,0x0c ); + temp=ReadLPCReg(0x30);//check if BIOS Enable Fir + if((temp&0x01)==1) { // BIOS close or no FIR + WriteLPCReg(0x1d, 0x82 ); + WriteLPCReg(0x23,0x18); + temp=ReadLPCReg(0xF0); + if((temp&0x01)==0) { + temp=(ReadLPCReg(0x74)&0x03); //DMA + FirDRQ0=temp + 4; + temp=(ReadLPCReg(0x74)&0x0C) >> 2; + FirDRQ1=temp + 4; + } else { + temp=(ReadLPCReg(0x74)&0x0C) >> 2; //DMA + FirDRQ0=temp + 4; + FirDRQ1=FirDRQ0; + } + FirIRQ=(ReadLPCReg(0x70)&0x0f); //IRQ + FirIOBase=ReadLPCReg(0x60 ) << 8; //IO Space :high byte + FirIOBase=FirIOBase| ReadLPCReg(0x61) ; //low byte + FirIOBase=FirIOBase ; + info.fir_base=FirIOBase; + info.irq=FirIRQ; + info.dma=FirDRQ1; + info.dma2=FirDRQ0; + pci_read_config_byte(pcidev,0x40,&bTmp); + pci_write_config_byte(pcidev,0x40,((bTmp | 0x08) & 0xfe)); + pci_read_config_byte(pcidev,0x42,&bTmp); + pci_write_config_byte(pcidev,0x42,(bTmp | 0xf0)); + pci_write_config_byte(pcidev,0x5a,0xc0); + WriteLPCReg(0x28, 0x70 ); + if (via_ircc_open(0, &info,0x3076) == 0) + rc=0; + } else + rc = -ENODEV; //IR not turn on + } else { //Not VT1211 + IRDA_DEBUG(2, "%s(): Chipset = 3096\n", __FUNCTION__); + + pci_read_config_byte(pcidev,0x67,&bTmp);//check if BIOS Enable Fir + if((bTmp&0x01)==1) { // BIOS enable FIR + //Enable Double DMA clock + pci_read_config_byte(pcidev,0x42,&oldPCI_40); + pci_write_config_byte(pcidev,0x42,oldPCI_40 | 0x80); + pci_read_config_byte(pcidev,0x40,&oldPCI_40); + pci_write_config_byte(pcidev,0x40,oldPCI_40 & 0xf7); + pci_read_config_byte(pcidev,0x44,&oldPCI_44); + pci_write_config_byte(pcidev,0x44,0x4e); + //---------- read configuration from Function0 of south bridge + if((bTmp&0x02)==0) { + pci_read_config_byte(pcidev,0x44,&bTmp1); //DMA + FirDRQ0 = (bTmp1 & 0x30) >> 4; + pci_read_config_byte(pcidev,0x44,&bTmp1); + FirDRQ1 = (bTmp1 & 0xc0) >> 6; + } else { + pci_read_config_byte(pcidev,0x44,&bTmp1); //DMA + FirDRQ0 = (bTmp1 & 0x30) >> 4 ; + FirDRQ1=0; + } + pci_read_config_byte(pcidev,0x47,&bTmp1); //IRQ + FirIRQ = bTmp1 & 0x0f; + + pci_read_config_byte(pcidev,0x69,&bTmp); + FirIOBase = bTmp << 8;//hight byte + pci_read_config_byte(pcidev,0x68,&bTmp); + FirIOBase = (FirIOBase | bTmp ) & 0xfff0; + //------------------------- + info.fir_base=FirIOBase; + info.irq=FirIRQ; + info.dma=FirDRQ1; + info.dma2=FirDRQ0; + if (via_ircc_open(0, &info,0x3096) == 0) + rc=0; + } else + rc = -ENODEV; //IR not turn on !!!!! + }//Not VT1211 + + IRDA_DEBUG(2, "%s(): End - rc = %d\n", __FUNCTION__, rc); + return rc; +} + +/* + * Function via_ircc_clean () + * + * Close all configured chips + * + */ +static void via_ircc_clean(void) +{ + int i; + + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + for (i=0; i < 4; i++) { + if (dev_self[i]) + via_ircc_close(dev_self[i]); + } +} + +static void __devexit via_remove_one (struct pci_dev *pdev) +{ + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + /* FIXME : This is ugly. We should use pci_get_drvdata(pdev); + * to get our driver instance and call directly via_ircc_close(). + * See vlsi_ir for details... + * Jean II */ + via_ircc_clean(); + + /* FIXME : This should be in via_ircc_close(), because here we may + * theoritically disable still configured devices :-( - Jean II */ + pci_disable_device(pdev); +} + +static void __exit via_ircc_cleanup(void) +{ + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + /* FIXME : This should be redundant, as pci_unregister_driver() + * should call via_remove_one() on each device. + * Jean II */ + via_ircc_clean(); + + /* Cleanup all instances of the driver */ + pci_unregister_driver (&via_driver); +} + +/* + * Function via_ircc_open (iobase, irq) + * + * Open driver instance + * + */ +static __devinit int via_ircc_open(int i, chipio_t * info, unsigned int id) +{ + struct net_device *dev; + struct via_ircc_cb *self; + int err; + + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + /* Allocate new instance of the driver */ + dev = alloc_irdadev(sizeof(struct via_ircc_cb)); + if (dev == NULL) + return -ENOMEM; + + self = dev->priv; + self->netdev = dev; + spin_lock_init(&self->lock); + + /* FIXME : We should store our driver instance in the PCI layer, + * using pci_set_drvdata(), not in this array. + * See vlsi_ir for details... - Jean II */ + /* FIXME : 'i' is always 0 (see via_init_one()) :-( - Jean II */ + /* Need to store self somewhere */ + dev_self[i] = self; + self->index = i; + /* Initialize Resource */ + self->io.cfg_base = info->cfg_base; + self->io.fir_base = info->fir_base; + self->io.irq = info->irq; + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.dma = info->dma; + self->io.dma2 = info->dma2; + self->io.fifo_size = 32; + self->chip_id = id; + self->st_fifo.len = 0; + self->RxDataReady = 0; + + /* Reserve the ioports that we need */ + if (!request_region(self->io.fir_base, self->io.fir_ext, driver_name)) { + IRDA_DEBUG(0, "%s(), can't get iobase of 0x%03x\n", + __FUNCTION__, self->io.fir_base); + err = -ENODEV; + goto err_out1; + } + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + /* Check if user has supplied the dongle id or not */ + if (!dongle_id) + dongle_id = via_ircc_read_dongle_id(self->io.fir_base); + self->io.dongle_id = dongle_id; + + /* The only value we must override it the baudrate */ + /* Maximum speeds and capabilities are dongle-dependant. */ + switch( self->io.dongle_id ){ + case 0x0d: + self->qos.baud_rate.bits = + IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200 | + IR_576000 | IR_1152000 | (IR_4000000 << 8); + break; + default: + self->qos.baud_rate.bits = + IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200; + break; + } + + /* Following was used for testing: + * + * self->qos.baud_rate.bits = IR_9600; + * + * Is is no good, as it prohibits (error-prone) speed-changes. + */ + + self->qos.min_turn_time.bits = qos_mtt_bits; + irda_qos_bits_to_value(&self->qos); + + /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ + self->rx_buff.truesize = 14384 + 2048; + self->tx_buff.truesize = 14384 + 2048; + + /* Allocate memory if needed */ + self->rx_buff.head = + dma_alloc_coherent(NULL, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); + if (self->rx_buff.head == NULL) { + err = -ENOMEM; + goto err_out2; + } + memset(self->rx_buff.head, 0, self->rx_buff.truesize); + + self->tx_buff.head = + dma_alloc_coherent(NULL, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); + if (self->tx_buff.head == NULL) { + err = -ENOMEM; + goto err_out3; + } + 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; + + /* Reset Tx queue info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + + /* Keep track of module usage */ + SET_MODULE_OWNER(dev); + + /* Override the network functions we need to use */ + dev->hard_start_xmit = via_ircc_hard_xmit_sir; + dev->open = via_ircc_net_open; + dev->stop = via_ircc_net_close; + dev->do_ioctl = via_ircc_net_ioctl; + dev->get_stats = via_ircc_net_get_stats; + + err = register_netdev(dev); + if (err) + goto err_out4; + + IRDA_MESSAGE("IrDA: Registered device %s (via-ircc)\n", dev->name); + + /* Initialise the hardware.. + */ + self->io.speed = 9600; + via_hw_init(self); + return 0; + err_out4: + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + err_out3: + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + err_out2: + release_region(self->io.fir_base, self->io.fir_ext); + err_out1: + free_netdev(dev); + dev_self[i] = NULL; + return err; +} + +/* + * Function via_ircc_close (self) + * + * Close driver instance + * + */ +static int via_ircc_close(struct via_ircc_cb *self) +{ + int iobase; + + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + IRDA_ASSERT(self != NULL, return -1;); + + iobase = self->io.fir_base; + + ResetChip(iobase, 5); //hardware reset. + /* Remove netdevice */ + unregister_netdev(self->netdev); + + /* Release the PORT that this driver is using */ + IRDA_DEBUG(2, "%s(), Releasing Region %03x\n", + __FUNCTION__, self->io.fir_base); + release_region(self->io.fir_base, self->io.fir_ext); + if (self->tx_buff.head) + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + if (self->rx_buff.head) + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + dev_self[self->index] = NULL; + + free_netdev(self->netdev); + + return 0; +} + +/* + * Function via_hw_init(self) + * + * Returns non-negative on success. + * + * Formerly via_ircc_setup + */ +static void via_hw_init(struct via_ircc_cb *self) +{ + int iobase = self->io.fir_base; + + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + SetMaxRxPacketSize(iobase, 0x0fff); //set to max:4095 + // FIFO Init + EnRXFIFOReadyInt(iobase, OFF); + EnRXFIFOHalfLevelInt(iobase, OFF); + EnTXFIFOHalfLevelInt(iobase, OFF); + EnTXFIFOUnderrunEOMInt(iobase, ON); + EnTXFIFOReadyInt(iobase, OFF); + InvertTX(iobase, OFF); + InvertRX(iobase, OFF); + + if (ReadLPCReg(0x20) == 0x3c) + WriteLPCReg(0xF0, 0); // for VT1211 + /* Int Init */ + EnRXSpecInt(iobase, ON); + + /* The following is basically hwreset */ + /* If this is the case, why not just call hwreset() ? Jean II */ + ResetChip(iobase, 5); + EnableDMA(iobase, OFF); + EnableTX(iobase, OFF); + EnableRX(iobase, OFF); + EnRXDMA(iobase, OFF); + EnTXDMA(iobase, OFF); + RXStart(iobase, OFF); + TXStart(iobase, OFF); + InitCard(iobase); + CommonInit(iobase); + SIRFilter(iobase, ON); + SetSIR(iobase, ON); + CRC16(iobase, ON); + EnTXCRC(iobase, 0); + WriteReg(iobase, I_ST_CT_0, 0x00); + SetBaudRate(iobase, 9600); + SetPulseWidth(iobase, 12); + SetSendPreambleCount(iobase, 0); + + self->io.speed = 9600; + self->st_fifo.len = 0; + + via_ircc_change_dongle_speed(iobase, self->io.speed, + self->io.dongle_id); + + WriteReg(iobase, I_ST_CT_0, 0x80); +} + +/* + * Function via_ircc_read_dongle_id (void) + * + */ +static int via_ircc_read_dongle_id(int iobase) +{ + int dongle_id = 9; /* Default to IBM */ + + IRDA_ERROR("via-ircc: dongle probing not supported, please specify dongle_id module parameter.\n"); + return dongle_id; +} + +/* + * Function via_ircc_change_dongle_speed (iobase, speed, dongle_id) + * Change speed of the attach dongle + * only implement two type of dongle currently. + */ +static void via_ircc_change_dongle_speed(int iobase, int speed, + int dongle_id) +{ + u8 mode = 0; + + /* speed is unused, as we use IsSIROn()/IsMIROn() */ + speed = speed; + + IRDA_DEBUG(1, "%s(): change_dongle_speed to %d for 0x%x, %d\n", + __FUNCTION__, speed, iobase, dongle_id); + + switch (dongle_id) { + + /* Note: The dongle_id's listed here are derived from + * nsc-ircc.c */ + + case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ + UseOneRX(iobase, ON); // use one RX pin RX1,RX2 + InvertTX(iobase, OFF); + InvertRX(iobase, OFF); + + EnRX2(iobase, ON); //sir to rx2 + EnGPIOtoRX2(iobase, OFF); + + if (IsSIROn(iobase)) { //sir + // Mode select Off + SlowIRRXLowActive(iobase, ON); + udelay(1000); + SlowIRRXLowActive(iobase, OFF); + } else { + if (IsMIROn(iobase)) { //mir + // Mode select On + SlowIRRXLowActive(iobase, OFF); + udelay(20); + } else { // fir + if (IsFIROn(iobase)) { //fir + // Mode select On + SlowIRRXLowActive(iobase, OFF); + udelay(20); + } + } + } + break; + + case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ + UseOneRX(iobase, ON); //use ONE RX....RX1 + InvertTX(iobase, OFF); + InvertRX(iobase, OFF); // invert RX pin + + EnRX2(iobase, ON); + EnGPIOtoRX2(iobase, OFF); + if (IsSIROn(iobase)) { //sir + // Mode select On + SlowIRRXLowActive(iobase, ON); + udelay(20); + // Mode select Off + SlowIRRXLowActive(iobase, OFF); + } + if (IsMIROn(iobase)) { //mir + // Mode select On + SlowIRRXLowActive(iobase, OFF); + udelay(20); + // Mode select Off + SlowIRRXLowActive(iobase, ON); + } else { // fir + if (IsFIROn(iobase)) { //fir + // Mode select On + SlowIRRXLowActive(iobase, OFF); + // TX On + WriteTX(iobase, ON); + udelay(20); + // Mode select OFF + SlowIRRXLowActive(iobase, ON); + udelay(20); + // TX Off + WriteTX(iobase, OFF); + } + } + break; + + case 0x0d: + UseOneRX(iobase, OFF); // use two RX pin RX1,RX2 + InvertTX(iobase, OFF); + InvertRX(iobase, OFF); + SlowIRRXLowActive(iobase, OFF); + if (IsSIROn(iobase)) { //sir + EnGPIOtoRX2(iobase, OFF); + WriteGIO(iobase, OFF); + EnRX2(iobase, OFF); //sir to rx2 + } else { // fir mir + EnGPIOtoRX2(iobase, OFF); + WriteGIO(iobase, OFF); + EnRX2(iobase, OFF); //fir to rx + } + break; + + case 0x11: /* Temic TFDS4500 */ + + IRDA_DEBUG(2, "%s: Temic TFDS4500: One RX pin, TX normal, RX inverted.\n", __FUNCTION__); + + UseOneRX(iobase, ON); //use ONE RX....RX1 + InvertTX(iobase, OFF); + InvertRX(iobase, ON); // invert RX pin + + EnRX2(iobase, ON); //sir to rx2 + EnGPIOtoRX2(iobase, OFF); + + if( IsSIROn(iobase) ){ //sir + + // Mode select On + SlowIRRXLowActive(iobase, ON); + udelay(20); + // Mode select Off + SlowIRRXLowActive(iobase, OFF); + + } else{ + IRDA_DEBUG(0, "%s: Warning: TFDS4500 not running in SIR mode !\n", __FUNCTION__); + } + break; + + case 0x0ff: /* Vishay */ + if (IsSIROn(iobase)) + mode = 0; + else if (IsMIROn(iobase)) + mode = 1; + else if (IsFIROn(iobase)) + mode = 2; + else if (IsVFIROn(iobase)) + mode = 5; //VFIR-16 + SI_SetMode(iobase, mode); + break; + + default: + IRDA_ERROR("%s: Error: dongle_id %d unsupported !\n", + __FUNCTION__, dongle_id); + } +} + +/* + * Function via_ircc_change_speed (self, baud) + * + * Change the speed of the device + * + */ +static void via_ircc_change_speed(struct via_ircc_cb *self, __u32 speed) +{ + struct net_device *dev = self->netdev; + u16 iobase; + u8 value = 0, bTmp; + + iobase = self->io.fir_base; + /* Update accounting for new speed */ + self->io.speed = speed; + IRDA_DEBUG(1, "%s: change_speed to %d bps.\n", __FUNCTION__, speed); + + WriteReg(iobase, I_ST_CT_0, 0x0); + + /* Controller mode sellection */ + switch (speed) { + case 2400: + case 9600: + case 19200: + case 38400: + case 57600: + case 115200: + value = (115200/speed)-1; + SetSIR(iobase, ON); + CRC16(iobase, ON); + break; + case 576000: + /* FIXME: this can't be right, as it's the same as 115200, + * and 576000 is MIR, not SIR. */ + value = 0; + SetSIR(iobase, ON); + CRC16(iobase, ON); + break; + case 1152000: + value = 0; + SetMIR(iobase, ON); + /* FIXME: CRC ??? */ + break; + case 4000000: + value = 0; + SetFIR(iobase, ON); + SetPulseWidth(iobase, 0); + SetSendPreambleCount(iobase, 14); + CRC16(iobase, OFF); + EnTXCRC(iobase, ON); + break; + case 16000000: + value = 0; + SetVFIR(iobase, ON); + /* FIXME: CRC ??? */ + break; + default: + value = 0; + break; + } + + /* Set baudrate to 0x19[2..7] */ + bTmp = (ReadReg(iobase, I_CF_H_1) & 0x03); + bTmp |= value << 2; + WriteReg(iobase, I_CF_H_1, bTmp); + + /* Some dongles may need to be informed about speed changes. */ + via_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id); + + /* Set FIFO size to 64 */ + SetFIFO(iobase, 64); + + /* Enable IR */ + WriteReg(iobase, I_ST_CT_0, 0x80); + + // EnTXFIFOHalfLevelInt(iobase,ON); + + /* Enable some interrupts so we can receive frames */ + //EnAllInt(iobase,ON); + + if (IsSIROn(iobase)) { + SIRFilter(iobase, ON); + SIRRecvAny(iobase, ON); + } else { + SIRFilter(iobase, OFF); + SIRRecvAny(iobase, OFF); + } + + if (speed > 115200) { + /* Install FIR xmit handler */ + dev->hard_start_xmit = via_ircc_hard_xmit_fir; + via_ircc_dma_receive(self); + } else { + /* Install SIR xmit handler */ + dev->hard_start_xmit = via_ircc_hard_xmit_sir; + } + netif_wake_queue(dev); +} + +/* + * Function via_ircc_hard_xmit (skb, dev) + * + * Transmit the frame! + * + */ +static int via_ircc_hard_xmit_sir(struct sk_buff *skb, + struct net_device *dev) +{ + struct via_ircc_cb *self; + unsigned long flags; + u16 iobase; + __u32 speed; + + self = (struct via_ircc_cb *) dev->priv; + IRDA_ASSERT(self != NULL, return 0;); + iobase = self->io.fir_base; + + netif_stop_queue(dev); + /* 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) { + via_ircc_change_speed(self, speed); + dev->trans_start = jiffies; + dev_kfree_skb(skb); + return 0; + } else + self->new_speed = speed; + } + InitCard(iobase); + CommonInit(iobase); + SIRFilter(iobase, ON); + SetSIR(iobase, ON); + CRC16(iobase, ON); + EnTXCRC(iobase, 0); + WriteReg(iobase, I_ST_CT_0, 0x00); + + spin_lock_irqsave(&self->lock, flags); + self->tx_buff.data = self->tx_buff.head; + self->tx_buff.len = + async_wrap_skb(skb, self->tx_buff.data, + self->tx_buff.truesize); + + self->stats.tx_bytes += self->tx_buff.len; + /* Send this frame with old speed */ + SetBaudRate(iobase, self->io.speed); + SetPulseWidth(iobase, 12); + SetSendPreambleCount(iobase, 0); + WriteReg(iobase, I_ST_CT_0, 0x80); + + EnableTX(iobase, ON); + EnableRX(iobase, OFF); + + ResetChip(iobase, 0); + ResetChip(iobase, 1); + ResetChip(iobase, 2); + ResetChip(iobase, 3); + ResetChip(iobase, 4); + + EnAllInt(iobase, ON); + EnTXDMA(iobase, ON); + EnRXDMA(iobase, OFF); + + irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len, + DMA_TX_MODE); + + SetSendByte(iobase, self->tx_buff.len); + RXStart(iobase, OFF); + TXStart(iobase, ON); + + dev->trans_start = jiffies; + spin_unlock_irqrestore(&self->lock, flags); + dev_kfree_skb(skb); + return 0; +} + +static int via_ircc_hard_xmit_fir(struct sk_buff *skb, + struct net_device *dev) +{ + struct via_ircc_cb *self; + u16 iobase; + __u32 speed; + unsigned long flags; + + self = (struct via_ircc_cb *) dev->priv; + iobase = self->io.fir_base; + + if (self->st_fifo.len) + return 0; + if (self->chip_id == 0x3076) + iodelay(1500); + else + udelay(1500); + netif_stop_queue(dev); + speed = irda_get_next_speed(skb); + if ((speed != self->io.speed) && (speed != -1)) { + if (!skb->len) { + via_ircc_change_speed(self, speed); + dev->trans_start = jiffies; + dev_kfree_skb(skb); + return 0; + } else + self->new_speed = speed; + } + spin_lock_irqsave(&self->lock, flags); + self->tx_fifo.queue[self->tx_fifo.free].start = self->tx_fifo.tail; + self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; + + self->tx_fifo.tail += skb->len; + self->stats.tx_bytes += skb->len; + memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, + skb->len); + self->tx_fifo.len++; + self->tx_fifo.free++; +//F01 if (self->tx_fifo.len == 1) { + via_ircc_dma_xmit(self, iobase); +//F01 } +//F01 if (self->tx_fifo.free < (MAX_TX_WINDOW -1 )) netif_wake_queue(self->netdev); + dev->trans_start = jiffies; + dev_kfree_skb(skb); + spin_unlock_irqrestore(&self->lock, flags); + return 0; + +} + +static int via_ircc_dma_xmit(struct via_ircc_cb *self, u16 iobase) +{ + EnTXDMA(iobase, OFF); + self->io.direction = IO_XMIT; + EnPhys(iobase, ON); + EnableTX(iobase, ON); + EnableRX(iobase, OFF); + ResetChip(iobase, 0); + ResetChip(iobase, 1); + ResetChip(iobase, 2); + ResetChip(iobase, 3); + ResetChip(iobase, 4); + EnAllInt(iobase, ON); + EnTXDMA(iobase, ON); + EnRXDMA(iobase, OFF); + irda_setup_dma(self->io.dma, + ((u8 *)self->tx_fifo.queue[self->tx_fifo.ptr].start - + self->tx_buff.head) + self->tx_buff_dma, + self->tx_fifo.queue[self->tx_fifo.ptr].len, DMA_TX_MODE); + IRDA_DEBUG(1, "%s: tx_fifo.ptr=%x,len=%x,tx_fifo.len=%x..\n", + __FUNCTION__, self->tx_fifo.ptr, + self->tx_fifo.queue[self->tx_fifo.ptr].len, + self->tx_fifo.len); + + SetSendByte(iobase, self->tx_fifo.queue[self->tx_fifo.ptr].len); + RXStart(iobase, OFF); + TXStart(iobase, ON); + return 0; + +} + +/* + * Function via_ircc_dma_xmit_complete (self) + * + * The transfer of a frame in finished. This function will only be called + * by the interrupt handler + * + */ +static int via_ircc_dma_xmit_complete(struct via_ircc_cb *self) +{ + int iobase; + int ret = TRUE; + u8 Tx_status; + + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + iobase = self->io.fir_base; + /* Disable DMA */ +// DisableDmaChannel(self->io.dma); + /* Check for underrrun! */ + /* Clear bit, by writing 1 into it */ + Tx_status = GetTXStatus(iobase); + if (Tx_status & 0x08) { + self->stats.tx_errors++; + self->stats.tx_fifo_errors++; + hwreset(self); +// how to clear underrrun ? + } else { + self->stats.tx_packets++; + ResetChip(iobase, 3); + ResetChip(iobase, 4); + } + /* Check if we need to change the speed */ + if (self->new_speed) { + via_ircc_change_speed(self, self->new_speed); + self->new_speed = 0; + } + + /* Finished with this frame, so prepare for next */ + if (IsFIROn(iobase)) { + if (self->tx_fifo.len) { + self->tx_fifo.len--; + self->tx_fifo.ptr++; + } + } + IRDA_DEBUG(1, + "%s: tx_fifo.len=%x ,tx_fifo.ptr=%x,tx_fifo.free=%x...\n", + __FUNCTION__, + self->tx_fifo.len, self->tx_fifo.ptr, self->tx_fifo.free); +/* F01_S + // Any frames to be sent back-to-back? + if (self->tx_fifo.len) { + // Not finished yet! + via_ircc_dma_xmit(self, iobase); + ret = FALSE; + } else { +F01_E*/ + // Reset Tx FIFO info + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; +//F01 } + + // Make sure we have room for more frames +//F01 if (self->tx_fifo.free < (MAX_TX_WINDOW -1 )) { + // Not busy transmitting anymore + // Tell the network layer, that we can accept more frames + netif_wake_queue(self->netdev); +//F01 } + return ret; +} + +/* + * Function via_ircc_dma_receive (self) + * + * Set configuration for receive a frame. + * + */ +static int via_ircc_dma_receive(struct via_ircc_cb *self) +{ + int iobase; + + iobase = self->io.fir_base; + + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; + self->RxDataReady = 0; + self->io.direction = IO_RECV; + self->rx_buff.data = self->rx_buff.head; + self->st_fifo.len = self->st_fifo.pending_bytes = 0; + self->st_fifo.tail = self->st_fifo.head = 0; + + EnPhys(iobase, ON); + EnableTX(iobase, OFF); + EnableRX(iobase, ON); + + ResetChip(iobase, 0); + ResetChip(iobase, 1); + ResetChip(iobase, 2); + ResetChip(iobase, 3); + ResetChip(iobase, 4); + + EnAllInt(iobase, ON); + EnTXDMA(iobase, OFF); + EnRXDMA(iobase, ON); + irda_setup_dma(self->io.dma2, self->rx_buff_dma, + self->rx_buff.truesize, DMA_RX_MODE); + TXStart(iobase, OFF); + RXStart(iobase, ON); + + return 0; +} + +/* + * Function via_ircc_dma_receive_complete (self) + * + * Controller Finished with receiving frames, + * and this routine is call by ISR + * + */ +static int via_ircc_dma_receive_complete(struct via_ircc_cb *self, + int iobase) +{ + struct st_fifo *st_fifo; + struct sk_buff *skb; + int len, i; + u8 status = 0; + + iobase = self->io.fir_base; + st_fifo = &self->st_fifo; + + if (self->io.speed < 4000000) { //Speed below FIR + len = GetRecvByte(iobase, self); + skb = dev_alloc_skb(len + 1); + if (skb == NULL) + return FALSE; + // Make sure IP header gets aligned + skb_reserve(skb, 1); + skb_put(skb, len - 2); + if (self->chip_id == 0x3076) { + for (i = 0; i < len - 2; i++) + skb->data[i] = self->rx_buff.data[i * 2]; + } else { + if (self->chip_id == 0x3096) { + for (i = 0; i < len - 2; i++) + skb->data[i] = + self->rx_buff.data[i]; + } + } + // Move to next frame + self->rx_buff.data += len; + self->stats.rx_bytes += len; + self->stats.rx_packets++; + skb->dev = self->netdev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + return TRUE; + } + + else { //FIR mode + len = GetRecvByte(iobase, self); + if (len == 0) + return TRUE; //interrupt only, data maybe move by RxT + if (((len - 4) < 2) || ((len - 4) > 2048)) { + IRDA_DEBUG(1, "%s(): Trouble:len=%x,CurCount=%x,LastCount=%x..\n", + __FUNCTION__, len, RxCurCount(iobase, self), + self->RxLastCount); + hwreset(self); + return FALSE; + } + IRDA_DEBUG(2, "%s(): fifo.len=%x,len=%x,CurCount=%x..\n", + __FUNCTION__, + st_fifo->len, len - 4, RxCurCount(iobase, self)); + + st_fifo->entries[st_fifo->tail].status = status; + st_fifo->entries[st_fifo->tail].len = len; + st_fifo->pending_bytes += len; + st_fifo->tail++; + st_fifo->len++; + if (st_fifo->tail > MAX_RX_WINDOW) + st_fifo->tail = 0; + self->RxDataReady = 0; + + // It maybe have MAX_RX_WINDOW package receive by + // receive_complete before Timer IRQ +/* F01_S + if (st_fifo->len < (MAX_RX_WINDOW+2 )) { + RXStart(iobase,ON); + SetTimer(iobase,4); + } + else { +F01_E */ + EnableRX(iobase, OFF); + EnRXDMA(iobase, OFF); + RXStart(iobase, OFF); +//F01_S + // Put this entry back in fifo + if (st_fifo->head > MAX_RX_WINDOW) + st_fifo->head = 0; + status = st_fifo->entries[st_fifo->head].status; + len = st_fifo->entries[st_fifo->head].len; + st_fifo->head++; + st_fifo->len--; + + skb = dev_alloc_skb(len + 1 - 4); + /* + * if frame size,data ptr,or skb ptr are wrong ,the get next + * entry. + */ + if ((skb == NULL) || (skb->data == NULL) + || (self->rx_buff.data == NULL) || (len < 6)) { + self->stats.rx_dropped++; + return TRUE; + } + skb_reserve(skb, 1); + skb_put(skb, len - 4); + + memcpy(skb->data, self->rx_buff.data, len - 4); + IRDA_DEBUG(2, "%s(): len=%x.rx_buff=%p\n", __FUNCTION__, + len - 4, self->rx_buff.data); + + // Move to next frame + self->rx_buff.data += len; + self->stats.rx_bytes += len; + self->stats.rx_packets++; + skb->dev = self->netdev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + +//F01_E + } //FIR + return TRUE; + +} + +/* + * if frame is received , but no INT ,then use this routine to upload frame. + */ +static int upload_rxdata(struct via_ircc_cb *self, int iobase) +{ + struct sk_buff *skb; + int len; + struct st_fifo *st_fifo; + st_fifo = &self->st_fifo; + + len = GetRecvByte(iobase, self); + + IRDA_DEBUG(2, "%s(): len=%x\n", __FUNCTION__, len); + + skb = dev_alloc_skb(len + 1); + if ((skb == NULL) || ((len - 4) < 2)) { + self->stats.rx_dropped++; + return FALSE; + } + skb_reserve(skb, 1); + skb_put(skb, len - 4 + 1); + memcpy(skb->data, self->rx_buff.data, len - 4 + 1); + st_fifo->tail++; + st_fifo->len++; + if (st_fifo->tail > MAX_RX_WINDOW) + st_fifo->tail = 0; + // Move to next frame + self->rx_buff.data += len; + self->stats.rx_bytes += len; + self->stats.rx_packets++; + skb->dev = self->netdev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + if (st_fifo->len < (MAX_RX_WINDOW + 2)) { + RXStart(iobase, ON); + } else { + EnableRX(iobase, OFF); + EnRXDMA(iobase, OFF); + RXStart(iobase, OFF); + } + return TRUE; +} + +/* + * Implement back to back receive , use this routine to upload data. + */ + +static int RxTimerHandler(struct via_ircc_cb *self, int iobase) +{ + struct st_fifo *st_fifo; + struct sk_buff *skb; + int len; + u8 status; + + st_fifo = &self->st_fifo; + + if (CkRxRecv(iobase, self)) { + // if still receiving ,then return ,don't upload frame + self->RetryCount = 0; + SetTimer(iobase, 20); + self->RxDataReady++; + return FALSE; + } else + self->RetryCount++; + + if ((self->RetryCount >= 1) || + ((st_fifo->pending_bytes + 2048) > self->rx_buff.truesize) + || (st_fifo->len >= (MAX_RX_WINDOW))) { + while (st_fifo->len > 0) { //upload frame + // Put this entry back in fifo + if (st_fifo->head > MAX_RX_WINDOW) + st_fifo->head = 0; + status = st_fifo->entries[st_fifo->head].status; + len = st_fifo->entries[st_fifo->head].len; + st_fifo->head++; + st_fifo->len--; + + skb = dev_alloc_skb(len + 1 - 4); + /* + * if frame size, data ptr, or skb ptr are wrong, + * then get next entry. + */ + if ((skb == NULL) || (skb->data == NULL) + || (self->rx_buff.data == NULL) || (len < 6)) { + self->stats.rx_dropped++; + continue; + } + skb_reserve(skb, 1); + skb_put(skb, len - 4); + memcpy(skb->data, self->rx_buff.data, len - 4); + + IRDA_DEBUG(2, "%s(): len=%x.head=%x\n", __FUNCTION__, + len - 4, st_fifo->head); + + // Move to next frame + self->rx_buff.data += len; + self->stats.rx_bytes += len; + self->stats.rx_packets++; + skb->dev = self->netdev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + } //while + self->RetryCount = 0; + + IRDA_DEBUG(2, + "%s(): End of upload HostStatus=%x,RxStatus=%x\n", + __FUNCTION__, + GetHostStatus(iobase), GetRXStatus(iobase)); + + /* + * if frame is receive complete at this routine ,then upload + * frame. + */ + if ((GetRXStatus(iobase) & 0x10) + && (RxCurCount(iobase, self) != self->RxLastCount)) { + upload_rxdata(self, iobase); + if (irda_device_txqueue_empty(self->netdev)) + via_ircc_dma_receive(self); + } + } // timer detect complete + else + SetTimer(iobase, 4); + return TRUE; + +} + + + +/* + * Function via_ircc_interrupt (irq, dev_id, regs) + * + * An interrupt from the chip has arrived. Time to do some work + * + */ +static irqreturn_t via_ircc_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct via_ircc_cb *self; + int iobase; + u8 iHostIntType, iRxIntType, iTxIntType; + + if (!dev) { + IRDA_WARNING("%s: irq %d for unknown device.\n", driver_name, + irq); + return IRQ_NONE; + } + self = (struct via_ircc_cb *) dev->priv; + iobase = self->io.fir_base; + spin_lock(&self->lock); + iHostIntType = GetHostStatus(iobase); + + IRDA_DEBUG(4, "%s(): iHostIntType %02x: %s %s %s %02x\n", + __FUNCTION__, iHostIntType, + (iHostIntType & 0x40) ? "Timer" : "", + (iHostIntType & 0x20) ? "Tx" : "", + (iHostIntType & 0x10) ? "Rx" : "", + (iHostIntType & 0x0e) >> 1); + + if ((iHostIntType & 0x40) != 0) { //Timer Event + self->EventFlag.TimeOut++; + ClearTimerInt(iobase, 1); + if (self->io.direction == IO_XMIT) { + via_ircc_dma_xmit(self, iobase); + } + if (self->io.direction == IO_RECV) { + /* + * frame ready hold too long, must reset. + */ + if (self->RxDataReady > 30) { + hwreset(self); + if (irda_device_txqueue_empty(self->netdev)) { + via_ircc_dma_receive(self); + } + } else { // call this to upload frame. + RxTimerHandler(self, iobase); + } + } //RECV + } //Timer Event + if ((iHostIntType & 0x20) != 0) { //Tx Event + iTxIntType = GetTXStatus(iobase); + + IRDA_DEBUG(4, "%s(): iTxIntType %02x: %s %s %s %s\n", + __FUNCTION__, iTxIntType, + (iTxIntType & 0x08) ? "FIFO underr." : "", + (iTxIntType & 0x04) ? "EOM" : "", + (iTxIntType & 0x02) ? "FIFO ready" : "", + (iTxIntType & 0x01) ? "Early EOM" : ""); + + if (iTxIntType & 0x4) { + self->EventFlag.EOMessage++; // read and will auto clean + if (via_ircc_dma_xmit_complete(self)) { + if (irda_device_txqueue_empty + (self->netdev)) { + via_ircc_dma_receive(self); + } + } else { + self->EventFlag.Unknown++; + } + } //EOP + } //Tx Event + //---------------------------------------- + if ((iHostIntType & 0x10) != 0) { //Rx Event + /* Check if DMA has finished */ + iRxIntType = GetRXStatus(iobase); + + IRDA_DEBUG(4, "%s(): iRxIntType %02x: %s %s %s %s %s %s %s\n", + __FUNCTION__, iRxIntType, + (iRxIntType & 0x80) ? "PHY err." : "", + (iRxIntType & 0x40) ? "CRC err" : "", + (iRxIntType & 0x20) ? "FIFO overr." : "", + (iRxIntType & 0x10) ? "EOF" : "", + (iRxIntType & 0x08) ? "RxData" : "", + (iRxIntType & 0x02) ? "RxMaxLen" : "", + (iRxIntType & 0x01) ? "SIR bad" : ""); + if (!iRxIntType) + IRDA_DEBUG(3, "%s(): RxIRQ =0\n", __FUNCTION__); + + if (iRxIntType & 0x10) { + if (via_ircc_dma_receive_complete(self, iobase)) { +//F01 if(!(IsFIROn(iobase))) via_ircc_dma_receive(self); + via_ircc_dma_receive(self); + } + } // No ERR + else { //ERR + IRDA_DEBUG(4, "%s(): RxIRQ ERR:iRxIntType=%x,HostIntType=%x,CurCount=%x,RxLastCount=%x_____\n", + __FUNCTION__, iRxIntType, iHostIntType, + RxCurCount(iobase, self), + self->RxLastCount); + + if (iRxIntType & 0x20) { //FIFO OverRun ERR + ResetChip(iobase, 0); + ResetChip(iobase, 1); + } else { //PHY,CRC ERR + + if (iRxIntType != 0x08) + hwreset(self); //F01 + } + via_ircc_dma_receive(self); + } //ERR + + } //Rx Event + spin_unlock(&self->lock); + return IRQ_RETVAL(iHostIntType); +} + +static void hwreset(struct via_ircc_cb *self) +{ + int iobase; + iobase = self->io.fir_base; + + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + ResetChip(iobase, 5); + EnableDMA(iobase, OFF); + EnableTX(iobase, OFF); + EnableRX(iobase, OFF); + EnRXDMA(iobase, OFF); + EnTXDMA(iobase, OFF); + RXStart(iobase, OFF); + TXStart(iobase, OFF); + InitCard(iobase); + CommonInit(iobase); + SIRFilter(iobase, ON); + SetSIR(iobase, ON); + CRC16(iobase, ON); + EnTXCRC(iobase, 0); + WriteReg(iobase, I_ST_CT_0, 0x00); + SetBaudRate(iobase, 9600); + SetPulseWidth(iobase, 12); + SetSendPreambleCount(iobase, 0); + WriteReg(iobase, I_ST_CT_0, 0x80); + + /* Restore speed. */ + via_ircc_change_speed(self, self->io.speed); + + self->st_fifo.len = 0; +} + +/* + * Function via_ircc_is_receiving (self) + * + * Return TRUE is we are currently receiving a frame + * + */ +static int via_ircc_is_receiving(struct via_ircc_cb *self) +{ + int status = FALSE; + int iobase; + + IRDA_ASSERT(self != NULL, return FALSE;); + + iobase = self->io.fir_base; + if (CkRxRecv(iobase, self)) + status = TRUE; + + IRDA_DEBUG(2, "%s(): status=%x....\n", __FUNCTION__, status); + + return status; +} + + +/* + * Function via_ircc_net_open (dev) + * + * Start the device + * + */ +static int via_ircc_net_open(struct net_device *dev) +{ + struct via_ircc_cb *self; + int iobase; + char hwname[32]; + + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + IRDA_ASSERT(dev != NULL, return -1;); + self = (struct via_ircc_cb *) dev->priv; + self->stats.rx_packets = 0; + IRDA_ASSERT(self != NULL, return 0;); + iobase = self->io.fir_base; + if (request_irq(self->io.irq, via_ircc_interrupt, 0, dev->name, dev)) { + IRDA_WARNING("%s, unable to allocate irq=%d\n", driver_name, + self->io.irq); + return -EAGAIN; + } + /* + * Always allocate the DMA channel after the IRQ, and clean up on + * failure. + */ + if (request_dma(self->io.dma, dev->name)) { + IRDA_WARNING("%s, unable to allocate dma=%d\n", driver_name, + self->io.dma); + free_irq(self->io.irq, self); + return -EAGAIN; + } + if (self->io.dma2 != self->io.dma) { + if (request_dma(self->io.dma2, dev->name)) { + IRDA_WARNING("%s, unable to allocate dma2=%d\n", + driver_name, self->io.dma2); + free_irq(self->io.irq, self); + return -EAGAIN; + } + } + + + /* turn on interrupts */ + EnAllInt(iobase, ON); + EnInternalLoop(iobase, OFF); + EnExternalLoop(iobase, OFF); + + /* */ + via_ircc_dma_receive(self); + + /* Ready to play! */ + netif_start_queue(dev); + + /* + * Open new IrLAP layer instance, now that everything should be + * initialized properly + */ + sprintf(hwname, "VIA @ 0x%x", iobase); + self->irlap = irlap_open(dev, &self->qos, hwname); + + self->RxLastCount = 0; + + return 0; +} + +/* + * Function via_ircc_net_close (dev) + * + * Stop the device + * + */ +static int via_ircc_net_close(struct net_device *dev) +{ + struct via_ircc_cb *self; + int iobase; + + IRDA_DEBUG(3, "%s()\n", __FUNCTION__); + + IRDA_ASSERT(dev != NULL, return -1;); + self = (struct via_ircc_cb *) dev->priv; + IRDA_ASSERT(self != NULL, return 0;); + + /* Stop device */ + netif_stop_queue(dev); + /* Stop and remove instance of IrLAP */ + if (self->irlap) + irlap_close(self->irlap); + self->irlap = NULL; + iobase = self->io.fir_base; + EnTXDMA(iobase, OFF); + EnRXDMA(iobase, OFF); + DisableDmaChannel(self->io.dma); + + /* Disable interrupts */ + EnAllInt(iobase, OFF); + free_irq(self->io.irq, dev); + free_dma(self->io.dma); + + return 0; +} + +/* + * Function via_ircc_net_ioctl (dev, rq, cmd) + * + * Process IOCTL commands for this device + * + */ +static int via_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, + int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct via_ircc_cb *self; + unsigned long flags; + int ret = 0; + + IRDA_ASSERT(dev != NULL, return -1;); + self = dev->priv; + IRDA_ASSERT(self != NULL, return -1;); + IRDA_DEBUG(1, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, + cmd); + /* Disable interrupts & save flags */ + spin_lock_irqsave(&self->lock, flags); + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + goto out; + } + via_ircc_change_speed(self, irq->ifr_baudrate); + break; + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + goto out; + } + irda_device_set_media_busy(self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + irq->ifr_receiving = via_ircc_is_receiving(self); + break; + default: + ret = -EOPNOTSUPP; + } + out: + spin_unlock_irqrestore(&self->lock, flags); + return ret; +} + +static struct net_device_stats *via_ircc_net_get_stats(struct net_device + *dev) +{ + struct via_ircc_cb *self = (struct via_ircc_cb *) dev->priv; + + return &self->stats; +} + +MODULE_AUTHOR("VIA Technologies,inc"); +MODULE_DESCRIPTION("VIA IrDA Device Driver"); +MODULE_LICENSE("GPL"); + +module_init(via_ircc_init); +module_exit(via_ircc_cleanup); diff --git a/drivers/net/irda/via-ircc.h b/drivers/net/irda/via-ircc.h new file mode 100644 index 000000000000..204b1b34ffc7 --- /dev/null +++ b/drivers/net/irda/via-ircc.h @@ -0,0 +1,853 @@ +/********************************************************************* + * + * Filename: via-ircc.h + * Version: 1.0 + * Description: Driver for the VIA VT8231/VT8233 IrDA chipsets + * Author: VIA Technologies, inc + * Date : 08/06/2003 + +Copyright (c) 1998-2003 VIA Technologies, Inc. + +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, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTIES OR REPRESENTATIONS; 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. + + * Comment: + * jul/08/2002 : Rx buffer length should use Rx ring ptr. + * Oct/28/2002 : Add SB id for 3147 and 3177. + * jul/09/2002 : only implement two kind of dongle currently. + * Oct/02/2002 : work on VT8231 and VT8233 . + * Aug/06/2003 : change driver format to pci driver . + ********************************************************************/ +#ifndef via_IRCC_H +#define via_IRCC_H +#include <linux/time.h> +#include <linux/spinlock.h> +#include <linux/pm.h> +#include <linux/types.h> +#include <asm/io.h> + +#define MAX_TX_WINDOW 7 +#define MAX_RX_WINDOW 7 + +struct st_fifo_entry { + int status; + int len; +}; + +struct st_fifo { + struct st_fifo_entry entries[MAX_RX_WINDOW + 2]; + int pending_bytes; + int head; + int tail; + int len; +}; + +struct frame_cb { + void *start; /* Start of frame in DMA mem */ + int len; /* Lenght of frame in DMA mem */ +}; + +struct tx_fifo { + struct frame_cb queue[MAX_TX_WINDOW + 2]; /* Info about frames in queue */ + int ptr; /* Currently being sent */ + int len; /* Lenght of queue */ + int free; /* Next free slot */ + void *tail; /* Next free start in DMA mem */ +}; + + +struct eventflag // for keeping track of Interrupt Events +{ + //--------tx part + unsigned char TxFIFOUnderRun; + unsigned char EOMessage; + unsigned char TxFIFOReady; + unsigned char EarlyEOM; + //--------rx part + unsigned char PHYErr; + unsigned char CRCErr; + unsigned char RxFIFOOverRun; + unsigned char EOPacket; + unsigned char RxAvail; + unsigned char TooLargePacket; + unsigned char SIRBad; + //--------unknown + unsigned char Unknown; + //---------- + unsigned char TimeOut; + unsigned char RxDMATC; + unsigned char TxDMATC; +}; + +/* Private data for each instance */ +struct via_ircc_cb { + struct st_fifo st_fifo; /* Info about received frames */ + struct tx_fifo tx_fifo; /* Info about frames to be transmitted */ + + 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 */ + struct qos_info qos; /* QoS capabilities for this device */ + + chipio_t io; /* IrDA controller information */ + iobuff_t tx_buff; /* Transmit buffer */ + iobuff_t rx_buff; /* Receive buffer */ + dma_addr_t tx_buff_dma; + dma_addr_t rx_buff_dma; + + __u8 ier; /* Interrupt enable register */ + + struct timeval stamp; + struct timeval now; + + spinlock_t lock; /* For serializing operations */ + + __u32 flags; /* Interface flags */ + __u32 new_speed; + int index; /* Instance index */ + + struct eventflag EventFlag; + struct pm_dev *dev; + unsigned int chip_id; /* to remember chip id */ + unsigned int RetryCount; + unsigned int RxDataReady; + unsigned int RxLastCount; +}; + + +//---------I=Infrared, H=Host, M=Misc, T=Tx, R=Rx, ST=Status, +// CF=Config, CT=Control, L=Low, H=High, C=Count +#define I_CF_L_0 0x10 +#define I_CF_H_0 0x11 +#define I_SIR_BOF 0x12 +#define I_SIR_EOF 0x13 +#define I_ST_CT_0 0x15 +#define I_ST_L_1 0x16 +#define I_ST_H_1 0x17 +#define I_CF_L_1 0x18 +#define I_CF_H_1 0x19 +#define I_CF_L_2 0x1a +#define I_CF_H_2 0x1b +#define I_CF_3 0x1e +#define H_CT 0x20 +#define H_ST 0x21 +#define M_CT 0x22 +#define TX_CT_1 0x23 +#define TX_CT_2 0x24 +#define TX_ST 0x25 +#define RX_CT 0x26 +#define RX_ST 0x27 +#define RESET 0x28 +#define P_ADDR 0x29 +#define RX_C_L 0x2a +#define RX_C_H 0x2b +#define RX_P_L 0x2c +#define RX_P_H 0x2d +#define TX_C_L 0x2e +#define TX_C_H 0x2f +#define TIMER 0x32 +#define I_CF_4 0x33 +#define I_T_C_L 0x34 +#define I_T_C_H 0x35 +#define VERSION 0x3f +//------------------------------- +#define StartAddr 0x10 // the first register address +#define EndAddr 0x3f // the last register address +#define GetBit(val,bit) val = (unsigned char) ((val>>bit) & 0x1) + // Returns the bit +#define SetBit(val,bit) val= (unsigned char ) (val | (0x1 << bit)) + // Sets bit to 1 +#define ResetBit(val,bit) val= (unsigned char ) (val & ~(0x1 << bit)) + // Sets bit to 0 + +#define OFF 0 +#define ON 1 +#define DMA_TX_MODE 0x08 +#define DMA_RX_MODE 0x04 + +#define DMA1 0 +#define DMA2 0xc0 +#define MASK1 DMA1+0x0a +#define MASK2 DMA2+0x14 + +#define Clk_bit 0x40 +#define Tx_bit 0x01 +#define Rd_Valid 0x08 +#define RxBit 0x08 + +static void DisableDmaChannel(unsigned int channel) +{ + switch (channel) { // 8 Bit DMA channels DMAC1 + case 0: + outb(4, MASK1); //mask channel 0 + break; + case 1: + outb(5, MASK1); //Mask channel 1 + break; + case 2: + outb(6, MASK1); //Mask channel 2 + break; + case 3: + outb(7, MASK1); //Mask channel 3 + break; + case 5: + outb(5, MASK2); //Mask channel 5 + break; + case 6: + outb(6, MASK2); //Mask channel 6 + break; + case 7: + outb(7, MASK2); //Mask channel 7 + break; + default: + break; + }; //Switch +} + +static unsigned char ReadLPCReg(int iRegNum) +{ + unsigned char iVal; + + outb(0x87, 0x2e); + outb(0x87, 0x2e); + outb(iRegNum, 0x2e); + iVal = inb(0x2f); + outb(0xaa, 0x2e); + + return iVal; +} + +static void WriteLPCReg(int iRegNum, unsigned char iVal) +{ + + outb(0x87, 0x2e); + outb(0x87, 0x2e); + outb(iRegNum, 0x2e); + outb(iVal, 0x2f); + outb(0xAA, 0x2e); +} + +static __u8 ReadReg(unsigned int BaseAddr, int iRegNum) +{ + return ((__u8) inb(BaseAddr + iRegNum)); +} + +static void WriteReg(unsigned int BaseAddr, int iRegNum, unsigned char iVal) +{ + outb(iVal, BaseAddr + iRegNum); +} + +static int WriteRegBit(unsigned int BaseAddr, unsigned char RegNum, + unsigned char BitPos, unsigned char value) +{ + __u8 Rtemp, Wtemp; + + if (BitPos > 7) { + return -1; + } + if ((RegNum < StartAddr) || (RegNum > EndAddr)) + return -1; + Rtemp = ReadReg(BaseAddr, RegNum); + if (value == 0) + Wtemp = ResetBit(Rtemp, BitPos); + else { + if (value == 1) + Wtemp = SetBit(Rtemp, BitPos); + else + return -1; + } + WriteReg(BaseAddr, RegNum, Wtemp); + return 0; +} + +static __u8 CheckRegBit(unsigned int BaseAddr, unsigned char RegNum, + unsigned char BitPos) +{ + __u8 temp; + + if (BitPos > 7) + return 0xff; + if ((RegNum < StartAddr) || (RegNum > EndAddr)) { +// printf("what is the register %x!\n",RegNum); + } + temp = ReadReg(BaseAddr, RegNum); + return GetBit(temp, BitPos); +} + +static void SetMaxRxPacketSize(__u16 iobase, __u16 size) +{ + __u16 low, high; + if ((size & 0xe000) == 0) { + low = size & 0x00ff; + high = (size & 0x1f00) >> 8; + WriteReg(iobase, I_CF_L_2, low); + WriteReg(iobase, I_CF_H_2, high); + + } + +} + +//for both Rx and Tx + +static void SetFIFO(__u16 iobase, __u16 value) +{ + switch (value) { + case 128: + WriteRegBit(iobase, 0x11, 0, 0); + WriteRegBit(iobase, 0x11, 7, 1); + break; + case 64: + WriteRegBit(iobase, 0x11, 0, 0); + WriteRegBit(iobase, 0x11, 7, 0); + break; + case 32: + WriteRegBit(iobase, 0x11, 0, 1); + WriteRegBit(iobase, 0x11, 7, 0); + break; + default: + WriteRegBit(iobase, 0x11, 0, 0); + WriteRegBit(iobase, 0x11, 7, 0); + } + +} + +#define CRC16(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,7,val) //0 for 32 CRC +/* +#define SetVFIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,5,val) +#define SetFIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,6,val) +#define SetMIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,5,val) +#define SetSIR(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,4,val) +*/ +#define SIRFilter(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,3,val) +#define Filter(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,2,val) +#define InvertTX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,1,val) +#define InvertRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_L_0,0,val) +//****************************I_CF_H_0 +#define EnableTX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,4,val) +#define EnableRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,3,val) +#define EnableDMA(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,2,val) +#define SIRRecvAny(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,1,val) +#define DiableTrans(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_H_0,0,val) +//***************************I_SIR_BOF,I_SIR_EOF +#define SetSIRBOF(BaseAddr,val) WriteReg(BaseAddr,I_SIR_BOF,val) +#define SetSIREOF(BaseAddr,val) WriteReg(BaseAddr,I_SIR_EOF,val) +#define GetSIRBOF(BaseAddr) ReadReg(BaseAddr,I_SIR_BOF) +#define GetSIREOF(BaseAddr) ReadReg(BaseAddr,I_SIR_EOF) +//*******************I_ST_CT_0 +#define EnPhys(BaseAddr,val) WriteRegBit(BaseAddr,I_ST_CT_0,7,val) +#define IsModeError(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,6) //RO +#define IsVFIROn(BaseAddr) CheckRegBit(BaseAddr,0x14,0) //RO for VT1211 only +#define IsFIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,5) //RO +#define IsMIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,4) //RO +#define IsSIROn(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,3) //RO +#define IsEnableTX(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,2) //RO +#define IsEnableRX(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,1) //RO +#define Is16CRC(BaseAddr) CheckRegBit(BaseAddr,I_ST_CT_0,0) //RO +//***************************I_CF_3 +#define DisableAdjacentPulseWidth(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,5,val) //1 disable +#define DisablePulseWidthAdjust(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,4,val) //1 disable +#define UseOneRX(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,1,val) //0 use two RX +#define SlowIRRXLowActive(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_3,0,val) //0 show RX high=1 in SIR +//***************************H_CT +#define EnAllInt(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,7,val) +#define TXStart(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,6,val) +#define RXStart(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,5,val) +#define ClearRXInt(BaseAddr,val) WriteRegBit(BaseAddr,H_CT,4,val) // 1 clear +//*****************H_ST +#define IsRXInt(BaseAddr) CheckRegBit(BaseAddr,H_ST,4) +#define GetIntIndentify(BaseAddr) ((ReadReg(BaseAddr,H_ST)&0xf1) >>1) +#define IsHostBusy(BaseAddr) CheckRegBit(BaseAddr,H_ST,0) +#define GetHostStatus(BaseAddr) ReadReg(BaseAddr,H_ST) //RO +//**************************M_CT +#define EnTXDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,7,val) +#define EnRXDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,6,val) +#define SwapDMA(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,5,val) +#define EnInternalLoop(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,4,val) +#define EnExternalLoop(BaseAddr,val) WriteRegBit(BaseAddr,M_CT,3,val) +//**************************TX_CT_1 +#define EnTXFIFOHalfLevelInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,4,val) //half empty int (1 half) +#define EnTXFIFOUnderrunEOMInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,5,val) +#define EnTXFIFOReadyInt(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_1,6,val) //int when reach it threshold (setting by bit 4) +//**************************TX_CT_2 +#define ForceUnderrun(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,7,val) // force an underrun int +#define EnTXCRC(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,6,val) //1 for FIR,MIR...0 (not SIR) +#define ForceBADCRC(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,5,val) //force an bad CRC +#define SendSIP(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,4,val) //send indication pulse for prevent SIR disturb +#define ClearEnTX(BaseAddr,val) WriteRegBit(BaseAddr,TX_CT_2,3,val) // opposite to EnTX +//*****************TX_ST +#define GetTXStatus(BaseAddr) ReadReg(BaseAddr,TX_ST) //RO +//**************************RX_CT +#define EnRXSpecInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,0,val) +#define EnRXFIFOReadyInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,1,val) //enable int when reach it threshold (setting by bit 7) +#define EnRXFIFOHalfLevelInt(BaseAddr,val) WriteRegBit(BaseAddr,RX_CT,7,val) //enable int when (1) half full...or (0) just not full +//*****************RX_ST +#define GetRXStatus(BaseAddr) ReadReg(BaseAddr,RX_ST) //RO +//***********************P_ADDR +#define SetPacketAddr(BaseAddr,addr) WriteReg(BaseAddr,P_ADDR,addr) +//***********************I_CF_4 +#define EnGPIOtoRX2(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,7,val) +#define EnTimerInt(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,1,val) +#define ClearTimerInt(BaseAddr,val) WriteRegBit(BaseAddr,I_CF_4,0,val) +//***********************I_T_C_L +#define WriteGIO(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_L,7,val) +#define ReadGIO(BaseAddr) CheckRegBit(BaseAddr,I_T_C_L,7) +#define ReadRX(BaseAddr) CheckRegBit(BaseAddr,I_T_C_L,3) //RO +#define WriteTX(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_L,0,val) +//***********************I_T_C_H +#define EnRX2(BaseAddr,val) WriteRegBit(BaseAddr,I_T_C_H,7,val) +#define ReadRX2(BaseAddr) CheckRegBit(BaseAddr,I_T_C_H,7) +//**********************Version +#define GetFIRVersion(BaseAddr) ReadReg(BaseAddr,VERSION) + + +static void SetTimer(__u16 iobase, __u8 count) +{ + EnTimerInt(iobase, OFF); + WriteReg(iobase, TIMER, count); + EnTimerInt(iobase, ON); +} + + +static void SetSendByte(__u16 iobase, __u32 count) +{ + __u32 low, high; + + if ((count & 0xf000) == 0) { + low = count & 0x00ff; + high = (count & 0x0f00) >> 8; + WriteReg(iobase, TX_C_L, low); + WriteReg(iobase, TX_C_H, high); + } +} + +static void ResetChip(__u16 iobase, __u8 type) +{ + __u8 value; + + value = (type + 2) << 4; + WriteReg(iobase, RESET, type); +} + +static int CkRxRecv(__u16 iobase, struct via_ircc_cb *self) +{ + __u8 low, high; + __u16 wTmp = 0, wTmp1 = 0, wTmp_new = 0; + + low = ReadReg(iobase, RX_C_L); + high = ReadReg(iobase, RX_C_H); + wTmp1 = high; + wTmp = (wTmp1 << 8) | low; + udelay(10); + low = ReadReg(iobase, RX_C_L); + high = ReadReg(iobase, RX_C_H); + wTmp1 = high; + wTmp_new = (wTmp1 << 8) | low; + if (wTmp_new != wTmp) + return 1; + else + return 0; + +} + +static __u16 RxCurCount(__u16 iobase, struct via_ircc_cb * self) +{ + __u8 low, high; + __u16 wTmp = 0, wTmp1 = 0; + + low = ReadReg(iobase, RX_P_L); + high = ReadReg(iobase, RX_P_H); + wTmp1 = high; + wTmp = (wTmp1 << 8) | low; + return wTmp; +} + +/* This Routine can only use in recevie_complete + * for it will update last count. + */ + +static __u16 GetRecvByte(__u16 iobase, struct via_ircc_cb * self) +{ + __u8 low, high; + __u16 wTmp, wTmp1, ret; + + low = ReadReg(iobase, RX_P_L); + high = ReadReg(iobase, RX_P_H); + wTmp1 = high; + wTmp = (wTmp1 << 8) | low; + + + if (wTmp >= self->RxLastCount) + ret = wTmp - self->RxLastCount; + else + ret = (0x8000 - self->RxLastCount) + wTmp; + self->RxLastCount = wTmp; + +/* RX_P is more actually the RX_C + low=ReadReg(iobase,RX_C_L); + high=ReadReg(iobase,RX_C_H); + + if(!(high&0xe000)) { + temp=(high<<8)+low; + return temp; + } + else return 0; +*/ + return ret; +} + +static void Sdelay(__u16 scale) +{ + __u8 bTmp; + int i, j; + + for (j = 0; j < scale; j++) { + for (i = 0; i < 0x20; i++) { + bTmp = inb(0xeb); + outb(bTmp, 0xeb); + } + } +} + +static void Tdelay(__u16 scale) +{ + __u8 bTmp; + int i, j; + + for (j = 0; j < scale; j++) { + for (i = 0; i < 0x50; i++) { + bTmp = inb(0xeb); + outb(bTmp, 0xeb); + } + } +} + + +static void ActClk(__u16 iobase, __u8 value) +{ + __u8 bTmp; + bTmp = ReadReg(iobase, 0x34); + if (value) + WriteReg(iobase, 0x34, bTmp | Clk_bit); + else + WriteReg(iobase, 0x34, bTmp & ~Clk_bit); +} + +static void ClkTx(__u16 iobase, __u8 Clk, __u8 Tx) +{ + __u8 bTmp; + + bTmp = ReadReg(iobase, 0x34); + if (Clk == 0) + bTmp &= ~Clk_bit; + else { + if (Clk == 1) + bTmp |= Clk_bit; + } + WriteReg(iobase, 0x34, bTmp); + Sdelay(1); + if (Tx == 0) + bTmp &= ~Tx_bit; + else { + if (Tx == 1) + bTmp |= Tx_bit; + } + WriteReg(iobase, 0x34, bTmp); +} + +static void Wr_Byte(__u16 iobase, __u8 data) +{ + __u8 bData = data; +// __u8 btmp; + int i; + + ClkTx(iobase, 0, 1); + + Tdelay(2); + ActClk(iobase, 1); + Tdelay(1); + + for (i = 0; i < 8; i++) { //LDN + + if ((bData >> i) & 0x01) { + ClkTx(iobase, 0, 1); //bit data = 1; + } else { + ClkTx(iobase, 0, 0); //bit data = 1; + } + Tdelay(2); + Sdelay(1); + ActClk(iobase, 1); //clk hi + Tdelay(1); + } +} + +static __u8 Rd_Indx(__u16 iobase, __u8 addr, __u8 index) +{ + __u8 data = 0, bTmp, data_bit; + int i; + + bTmp = addr | (index << 1) | 0; + ClkTx(iobase, 0, 0); + Tdelay(2); + ActClk(iobase, 1); + udelay(1); + Wr_Byte(iobase, bTmp); + Sdelay(1); + ClkTx(iobase, 0, 0); + Tdelay(2); + for (i = 0; i < 10; i++) { + ActClk(iobase, 1); + Tdelay(1); + ActClk(iobase, 0); + Tdelay(1); + ClkTx(iobase, 0, 1); + Tdelay(1); + bTmp = ReadReg(iobase, 0x34); + if (!(bTmp & Rd_Valid)) + break; + } + if (!(bTmp & Rd_Valid)) { + for (i = 0; i < 8; i++) { + ActClk(iobase, 1); + Tdelay(1); + ActClk(iobase, 0); + bTmp = ReadReg(iobase, 0x34); + data_bit = 1 << i; + if (bTmp & RxBit) + data |= data_bit; + else + data &= ~data_bit; + Tdelay(2); + } + } else { + for (i = 0; i < 2; i++) { + ActClk(iobase, 1); + Tdelay(1); + ActClk(iobase, 0); + Tdelay(2); + } + bTmp = ReadReg(iobase, 0x34); + } + for (i = 0; i < 1; i++) { + ActClk(iobase, 1); + Tdelay(1); + ActClk(iobase, 0); + Tdelay(2); + } + ClkTx(iobase, 0, 0); + Tdelay(1); + for (i = 0; i < 3; i++) { + ActClk(iobase, 1); + Tdelay(1); + ActClk(iobase, 0); + Tdelay(2); + } + return data; +} + +static void Wr_Indx(__u16 iobase, __u8 addr, __u8 index, __u8 data) +{ + int i; + __u8 bTmp; + + ClkTx(iobase, 0, 0); + udelay(2); + ActClk(iobase, 1); + udelay(1); + bTmp = addr | (index << 1) | 1; + Wr_Byte(iobase, bTmp); + Wr_Byte(iobase, data); + for (i = 0; i < 2; i++) { + ClkTx(iobase, 0, 0); + Tdelay(2); + ActClk(iobase, 1); + Tdelay(1); + } + ActClk(iobase, 0); +} + +static void ResetDongle(__u16 iobase) +{ + int i; + ClkTx(iobase, 0, 0); + Tdelay(1); + for (i = 0; i < 30; i++) { + ActClk(iobase, 1); + Tdelay(1); + ActClk(iobase, 0); + Tdelay(1); + } + ActClk(iobase, 0); +} + +static void SetSITmode(__u16 iobase) +{ + + __u8 bTmp; + + bTmp = ReadLPCReg(0x28); + WriteLPCReg(0x28, bTmp | 0x10); //select ITMOFF + bTmp = ReadReg(iobase, 0x35); + WriteReg(iobase, 0x35, bTmp | 0x40); // Driver ITMOFF + WriteReg(iobase, 0x28, bTmp | 0x80); // enable All interrupt +} + +static void SI_SetMode(__u16 iobase, int mode) +{ + //__u32 dTmp; + __u8 bTmp; + + WriteLPCReg(0x28, 0x70); // S/W Reset + SetSITmode(iobase); + ResetDongle(iobase); + udelay(10); + Wr_Indx(iobase, 0x40, 0x0, 0x17); //RX ,APEN enable,Normal power + Wr_Indx(iobase, 0x40, 0x1, mode); //Set Mode + Wr_Indx(iobase, 0x40, 0x2, 0xff); //Set power to FIR VFIR > 1m + bTmp = Rd_Indx(iobase, 0x40, 1); +} + +static void InitCard(__u16 iobase) +{ + ResetChip(iobase, 5); + WriteReg(iobase, I_ST_CT_0, 0x00); // open CHIP on + SetSIRBOF(iobase, 0xc0); // hardware default value + SetSIREOF(iobase, 0xc1); +} + +static void CommonInit(__u16 iobase) +{ +// EnTXCRC(iobase,0); + SwapDMA(iobase, OFF); + SetMaxRxPacketSize(iobase, 0x0fff); //set to max:4095 + EnRXFIFOReadyInt(iobase, OFF); + EnRXFIFOHalfLevelInt(iobase, OFF); + EnTXFIFOHalfLevelInt(iobase, OFF); + EnTXFIFOUnderrunEOMInt(iobase, ON); +// EnTXFIFOReadyInt(iobase,ON); + InvertTX(iobase, OFF); + InvertRX(iobase, OFF); +// WriteLPCReg(0xF0,0); //(if VT1211 then do this) + if (IsSIROn(iobase)) { + SIRFilter(iobase, ON); + SIRRecvAny(iobase, ON); + } else { + SIRFilter(iobase, OFF); + SIRRecvAny(iobase, OFF); + } + EnRXSpecInt(iobase, ON); + WriteReg(iobase, I_ST_CT_0, 0x80); + EnableDMA(iobase, ON); +} + +static void SetBaudRate(__u16 iobase, __u32 rate) +{ + __u8 value = 11, temp; + + if (IsSIROn(iobase)) { + switch (rate) { + case (__u32) (2400L): + value = 47; + break; + case (__u32) (9600L): + value = 11; + break; + case (__u32) (19200L): + value = 5; + break; + case (__u32) (38400L): + value = 2; + break; + case (__u32) (57600L): + value = 1; + break; + case (__u32) (115200L): + value = 0; + break; + default: + break; + }; + } else if (IsMIROn(iobase)) { + value = 0; // will automatically be fixed in 1.152M + } else if (IsFIROn(iobase)) { + value = 0; // will automatically be fixed in 4M + } + temp = (ReadReg(iobase, I_CF_H_1) & 0x03); + temp |= value << 2; + WriteReg(iobase, I_CF_H_1, temp); +} + +static void SetPulseWidth(__u16 iobase, __u8 width) +{ + __u8 temp, temp1, temp2; + + temp = (ReadReg(iobase, I_CF_L_1) & 0x1f); + temp1 = (ReadReg(iobase, I_CF_H_1) & 0xfc); + temp2 = (width & 0x07) << 5; + temp |= temp2; + temp2 = (width & 0x18) >> 3; + temp1 |= temp2; + WriteReg(iobase, I_CF_L_1, temp); + WriteReg(iobase, I_CF_H_1, temp1); +} + +static void SetSendPreambleCount(__u16 iobase, __u8 count) +{ + __u8 temp; + + temp = ReadReg(iobase, I_CF_L_1) & 0xe0; + temp |= count; + WriteReg(iobase, I_CF_L_1, temp); + +} + +static void SetVFIR(__u16 BaseAddr, __u8 val) +{ + __u8 tmp; + + tmp = ReadReg(BaseAddr, I_CF_L_0); + WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); + WriteRegBit(BaseAddr, I_CF_H_0, 5, val); +} + +static void SetFIR(__u16 BaseAddr, __u8 val) +{ + __u8 tmp; + + WriteRegBit(BaseAddr, I_CF_H_0, 5, 0); + tmp = ReadReg(BaseAddr, I_CF_L_0); + WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); + WriteRegBit(BaseAddr, I_CF_L_0, 6, val); +} + +static void SetMIR(__u16 BaseAddr, __u8 val) +{ + __u8 tmp; + + WriteRegBit(BaseAddr, I_CF_H_0, 5, 0); + tmp = ReadReg(BaseAddr, I_CF_L_0); + WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); + WriteRegBit(BaseAddr, I_CF_L_0, 5, val); +} + +static void SetSIR(__u16 BaseAddr, __u8 val) +{ + __u8 tmp; + + WriteRegBit(BaseAddr, I_CF_H_0, 5, 0); + tmp = ReadReg(BaseAddr, I_CF_L_0); + WriteReg(BaseAddr, I_CF_L_0, tmp & 0x8f); + WriteRegBit(BaseAddr, I_CF_L_0, 4, val); +} + +#endif /* via_IRCC_H */ diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c new file mode 100644 index 000000000000..35fad8171a01 --- /dev/null +++ b/drivers/net/irda/vlsi_ir.c @@ -0,0 +1,1912 @@ +/********************************************************************* + * + * vlsi_ir.c: VLSI82C147 PCI IrDA controller driver for Linux + * + * Copyright (c) 2001-2003 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + ********************************************************************/ + +#include <linux/config.h> +#include <linux/module.h> + +#define DRIVER_NAME "vlsi_ir" +#define DRIVER_VERSION "v0.5" +#define DRIVER_DESCRIPTION "IrDA SIR/MIR/FIR driver for VLSI 82C147" +#define DRIVER_AUTHOR "Martin Diehl <info@mdiehl.de>" + +MODULE_DESCRIPTION(DRIVER_DESCRIPTION); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); + +/********************************************************/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/slab.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/delay.h> +#include <linux/time.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/smp_lock.h> +#include <asm/uaccess.h> +#include <asm/byteorder.h> + +#include <net/irda/irda.h> +#include <net/irda/irda_device.h> +#include <net/irda/wrapper.h> +#include <net/irda/crc.h> + +#include "vlsi_ir.h" + +/********************************************************/ + +static /* const */ char drivername[] = DRIVER_NAME; + +static struct pci_device_id vlsi_irda_table [] = { + { + .class = PCI_CLASS_WIRELESS_IRDA << 8, + .class_mask = PCI_CLASS_SUBCLASS_MASK << 8, + .vendor = PCI_VENDOR_ID_VLSI, + .device = PCI_DEVICE_ID_VLSI_82C147, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { /* all zeroes */ } +}; + +MODULE_DEVICE_TABLE(pci, vlsi_irda_table); + +/********************************************************/ + +/* clksrc: which clock source to be used + * 0: auto - try PLL, fallback to 40MHz XCLK + * 1: on-chip 48MHz PLL + * 2: external 48MHz XCLK + * 3: external 40MHz XCLK (HP OB-800) + */ + +static int clksrc = 0; /* default is 0(auto) */ +module_param(clksrc, int, 0); +MODULE_PARM_DESC(clksrc, "clock input source selection"); + +/* ringsize: size of the tx and rx descriptor rings + * independent for tx and rx + * specify as ringsize=tx[,rx] + * allowed values: 4, 8, 16, 32, 64 + * Due to the IrDA 1.x max. allowed window size=7, + * there should be no gain when using rings larger than 8 + */ + +static int ringsize[] = {8,8}; /* default is tx=8 / rx=8 */ +module_param_array(ringsize, int, NULL, 0); +MODULE_PARM_DESC(ringsize, "TX, RX ring descriptor size"); + +/* sirpulse: tuning of the SIR pulse width within IrPHY 1.3 limits + * 0: very short, 1.5us (exception: 6us at 2.4 kbaud) + * 1: nominal 3/16 bittime width + * note: IrDA compliant peer devices should be happy regardless + * which one is used. Primary goal is to save some power + * on the sender's side - at 9.6kbaud for example the short + * pulse width saves more than 90% of the transmitted IR power. + */ + +static int sirpulse = 1; /* default is 3/16 bittime */ +module_param(sirpulse, int, 0); +MODULE_PARM_DESC(sirpulse, "SIR pulse width tuning"); + +/* qos_mtt_bits: encoded min-turn-time value we require the peer device + * to use before transmitting to us. "Type 1" (per-station) + * bitfield according to IrLAP definition (section 6.6.8) + * Don't know which transceiver is used by my OB800 - the + * pretty common HP HDLS-1100 requires 1 msec - so lets use this. + */ + +static int qos_mtt_bits = 0x07; /* default is 1 ms or more */ +module_param(qos_mtt_bits, int, 0); +MODULE_PARM_DESC(qos_mtt_bits, "IrLAP bitfield representing min-turn-time"); + +/********************************************************/ + +static void vlsi_reg_debug(unsigned iobase, const char *s) +{ + int i; + + printk(KERN_DEBUG "%s: ", s); + for (i = 0; i < 0x20; i++) + printk("%02x", (unsigned)inb((iobase+i))); + printk("\n"); +} + +static void vlsi_ring_debug(struct vlsi_ring *r) +{ + struct ring_descr *rd; + unsigned i; + + printk(KERN_DEBUG "%s - ring %p / size %u / mask 0x%04x / len %u / dir %d / hw %p\n", + __FUNCTION__, r, r->size, r->mask, r->len, r->dir, r->rd[0].hw); + printk(KERN_DEBUG "%s - head = %d / tail = %d\n", __FUNCTION__, + atomic_read(&r->head) & r->mask, atomic_read(&r->tail) & r->mask); + for (i = 0; i < r->size; i++) { + rd = &r->rd[i]; + printk(KERN_DEBUG "%s - ring descr %u: ", __FUNCTION__, i); + printk("skb=%p data=%p hw=%p\n", rd->skb, rd->buf, rd->hw); + printk(KERN_DEBUG "%s - hw: status=%02x count=%u addr=0x%08x\n", + __FUNCTION__, (unsigned) rd_get_status(rd), + (unsigned) rd_get_count(rd), (unsigned) rd_get_addr(rd)); + } +} + +/********************************************************/ + +/* needed regardless of CONFIG_PROC_FS */ +static struct proc_dir_entry *vlsi_proc_root = NULL; + +#ifdef CONFIG_PROC_FS + +static void vlsi_proc_pdev(struct seq_file *seq, struct pci_dev *pdev) +{ + unsigned iobase = pci_resource_start(pdev, 0); + unsigned i; + + seq_printf(seq, "\n%s (vid/did: %04x/%04x)\n", + PCIDEV_NAME(pdev), (int)pdev->vendor, (int)pdev->device); + seq_printf(seq, "pci-power-state: %u\n", (unsigned) pdev->current_state); + seq_printf(seq, "resources: irq=%u / io=0x%04x / dma_mask=0x%016Lx\n", + pdev->irq, (unsigned)pci_resource_start(pdev, 0), (unsigned long long)pdev->dma_mask); + seq_printf(seq, "hw registers: "); + for (i = 0; i < 0x20; i++) + seq_printf(seq, "%02x", (unsigned)inb((iobase+i))); + seq_printf(seq, "\n"); +} + +static void vlsi_proc_ndev(struct seq_file *seq, struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = ndev->priv; + u8 byte; + u16 word; + unsigned delta1, delta2; + struct timeval now; + unsigned iobase = ndev->base_addr; + + seq_printf(seq, "\n%s link state: %s / %s / %s / %s\n", ndev->name, + netif_device_present(ndev) ? "attached" : "detached", + netif_running(ndev) ? "running" : "not running", + netif_carrier_ok(ndev) ? "carrier ok" : "no carrier", + netif_queue_stopped(ndev) ? "queue stopped" : "queue running"); + + if (!netif_running(ndev)) + return; + + seq_printf(seq, "\nhw-state:\n"); + pci_read_config_byte(idev->pdev, VLSI_PCI_IRMISC, &byte); + seq_printf(seq, "IRMISC:%s%s%s uart%s", + (byte&IRMISC_IRRAIL) ? " irrail" : "", + (byte&IRMISC_IRPD) ? " irpd" : "", + (byte&IRMISC_UARTTST) ? " uarttest" : "", + (byte&IRMISC_UARTEN) ? "@" : " disabled\n"); + if (byte&IRMISC_UARTEN) { + seq_printf(seq, "0x%s\n", + (byte&2) ? ((byte&1) ? "3e8" : "2e8") + : ((byte&1) ? "3f8" : "2f8")); + } + pci_read_config_byte(idev->pdev, VLSI_PCI_CLKCTL, &byte); + seq_printf(seq, "CLKCTL: PLL %s%s%s / clock %s / wakeup %s\n", + (byte&CLKCTL_PD_INV) ? "powered" : "down", + (byte&CLKCTL_LOCK) ? " locked" : "", + (byte&CLKCTL_EXTCLK) ? ((byte&CLKCTL_XCKSEL)?" / 40 MHz XCLK":" / 48 MHz XCLK") : "", + (byte&CLKCTL_CLKSTP) ? "stopped" : "running", + (byte&CLKCTL_WAKE) ? "enabled" : "disabled"); + pci_read_config_byte(idev->pdev, VLSI_PCI_MSTRPAGE, &byte); + seq_printf(seq, "MSTRPAGE: 0x%02x\n", (unsigned)byte); + + byte = inb(iobase+VLSI_PIO_IRINTR); + seq_printf(seq, "IRINTR:%s%s%s%s%s%s%s%s\n", + (byte&IRINTR_ACTEN) ? " ACTEN" : "", + (byte&IRINTR_RPKTEN) ? " RPKTEN" : "", + (byte&IRINTR_TPKTEN) ? " TPKTEN" : "", + (byte&IRINTR_OE_EN) ? " OE_EN" : "", + (byte&IRINTR_ACTIVITY) ? " ACTIVITY" : "", + (byte&IRINTR_RPKTINT) ? " RPKTINT" : "", + (byte&IRINTR_TPKTINT) ? " TPKTINT" : "", + (byte&IRINTR_OE_INT) ? " OE_INT" : ""); + word = inw(iobase+VLSI_PIO_RINGPTR); + seq_printf(seq, "RINGPTR: rx=%u / tx=%u\n", RINGPTR_GET_RX(word), RINGPTR_GET_TX(word)); + word = inw(iobase+VLSI_PIO_RINGBASE); + seq_printf(seq, "RINGBASE: busmap=0x%08x\n", + ((unsigned)word << 10)|(MSTRPAGE_VALUE<<24)); + word = inw(iobase+VLSI_PIO_RINGSIZE); + seq_printf(seq, "RINGSIZE: rx=%u / tx=%u\n", RINGSIZE_TO_RXSIZE(word), + RINGSIZE_TO_TXSIZE(word)); + + word = inw(iobase+VLSI_PIO_IRCFG); + seq_printf(seq, "IRCFG:%s%s%s%s%s%s%s%s%s%s%s%s%s\n", + (word&IRCFG_LOOP) ? " LOOP" : "", + (word&IRCFG_ENTX) ? " ENTX" : "", + (word&IRCFG_ENRX) ? " ENRX" : "", + (word&IRCFG_MSTR) ? " MSTR" : "", + (word&IRCFG_RXANY) ? " RXANY" : "", + (word&IRCFG_CRC16) ? " CRC16" : "", + (word&IRCFG_FIR) ? " FIR" : "", + (word&IRCFG_MIR) ? " MIR" : "", + (word&IRCFG_SIR) ? " SIR" : "", + (word&IRCFG_SIRFILT) ? " SIRFILT" : "", + (word&IRCFG_SIRTEST) ? " SIRTEST" : "", + (word&IRCFG_TXPOL) ? " TXPOL" : "", + (word&IRCFG_RXPOL) ? " RXPOL" : ""); + word = inw(iobase+VLSI_PIO_IRENABLE); + seq_printf(seq, "IRENABLE:%s%s%s%s%s%s%s%s\n", + (word&IRENABLE_PHYANDCLOCK) ? " PHYANDCLOCK" : "", + (word&IRENABLE_CFGER) ? " CFGERR" : "", + (word&IRENABLE_FIR_ON) ? " FIR_ON" : "", + (word&IRENABLE_MIR_ON) ? " MIR_ON" : "", + (word&IRENABLE_SIR_ON) ? " SIR_ON" : "", + (word&IRENABLE_ENTXST) ? " ENTXST" : "", + (word&IRENABLE_ENRXST) ? " ENRXST" : "", + (word&IRENABLE_CRC16_ON) ? " CRC16_ON" : ""); + word = inw(iobase+VLSI_PIO_PHYCTL); + seq_printf(seq, "PHYCTL: baud-divisor=%u / pulsewidth=%u / preamble=%u\n", + (unsigned)PHYCTL_TO_BAUD(word), + (unsigned)PHYCTL_TO_PLSWID(word), + (unsigned)PHYCTL_TO_PREAMB(word)); + word = inw(iobase+VLSI_PIO_NPHYCTL); + seq_printf(seq, "NPHYCTL: baud-divisor=%u / pulsewidth=%u / preamble=%u\n", + (unsigned)PHYCTL_TO_BAUD(word), + (unsigned)PHYCTL_TO_PLSWID(word), + (unsigned)PHYCTL_TO_PREAMB(word)); + word = inw(iobase+VLSI_PIO_MAXPKT); + seq_printf(seq, "MAXPKT: max. rx packet size = %u\n", word); + word = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; + seq_printf(seq, "RCVBCNT: rx-fifo filling level = %u\n", word); + + seq_printf(seq, "\nsw-state:\n"); + seq_printf(seq, "IrPHY setup: %d baud - %s encoding\n", idev->baud, + (idev->mode==IFF_SIR)?"SIR":((idev->mode==IFF_MIR)?"MIR":"FIR")); + do_gettimeofday(&now); + if (now.tv_usec >= idev->last_rx.tv_usec) { + delta2 = now.tv_usec - idev->last_rx.tv_usec; + delta1 = 0; + } + else { + delta2 = 1000000 + now.tv_usec - idev->last_rx.tv_usec; + delta1 = 1; + } + seq_printf(seq, "last rx: %lu.%06u sec\n", + now.tv_sec - idev->last_rx.tv_sec - delta1, delta2); + + seq_printf(seq, "RX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu", + idev->stats.rx_packets, idev->stats.rx_bytes, idev->stats.rx_errors, + idev->stats.rx_dropped); + seq_printf(seq, " / overrun=%lu / length=%lu / frame=%lu / crc=%lu\n", + idev->stats.rx_over_errors, idev->stats.rx_length_errors, + idev->stats.rx_frame_errors, idev->stats.rx_crc_errors); + seq_printf(seq, "TX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu / fifo=%lu\n", + idev->stats.tx_packets, idev->stats.tx_bytes, idev->stats.tx_errors, + idev->stats.tx_dropped, idev->stats.tx_fifo_errors); + +} + +static void vlsi_proc_ring(struct seq_file *seq, struct vlsi_ring *r) +{ + struct ring_descr *rd; + unsigned i, j; + int h, t; + + seq_printf(seq, "size %u / mask 0x%04x / len %u / dir %d / hw %p\n", + r->size, r->mask, r->len, r->dir, r->rd[0].hw); + h = atomic_read(&r->head) & r->mask; + t = atomic_read(&r->tail) & r->mask; + seq_printf(seq, "head = %d / tail = %d ", h, t); + if (h == t) + seq_printf(seq, "(empty)\n"); + else { + if (((t+1)&r->mask) == h) + seq_printf(seq, "(full)\n"); + else + seq_printf(seq, "(level = %d)\n", ((unsigned)(t-h) & r->mask)); + rd = &r->rd[h]; + j = (unsigned) rd_get_count(rd); + seq_printf(seq, "current: rd = %d / status = %02x / len = %u\n", + h, (unsigned)rd_get_status(rd), j); + if (j > 0) { + seq_printf(seq, " data:"); + if (j > 20) + j = 20; + for (i = 0; i < j; i++) + seq_printf(seq, " %02x", (unsigned)((unsigned char *)rd->buf)[i]); + seq_printf(seq, "\n"); + } + } + for (i = 0; i < r->size; i++) { + rd = &r->rd[i]; + seq_printf(seq, "> ring descr %u: ", i); + seq_printf(seq, "skb=%p data=%p hw=%p\n", rd->skb, rd->buf, rd->hw); + seq_printf(seq, " hw: status=%02x count=%u busaddr=0x%08x\n", + (unsigned) rd_get_status(rd), + (unsigned) rd_get_count(rd), (unsigned) rd_get_addr(rd)); + } +} + +static int vlsi_seq_show(struct seq_file *seq, void *v) +{ + struct net_device *ndev = seq->private; + vlsi_irda_dev_t *idev = ndev->priv; + unsigned long flags; + + seq_printf(seq, "\n%s %s\n\n", DRIVER_NAME, DRIVER_VERSION); + seq_printf(seq, "clksrc: %s\n", + (clksrc>=2) ? ((clksrc==3)?"40MHz XCLK":"48MHz XCLK") + : ((clksrc==1)?"48MHz PLL":"autodetect")); + seq_printf(seq, "ringsize: tx=%d / rx=%d\n", + ringsize[0], ringsize[1]); + seq_printf(seq, "sirpulse: %s\n", (sirpulse)?"3/16 bittime":"short"); + seq_printf(seq, "qos_mtt_bits: 0x%02x\n", (unsigned)qos_mtt_bits); + + spin_lock_irqsave(&idev->lock, flags); + if (idev->pdev != NULL) { + vlsi_proc_pdev(seq, idev->pdev); + + if (idev->pdev->current_state == 0) + vlsi_proc_ndev(seq, ndev); + else + seq_printf(seq, "\nPCI controller down - resume_ok = %d\n", + idev->resume_ok); + if (netif_running(ndev) && idev->rx_ring && idev->tx_ring) { + seq_printf(seq, "\n--------- RX ring -----------\n\n"); + vlsi_proc_ring(seq, idev->rx_ring); + seq_printf(seq, "\n--------- TX ring -----------\n\n"); + vlsi_proc_ring(seq, idev->tx_ring); + } + } + seq_printf(seq, "\n"); + spin_unlock_irqrestore(&idev->lock, flags); + + return 0; +} + +static int vlsi_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, vlsi_seq_show, PDE(inode)->data); +} + +static struct file_operations vlsi_proc_fops = { + .owner = THIS_MODULE, + .open = vlsi_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#define VLSI_PROC_FOPS (&vlsi_proc_fops) + +#else /* CONFIG_PROC_FS */ +#define VLSI_PROC_FOPS NULL +#endif + +/********************************************************/ + +static struct vlsi_ring *vlsi_alloc_ring(struct pci_dev *pdev, struct ring_descr_hw *hwmap, + unsigned size, unsigned len, int dir) +{ + struct vlsi_ring *r; + struct ring_descr *rd; + unsigned i, j; + dma_addr_t busaddr; + + if (!size || ((size-1)&size)!=0) /* must be >0 and power of 2 */ + return NULL; + + r = kmalloc(sizeof(*r) + size * sizeof(struct ring_descr), GFP_KERNEL); + if (!r) + return NULL; + memset(r, 0, sizeof(*r)); + + r->pdev = pdev; + r->dir = dir; + r->len = len; + r->rd = (struct ring_descr *)(r+1); + r->mask = size - 1; + r->size = size; + atomic_set(&r->head, 0); + atomic_set(&r->tail, 0); + + for (i = 0; i < size; i++) { + rd = r->rd + i; + memset(rd, 0, sizeof(*rd)); + rd->hw = hwmap + i; + rd->buf = kmalloc(len, GFP_KERNEL|GFP_DMA); + if (rd->buf == NULL + || !(busaddr = pci_map_single(pdev, rd->buf, len, dir))) { + if (rd->buf) { + IRDA_ERROR("%s: failed to create PCI-MAP for %p", + __FUNCTION__, rd->buf); + kfree(rd->buf); + rd->buf = NULL; + } + for (j = 0; j < i; j++) { + rd = r->rd + j; + busaddr = rd_get_addr(rd); + rd_set_addr_status(rd, 0, 0); + if (busaddr) + pci_unmap_single(pdev, busaddr, len, dir); + kfree(rd->buf); + rd->buf = NULL; + } + kfree(r); + return NULL; + } + rd_set_addr_status(rd, busaddr, 0); + /* initially, the dma buffer is owned by the CPU */ + rd->skb = NULL; + } + return r; +} + +static int vlsi_free_ring(struct vlsi_ring *r) +{ + struct ring_descr *rd; + unsigned i; + dma_addr_t busaddr; + + for (i = 0; i < r->size; i++) { + rd = r->rd + i; + if (rd->skb) + dev_kfree_skb_any(rd->skb); + busaddr = rd_get_addr(rd); + rd_set_addr_status(rd, 0, 0); + if (busaddr) + pci_unmap_single(r->pdev, busaddr, r->len, r->dir); + if (rd->buf) + kfree(rd->buf); + } + kfree(r); + return 0; +} + +static int vlsi_create_hwif(vlsi_irda_dev_t *idev) +{ + char *ringarea; + struct ring_descr_hw *hwmap; + + idev->virtaddr = NULL; + idev->busaddr = 0; + + ringarea = pci_alloc_consistent(idev->pdev, HW_RING_AREA_SIZE, &idev->busaddr); + if (!ringarea) { + IRDA_ERROR("%s: insufficient memory for descriptor rings\n", + __FUNCTION__); + goto out; + } + memset(ringarea, 0, HW_RING_AREA_SIZE); + + hwmap = (struct ring_descr_hw *)ringarea; + idev->rx_ring = vlsi_alloc_ring(idev->pdev, hwmap, ringsize[1], + XFER_BUF_SIZE, PCI_DMA_FROMDEVICE); + if (idev->rx_ring == NULL) + goto out_unmap; + + hwmap += MAX_RING_DESCR; + idev->tx_ring = vlsi_alloc_ring(idev->pdev, hwmap, ringsize[0], + XFER_BUF_SIZE, PCI_DMA_TODEVICE); + if (idev->tx_ring == NULL) + goto out_free_rx; + + idev->virtaddr = ringarea; + return 0; + +out_free_rx: + vlsi_free_ring(idev->rx_ring); +out_unmap: + idev->rx_ring = idev->tx_ring = NULL; + pci_free_consistent(idev->pdev, HW_RING_AREA_SIZE, ringarea, idev->busaddr); + idev->busaddr = 0; +out: + return -ENOMEM; +} + +static int vlsi_destroy_hwif(vlsi_irda_dev_t *idev) +{ + vlsi_free_ring(idev->rx_ring); + vlsi_free_ring(idev->tx_ring); + idev->rx_ring = idev->tx_ring = NULL; + + if (idev->busaddr) + pci_free_consistent(idev->pdev,HW_RING_AREA_SIZE,idev->virtaddr,idev->busaddr); + + idev->virtaddr = NULL; + idev->busaddr = 0; + + return 0; +} + +/********************************************************/ + +static int vlsi_process_rx(struct vlsi_ring *r, struct ring_descr *rd) +{ + u16 status; + int crclen, len = 0; + struct sk_buff *skb; + int ret = 0; + struct net_device *ndev = (struct net_device *)pci_get_drvdata(r->pdev); + vlsi_irda_dev_t *idev = ndev->priv; + + pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); + /* dma buffer now owned by the CPU */ + status = rd_get_status(rd); + if (status & RD_RX_ERROR) { + if (status & RD_RX_OVER) + ret |= VLSI_RX_OVER; + if (status & RD_RX_LENGTH) + ret |= VLSI_RX_LENGTH; + if (status & RD_RX_PHYERR) + ret |= VLSI_RX_FRAME; + if (status & RD_RX_CRCERR) + ret |= VLSI_RX_CRC; + goto done; + } + + len = rd_get_count(rd); + crclen = (idev->mode==IFF_FIR) ? sizeof(u32) : sizeof(u16); + len -= crclen; /* remove trailing CRC */ + if (len <= 0) { + IRDA_DEBUG(0, "%s: strange frame (len=%d)\n", __FUNCTION__, len); + ret |= VLSI_RX_DROP; + goto done; + } + + if (idev->mode == IFF_SIR) { /* hw checks CRC in MIR, FIR mode */ + + /* rd->buf is a streaming PCI_DMA_FROMDEVICE map. Doing the + * endian-adjustment there just in place will dirty a cache line + * which belongs to the map and thus we must be sure it will + * get flushed before giving the buffer back to hardware. + * vlsi_fill_rx() will do this anyway - but here we rely on. + */ + le16_to_cpus(rd->buf+len); + if (irda_calc_crc16(INIT_FCS,rd->buf,len+crclen) != GOOD_FCS) { + IRDA_DEBUG(0, "%s: crc error\n", __FUNCTION__); + ret |= VLSI_RX_CRC; + goto done; + } + } + + if (!rd->skb) { + IRDA_WARNING("%s: rx packet lost\n", __FUNCTION__); + ret |= VLSI_RX_DROP; + goto done; + } + + skb = rd->skb; + rd->skb = NULL; + skb->dev = ndev; + memcpy(skb_put(skb,len), rd->buf, len); + skb->mac.raw = skb->data; + if (in_interrupt()) + netif_rx(skb); + else + netif_rx_ni(skb); + ndev->last_rx = jiffies; + +done: + rd_set_status(rd, 0); + rd_set_count(rd, 0); + /* buffer still owned by CPU */ + + return (ret) ? -ret : len; +} + +static void vlsi_fill_rx(struct vlsi_ring *r) +{ + struct ring_descr *rd; + + for (rd = ring_last(r); rd != NULL; rd = ring_put(r)) { + if (rd_is_active(rd)) { + IRDA_WARNING("%s: driver bug: rx descr race with hw\n", + __FUNCTION__); + vlsi_ring_debug(r); + break; + } + if (!rd->skb) { + rd->skb = dev_alloc_skb(IRLAP_SKB_ALLOCSIZE); + if (rd->skb) { + skb_reserve(rd->skb,1); + rd->skb->protocol = htons(ETH_P_IRDA); + } + else + break; /* probably not worth logging? */ + } + /* give dma buffer back to busmaster */ + pci_dma_sync_single_for_device(r->pdev, rd_get_addr(rd), r->len, r->dir); + rd_activate(rd); + } +} + +static void vlsi_rx_interrupt(struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = ndev->priv; + struct vlsi_ring *r = idev->rx_ring; + struct ring_descr *rd; + int ret; + + for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { + + if (rd_is_active(rd)) + break; + + ret = vlsi_process_rx(r, rd); + + if (ret < 0) { + ret = -ret; + idev->stats.rx_errors++; + if (ret & VLSI_RX_DROP) + idev->stats.rx_dropped++; + if (ret & VLSI_RX_OVER) + idev->stats.rx_over_errors++; + if (ret & VLSI_RX_LENGTH) + idev->stats.rx_length_errors++; + if (ret & VLSI_RX_FRAME) + idev->stats.rx_frame_errors++; + if (ret & VLSI_RX_CRC) + idev->stats.rx_crc_errors++; + } + else if (ret > 0) { + idev->stats.rx_packets++; + idev->stats.rx_bytes += ret; + } + } + + do_gettimeofday(&idev->last_rx); /* remember "now" for later mtt delay */ + + vlsi_fill_rx(r); + + if (ring_first(r) == NULL) { + /* we are in big trouble, if this should ever happen */ + IRDA_ERROR("%s: rx ring exhausted!\n", __FUNCTION__); + vlsi_ring_debug(r); + } + else + outw(0, ndev->base_addr+VLSI_PIO_PROMPT); +} + +/* caller must have stopped the controller from busmastering */ + +static void vlsi_unarm_rx(vlsi_irda_dev_t *idev) +{ + struct vlsi_ring *r = idev->rx_ring; + struct ring_descr *rd; + int ret; + + for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { + + ret = 0; + if (rd_is_active(rd)) { + rd_set_status(rd, 0); + if (rd_get_count(rd)) { + IRDA_DEBUG(0, "%s - dropping rx packet\n", __FUNCTION__); + ret = -VLSI_RX_DROP; + } + rd_set_count(rd, 0); + pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); + if (rd->skb) { + dev_kfree_skb_any(rd->skb); + rd->skb = NULL; + } + } + else + ret = vlsi_process_rx(r, rd); + + if (ret < 0) { + ret = -ret; + idev->stats.rx_errors++; + if (ret & VLSI_RX_DROP) + idev->stats.rx_dropped++; + if (ret & VLSI_RX_OVER) + idev->stats.rx_over_errors++; + if (ret & VLSI_RX_LENGTH) + idev->stats.rx_length_errors++; + if (ret & VLSI_RX_FRAME) + idev->stats.rx_frame_errors++; + if (ret & VLSI_RX_CRC) + idev->stats.rx_crc_errors++; + } + else if (ret > 0) { + idev->stats.rx_packets++; + idev->stats.rx_bytes += ret; + } + } +} + +/********************************************************/ + +static int vlsi_process_tx(struct vlsi_ring *r, struct ring_descr *rd) +{ + u16 status; + int len; + int ret; + + pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); + /* dma buffer now owned by the CPU */ + status = rd_get_status(rd); + if (status & RD_TX_UNDRN) + ret = VLSI_TX_FIFO; + else + ret = 0; + rd_set_status(rd, 0); + + if (rd->skb) { + len = rd->skb->len; + dev_kfree_skb_any(rd->skb); + rd->skb = NULL; + } + else /* tx-skb already freed? - should never happen */ + len = rd_get_count(rd); /* incorrect for SIR! (due to wrapping) */ + + rd_set_count(rd, 0); + /* dma buffer still owned by the CPU */ + + return (ret) ? -ret : len; +} + +static int vlsi_set_baud(vlsi_irda_dev_t *idev, unsigned iobase) +{ + u16 nphyctl; + u16 config; + unsigned mode; + int ret; + int baudrate; + int fifocnt; + + baudrate = idev->new_baud; + IRDA_DEBUG(2, "%s: %d -> %d\n", __FUNCTION__, idev->baud, idev->new_baud); + if (baudrate == 4000000) { + mode = IFF_FIR; + config = IRCFG_FIR; + nphyctl = PHYCTL_FIR; + } + else if (baudrate == 1152000) { + mode = IFF_MIR; + config = IRCFG_MIR | IRCFG_CRC16; + nphyctl = PHYCTL_MIR(clksrc==3); + } + else { + mode = IFF_SIR; + config = IRCFG_SIR | IRCFG_SIRFILT | IRCFG_RXANY; + switch(baudrate) { + default: + IRDA_WARNING("%s: undefined baudrate %d - fallback to 9600!\n", + __FUNCTION__, baudrate); + baudrate = 9600; + /* fallthru */ + case 2400: + case 9600: + case 19200: + case 38400: + case 57600: + case 115200: + nphyctl = PHYCTL_SIR(baudrate,sirpulse,clksrc==3); + break; + } + } + config |= IRCFG_MSTR | IRCFG_ENRX; + + fifocnt = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; + if (fifocnt != 0) { + IRDA_DEBUG(0, "%s: rx fifo not empty(%d)\n", __FUNCTION__, fifocnt); + } + + outw(0, iobase+VLSI_PIO_IRENABLE); + outw(config, iobase+VLSI_PIO_IRCFG); + outw(nphyctl, iobase+VLSI_PIO_NPHYCTL); + wmb(); + outw(IRENABLE_PHYANDCLOCK, iobase+VLSI_PIO_IRENABLE); + mb(); + + udelay(1); /* chip applies IRCFG on next rising edge of its 8MHz clock */ + + /* read back settings for validation */ + + config = inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_MASK; + + if (mode == IFF_FIR) + config ^= IRENABLE_FIR_ON; + else if (mode == IFF_MIR) + config ^= (IRENABLE_MIR_ON|IRENABLE_CRC16_ON); + else + config ^= IRENABLE_SIR_ON; + + if (config != (IRENABLE_PHYANDCLOCK|IRENABLE_ENRXST)) { + IRDA_WARNING("%s: failed to set %s mode!\n", __FUNCTION__, + (mode==IFF_SIR)?"SIR":((mode==IFF_MIR)?"MIR":"FIR")); + ret = -1; + } + else { + if (inw(iobase+VLSI_PIO_PHYCTL) != nphyctl) { + IRDA_WARNING("%s: failed to apply baudrate %d\n", + __FUNCTION__, baudrate); + ret = -1; + } + else { + idev->mode = mode; + idev->baud = baudrate; + idev->new_baud = 0; + ret = 0; + } + } + + if (ret) + vlsi_reg_debug(iobase,__FUNCTION__); + + return ret; +} + +static int vlsi_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = ndev->priv; + struct vlsi_ring *r = idev->tx_ring; + struct ring_descr *rd; + unsigned long flags; + unsigned iobase = ndev->base_addr; + u8 status; + u16 config; + int mtt; + int len, speed; + struct timeval now, ready; + char *msg = NULL; + + speed = irda_get_next_speed(skb); + spin_lock_irqsave(&idev->lock, flags); + if (speed != -1 && speed != idev->baud) { + netif_stop_queue(ndev); + idev->new_baud = speed; + status = RD_TX_CLRENTX; /* stop tx-ring after this frame */ + } + else + status = 0; + + if (skb->len == 0) { + /* handle zero packets - should be speed change */ + if (status == 0) { + msg = "bogus zero-length packet"; + goto drop_unlock; + } + + /* due to the completely asynch tx operation we might have + * IrLAP racing with the hardware here, f.e. if the controller + * is just sending the last packet with current speed while + * the LAP is already switching the speed using synchronous + * len=0 packet. Immediate execution would lead to hw lockup + * requiring a powercycle to reset. Good candidate to trigger + * this is the final UA:RSP packet after receiving a DISC:CMD + * when getting the LAP down. + * Note that we are not protected by the queue_stop approach + * because the final UA:RSP arrives _without_ request to apply + * new-speed-after-this-packet - hence the driver doesn't know + * this was the last packet and doesn't stop the queue. So the + * forced switch to default speed from LAP gets through as fast + * as only some 10 usec later while the UA:RSP is still processed + * by the hardware and we would get screwed. + */ + + if (ring_first(idev->tx_ring) == NULL) { + /* no race - tx-ring already empty */ + vlsi_set_baud(idev, iobase); + netif_wake_queue(ndev); + } + else + ; + /* keep the speed change pending like it would + * for any len>0 packet. tx completion interrupt + * will apply it when the tx ring becomes empty. + */ + spin_unlock_irqrestore(&idev->lock, flags); + dev_kfree_skb_any(skb); + return 0; + } + + /* sanity checks - simply drop the packet */ + + rd = ring_last(r); + if (!rd) { + msg = "ring full, but queue wasn't stopped"; + goto drop_unlock; + } + + if (rd_is_active(rd)) { + msg = "entry still owned by hw"; + goto drop_unlock; + } + + if (!rd->buf) { + msg = "tx ring entry without pci buffer"; + goto drop_unlock; + } + + if (rd->skb) { + msg = "ring entry with old skb still attached"; + goto drop_unlock; + } + + /* no need for serialization or interrupt disable during mtt */ + spin_unlock_irqrestore(&idev->lock, flags); + + if ((mtt = irda_get_mtt(skb)) > 0) { + + ready.tv_usec = idev->last_rx.tv_usec + mtt; + ready.tv_sec = idev->last_rx.tv_sec; + if (ready.tv_usec >= 1000000) { + ready.tv_usec -= 1000000; + ready.tv_sec++; /* IrLAP 1.1: mtt always < 1 sec */ + } + for(;;) { + do_gettimeofday(&now); + if (now.tv_sec > ready.tv_sec + || (now.tv_sec==ready.tv_sec && now.tv_usec>=ready.tv_usec)) + break; + udelay(100); + /* must not sleep here - we are called under xmit_lock! */ + } + } + + /* tx buffer already owned by CPU due to pci_dma_sync_single_for_cpu() + * after subsequent tx-completion + */ + + if (idev->mode == IFF_SIR) { + status |= RD_TX_DISCRC; /* no hw-crc creation */ + len = async_wrap_skb(skb, rd->buf, r->len); + + /* Some rare worst case situation in SIR mode might lead to + * potential buffer overflow. The wrapper detects this, returns + * with a shortened frame (without FCS/EOF) but doesn't provide + * any error indication about the invalid packet which we are + * going to transmit. + * Therefore we log if the buffer got filled to the point, where the + * wrapper would abort, i.e. when there are less than 5 bytes left to + * allow appending the FCS/EOF. + */ + + if (len >= r->len-5) + IRDA_WARNING("%s: possible buffer overflow with SIR wrapping!\n", + __FUNCTION__); + } + else { + /* hw deals with MIR/FIR mode wrapping */ + status |= RD_TX_PULSE; /* send 2 us highspeed indication pulse */ + len = skb->len; + if (len > r->len) { + msg = "frame exceeds tx buffer length"; + goto drop; + } + else + memcpy(rd->buf, skb->data, len); + } + + rd->skb = skb; /* remember skb for tx-complete stats */ + + rd_set_count(rd, len); + rd_set_status(rd, status); /* not yet active! */ + + /* give dma buffer back to busmaster-hw (flush caches to make + * CPU-driven changes visible from the pci bus). + */ + + pci_dma_sync_single_for_device(r->pdev, rd_get_addr(rd), r->len, r->dir); + +/* Switching to TX mode here races with the controller + * which may stop TX at any time when fetching an inactive descriptor + * or one with CLR_ENTX set. So we switch on TX only, if TX was not running + * _after_ the new descriptor was activated on the ring. This ensures + * we will either find TX already stopped or we can be sure, there + * will be a TX-complete interrupt even if the chip stopped doing + * TX just after we found it still running. The ISR will then find + * the non-empty ring and restart TX processing. The enclosing + * spinlock provides the correct serialization to prevent race with isr. + */ + + spin_lock_irqsave(&idev->lock,flags); + + rd_activate(rd); + + if (!(inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_ENTXST)) { + int fifocnt; + + fifocnt = inw(ndev->base_addr+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; + if (fifocnt != 0) { + IRDA_DEBUG(0, "%s: rx fifo not empty(%d)\n", __FUNCTION__, fifocnt); + } + + config = inw(iobase+VLSI_PIO_IRCFG); + mb(); + outw(config | IRCFG_ENTX, iobase+VLSI_PIO_IRCFG); + wmb(); + outw(0, iobase+VLSI_PIO_PROMPT); + } + ndev->trans_start = jiffies; + + if (ring_put(r) == NULL) { + netif_stop_queue(ndev); + IRDA_DEBUG(3, "%s: tx ring full - queue stopped\n", __FUNCTION__); + } + spin_unlock_irqrestore(&idev->lock, flags); + + return 0; + +drop_unlock: + spin_unlock_irqrestore(&idev->lock, flags); +drop: + IRDA_WARNING("%s: dropping packet - %s\n", __FUNCTION__, msg); + dev_kfree_skb_any(skb); + idev->stats.tx_errors++; + idev->stats.tx_dropped++; + /* Don't even think about returning NET_XMIT_DROP (=1) here! + * In fact any retval!=0 causes the packet scheduler to requeue the + * packet for later retry of transmission - which isn't exactly + * what we want after we've just called dev_kfree_skb_any ;-) + */ + return 0; +} + +static void vlsi_tx_interrupt(struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = ndev->priv; + struct vlsi_ring *r = idev->tx_ring; + struct ring_descr *rd; + unsigned iobase; + int ret; + u16 config; + + for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { + + if (rd_is_active(rd)) + break; + + ret = vlsi_process_tx(r, rd); + + if (ret < 0) { + ret = -ret; + idev->stats.tx_errors++; + if (ret & VLSI_TX_DROP) + idev->stats.tx_dropped++; + if (ret & VLSI_TX_FIFO) + idev->stats.tx_fifo_errors++; + } + else if (ret > 0){ + idev->stats.tx_packets++; + idev->stats.tx_bytes += ret; + } + } + + iobase = ndev->base_addr; + + if (idev->new_baud && rd == NULL) /* tx ring empty and speed change pending */ + vlsi_set_baud(idev, iobase); + + config = inw(iobase+VLSI_PIO_IRCFG); + if (rd == NULL) /* tx ring empty: re-enable rx */ + outw((config & ~IRCFG_ENTX) | IRCFG_ENRX, iobase+VLSI_PIO_IRCFG); + + else if (!(inw(iobase+VLSI_PIO_IRENABLE) & IRENABLE_ENTXST)) { + int fifocnt; + + fifocnt = inw(iobase+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; + if (fifocnt != 0) { + IRDA_DEBUG(0, "%s: rx fifo not empty(%d)\n", + __FUNCTION__, fifocnt); + } + outw(config | IRCFG_ENTX, iobase+VLSI_PIO_IRCFG); + } + + outw(0, iobase+VLSI_PIO_PROMPT); + + if (netif_queue_stopped(ndev) && !idev->new_baud) { + netif_wake_queue(ndev); + IRDA_DEBUG(3, "%s: queue awoken\n", __FUNCTION__); + } +} + +/* caller must have stopped the controller from busmastering */ + +static void vlsi_unarm_tx(vlsi_irda_dev_t *idev) +{ + struct vlsi_ring *r = idev->tx_ring; + struct ring_descr *rd; + int ret; + + for (rd = ring_first(r); rd != NULL; rd = ring_get(r)) { + + ret = 0; + if (rd_is_active(rd)) { + rd_set_status(rd, 0); + rd_set_count(rd, 0); + pci_dma_sync_single_for_cpu(r->pdev, rd_get_addr(rd), r->len, r->dir); + if (rd->skb) { + dev_kfree_skb_any(rd->skb); + rd->skb = NULL; + } + IRDA_DEBUG(0, "%s - dropping tx packet\n", __FUNCTION__); + ret = -VLSI_TX_DROP; + } + else + ret = vlsi_process_tx(r, rd); + + if (ret < 0) { + ret = -ret; + idev->stats.tx_errors++; + if (ret & VLSI_TX_DROP) + idev->stats.tx_dropped++; + if (ret & VLSI_TX_FIFO) + idev->stats.tx_fifo_errors++; + } + else if (ret > 0){ + idev->stats.tx_packets++; + idev->stats.tx_bytes += ret; + } + } + +} + +/********************************************************/ + +static int vlsi_start_clock(struct pci_dev *pdev) +{ + u8 clkctl, lock; + int i, count; + + if (clksrc < 2) { /* auto or PLL: try PLL */ + clkctl = CLKCTL_PD_INV | CLKCTL_CLKSTP; + pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); + + /* procedure to detect PLL lock synchronisation: + * after 0.5 msec initial delay we expect to find 3 PLL lock + * indications within 10 msec for successful PLL detection. + */ + udelay(500); + count = 0; + for (i = 500; i <= 10000; i += 50) { /* max 10 msec */ + pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &lock); + if (lock&CLKCTL_LOCK) { + if (++count >= 3) + break; + } + udelay(50); + } + if (count < 3) { + if (clksrc == 1) { /* explicitly asked for PLL hence bail out */ + IRDA_ERROR("%s: no PLL or failed to lock!\n", + __FUNCTION__); + clkctl = CLKCTL_CLKSTP; + pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); + return -1; + } + else /* was: clksrc=0(auto) */ + clksrc = 3; /* fallback to 40MHz XCLK (OB800) */ + + IRDA_DEBUG(0, "%s: PLL not locked, fallback to clksrc=%d\n", + __FUNCTION__, clksrc); + } + else + clksrc = 1; /* got successful PLL lock */ + } + + if (clksrc != 1) { + /* we get here if either no PLL detected in auto-mode or + an external clock source was explicitly specified */ + + clkctl = CLKCTL_EXTCLK | CLKCTL_CLKSTP; + if (clksrc == 3) + clkctl |= CLKCTL_XCKSEL; + pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); + + /* no way to test for working XCLK */ + } + else + pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &clkctl); + + /* ok, now going to connect the chip with the clock source */ + + clkctl &= ~CLKCTL_CLKSTP; + pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); + + return 0; +} + +static void vlsi_stop_clock(struct pci_dev *pdev) +{ + u8 clkctl; + + /* disconnect chip from clock source */ + pci_read_config_byte(pdev, VLSI_PCI_CLKCTL, &clkctl); + clkctl |= CLKCTL_CLKSTP; + pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); + + /* disable all clock sources */ + clkctl &= ~(CLKCTL_EXTCLK | CLKCTL_PD_INV); + pci_write_config_byte(pdev, VLSI_PCI_CLKCTL, clkctl); +} + +/********************************************************/ + +/* writing all-zero to the VLSI PCI IO register area seems to prevent + * some occasional situations where the hardware fails (symptoms are + * what appears as stalled tx/rx state machines, i.e. everything ok for + * receive or transmit but hw makes no progress or is unable to access + * the bus memory locations). + * Best place to call this is immediately after/before the internal clock + * gets started/stopped. + */ + +static inline void vlsi_clear_regs(unsigned iobase) +{ + unsigned i; + const unsigned chip_io_extent = 32; + + for (i = 0; i < chip_io_extent; i += sizeof(u16)) + outw(0, iobase + i); +} + +static int vlsi_init_chip(struct pci_dev *pdev) +{ + struct net_device *ndev = pci_get_drvdata(pdev); + vlsi_irda_dev_t *idev = ndev->priv; + unsigned iobase; + u16 ptr; + + /* start the clock and clean the registers */ + + if (vlsi_start_clock(pdev)) { + IRDA_ERROR("%s: no valid clock source\n", __FUNCTION__); + return -1; + } + iobase = ndev->base_addr; + vlsi_clear_regs(iobase); + + outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR); /* w/c pending IRQ, disable all INT */ + + outw(0, iobase+VLSI_PIO_IRENABLE); /* disable IrPHY-interface */ + + /* disable everything, particularly IRCFG_MSTR - (also resetting the RING_PTR) */ + + outw(0, iobase+VLSI_PIO_IRCFG); + wmb(); + + outw(MAX_PACKET_LENGTH, iobase+VLSI_PIO_MAXPKT); /* max possible value=0x0fff */ + + outw(BUS_TO_RINGBASE(idev->busaddr), iobase+VLSI_PIO_RINGBASE); + + outw(TX_RX_TO_RINGSIZE(idev->tx_ring->size, idev->rx_ring->size), + iobase+VLSI_PIO_RINGSIZE); + + ptr = inw(iobase+VLSI_PIO_RINGPTR); + atomic_set(&idev->rx_ring->head, RINGPTR_GET_RX(ptr)); + atomic_set(&idev->rx_ring->tail, RINGPTR_GET_RX(ptr)); + atomic_set(&idev->tx_ring->head, RINGPTR_GET_TX(ptr)); + atomic_set(&idev->tx_ring->tail, RINGPTR_GET_TX(ptr)); + + vlsi_set_baud(idev, iobase); /* idev->new_baud used as provided by caller */ + + outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR); /* just in case - w/c pending IRQ's */ + wmb(); + + /* DO NOT BLINDLY ENABLE IRINTR_ACTEN! + * basically every received pulse fires an ACTIVITY-INT + * leading to >>1000 INT's per second instead of few 10 + */ + + outb(IRINTR_RPKTEN|IRINTR_TPKTEN, iobase+VLSI_PIO_IRINTR); + + return 0; +} + +static int vlsi_start_hw(vlsi_irda_dev_t *idev) +{ + struct pci_dev *pdev = idev->pdev; + struct net_device *ndev = pci_get_drvdata(pdev); + unsigned iobase = ndev->base_addr; + u8 byte; + + /* we don't use the legacy UART, disable its address decoding */ + + pci_read_config_byte(pdev, VLSI_PCI_IRMISC, &byte); + byte &= ~(IRMISC_UARTEN | IRMISC_UARTTST); + pci_write_config_byte(pdev, VLSI_PCI_IRMISC, byte); + + /* enable PCI busmaster access to our 16MB page */ + + pci_write_config_byte(pdev, VLSI_PCI_MSTRPAGE, MSTRPAGE_VALUE); + pci_set_master(pdev); + + if (vlsi_init_chip(pdev) < 0) { + pci_disable_device(pdev); + return -1; + } + + vlsi_fill_rx(idev->rx_ring); + + do_gettimeofday(&idev->last_rx); /* first mtt may start from now on */ + + outw(0, iobase+VLSI_PIO_PROMPT); /* kick hw state machine */ + + return 0; +} + +static int vlsi_stop_hw(vlsi_irda_dev_t *idev) +{ + struct pci_dev *pdev = idev->pdev; + struct net_device *ndev = pci_get_drvdata(pdev); + unsigned iobase = ndev->base_addr; + unsigned long flags; + + spin_lock_irqsave(&idev->lock,flags); + outw(0, iobase+VLSI_PIO_IRENABLE); + outw(0, iobase+VLSI_PIO_IRCFG); /* disable everything */ + + /* disable and w/c irqs */ + outb(0, iobase+VLSI_PIO_IRINTR); + wmb(); + outb(IRINTR_INT_MASK, iobase+VLSI_PIO_IRINTR); + spin_unlock_irqrestore(&idev->lock,flags); + + vlsi_unarm_tx(idev); + vlsi_unarm_rx(idev); + + vlsi_clear_regs(iobase); + vlsi_stop_clock(pdev); + + pci_disable_device(pdev); + + return 0; +} + +/**************************************************************/ + +static struct net_device_stats * vlsi_get_stats(struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = ndev->priv; + + return &idev->stats; +} + +static void vlsi_tx_timeout(struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = ndev->priv; + + + vlsi_reg_debug(ndev->base_addr, __FUNCTION__); + vlsi_ring_debug(idev->tx_ring); + + if (netif_running(ndev)) + netif_stop_queue(ndev); + + vlsi_stop_hw(idev); + + /* now simply restart the whole thing */ + + if (!idev->new_baud) + idev->new_baud = idev->baud; /* keep current baudrate */ + + if (vlsi_start_hw(idev)) + IRDA_ERROR("%s: failed to restart hw - %s(%s) unusable!\n", + __FUNCTION__, PCIDEV_NAME(idev->pdev), ndev->name); + else + netif_start_queue(ndev); +} + +static int vlsi_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) +{ + vlsi_irda_dev_t *idev = ndev->priv; + struct if_irda_req *irq = (struct if_irda_req *) rq; + unsigned long flags; + u16 fifocnt; + int ret = 0; + + switch (cmd) { + case SIOCSBANDWIDTH: + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + spin_lock_irqsave(&idev->lock, flags); + idev->new_baud = irq->ifr_baudrate; + /* when called from userland there might be a minor race window here + * if the stack tries to change speed concurrently - which would be + * pretty strange anyway with the userland having full control... + */ + vlsi_set_baud(idev, ndev->base_addr); + spin_unlock_irqrestore(&idev->lock, flags); + break; + case SIOCSMEDIABUSY: + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + break; + } + irda_device_set_media_busy(ndev, TRUE); + break; + case SIOCGRECEIVING: + /* the best we can do: check whether there are any bytes in rx fifo. + * The trustable window (in case some data arrives just afterwards) + * may be as short as 1usec or so at 4Mbps. + */ + fifocnt = inw(ndev->base_addr+VLSI_PIO_RCVBCNT) & RCVBCNT_MASK; + irq->ifr_receiving = (fifocnt!=0) ? 1 : 0; + break; + default: + IRDA_WARNING("%s: notsupp - cmd=%04x\n", + __FUNCTION__, cmd); + ret = -EOPNOTSUPP; + } + + return ret; +} + +/********************************************************/ + +static irqreturn_t vlsi_interrupt(int irq, void *dev_instance, + struct pt_regs *regs) +{ + struct net_device *ndev = dev_instance; + vlsi_irda_dev_t *idev = ndev->priv; + unsigned iobase; + u8 irintr; + int boguscount = 5; + unsigned long flags; + int handled = 0; + + iobase = ndev->base_addr; + spin_lock_irqsave(&idev->lock,flags); + do { + irintr = inb(iobase+VLSI_PIO_IRINTR); + mb(); + outb(irintr, iobase+VLSI_PIO_IRINTR); /* acknowledge asap */ + + if (!(irintr&=IRINTR_INT_MASK)) /* not our INT - probably shared */ + break; + + handled = 1; + + if (unlikely(!(irintr & ~IRINTR_ACTIVITY))) + break; /* nothing todo if only activity */ + + if (irintr&IRINTR_RPKTINT) + vlsi_rx_interrupt(ndev); + + if (irintr&IRINTR_TPKTINT) + vlsi_tx_interrupt(ndev); + + } while (--boguscount > 0); + spin_unlock_irqrestore(&idev->lock,flags); + + if (boguscount <= 0) + IRDA_MESSAGE("%s: too much work in interrupt!\n", + __FUNCTION__); + return IRQ_RETVAL(handled); +} + +/********************************************************/ + +static int vlsi_open(struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = ndev->priv; + int err = -EAGAIN; + char hwname[32]; + + if (pci_request_regions(idev->pdev, drivername)) { + IRDA_WARNING("%s: io resource busy\n", __FUNCTION__); + goto errout; + } + ndev->base_addr = pci_resource_start(idev->pdev,0); + ndev->irq = idev->pdev->irq; + + /* under some rare occasions the chip apparently comes up with + * IRQ's pending. We better w/c pending IRQ and disable them all + */ + + outb(IRINTR_INT_MASK, ndev->base_addr+VLSI_PIO_IRINTR); + + if (request_irq(ndev->irq, vlsi_interrupt, SA_SHIRQ, + drivername, ndev)) { + IRDA_WARNING("%s: couldn't get IRQ: %d\n", + __FUNCTION__, ndev->irq); + goto errout_io; + } + + if ((err = vlsi_create_hwif(idev)) != 0) + goto errout_irq; + + sprintf(hwname, "VLSI-FIR @ 0x%04x", (unsigned)ndev->base_addr); + idev->irlap = irlap_open(ndev,&idev->qos,hwname); + if (!idev->irlap) + goto errout_free_ring; + + do_gettimeofday(&idev->last_rx); /* first mtt may start from now on */ + + idev->new_baud = 9600; /* start with IrPHY using 9600(SIR) mode */ + + if ((err = vlsi_start_hw(idev)) != 0) + goto errout_close_irlap; + + netif_start_queue(ndev); + + IRDA_MESSAGE("%s: device %s operational\n", __FUNCTION__, ndev->name); + + return 0; + +errout_close_irlap: + irlap_close(idev->irlap); +errout_free_ring: + vlsi_destroy_hwif(idev); +errout_irq: + free_irq(ndev->irq,ndev); +errout_io: + pci_release_regions(idev->pdev); +errout: + return err; +} + +static int vlsi_close(struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = ndev->priv; + + netif_stop_queue(ndev); + + if (idev->irlap) + irlap_close(idev->irlap); + idev->irlap = NULL; + + vlsi_stop_hw(idev); + + vlsi_destroy_hwif(idev); + + free_irq(ndev->irq,ndev); + + pci_release_regions(idev->pdev); + + IRDA_MESSAGE("%s: device %s stopped\n", __FUNCTION__, ndev->name); + + return 0; +} + +static int vlsi_irda_init(struct net_device *ndev) +{ + vlsi_irda_dev_t *idev = ndev->priv; + struct pci_dev *pdev = idev->pdev; + + SET_MODULE_OWNER(ndev); + + ndev->irq = pdev->irq; + ndev->base_addr = pci_resource_start(pdev,0); + + /* PCI busmastering + * see include file for details why we need these 2 masks, in this order! + */ + + if (pci_set_dma_mask(pdev,DMA_MASK_USED_BY_HW) + || pci_set_dma_mask(pdev,DMA_MASK_MSTRPAGE)) { + IRDA_ERROR("%s: aborting due to PCI BM-DMA address limitations\n", __FUNCTION__); + return -1; + } + + irda_init_max_qos_capabilies(&idev->qos); + + /* the VLSI82C147 does not support 576000! */ + + idev->qos.baud_rate.bits = IR_2400 | IR_9600 + | IR_19200 | IR_38400 | IR_57600 | IR_115200 + | IR_1152000 | (IR_4000000 << 8); + + idev->qos.min_turn_time.bits = qos_mtt_bits; + + irda_qos_bits_to_value(&idev->qos); + + /* currently no public media definitions for IrDA */ + + ndev->flags |= IFF_PORTSEL | IFF_AUTOMEDIA; + ndev->if_port = IF_PORT_UNKNOWN; + + ndev->open = vlsi_open; + ndev->stop = vlsi_close; + ndev->get_stats = vlsi_get_stats; + ndev->hard_start_xmit = vlsi_hard_start_xmit; + ndev->do_ioctl = vlsi_ioctl; + ndev->tx_timeout = vlsi_tx_timeout; + ndev->watchdog_timeo = 500*HZ/1000; /* max. allowed turn time for IrLAP */ + + SET_NETDEV_DEV(ndev, &pdev->dev); + + return 0; +} + +/**************************************************************/ + +static int __devinit +vlsi_irda_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct net_device *ndev; + vlsi_irda_dev_t *idev; + + if (pci_enable_device(pdev)) + goto out; + else + pdev->current_state = 0; /* hw must be running now */ + + IRDA_MESSAGE("%s: IrDA PCI controller %s detected\n", + drivername, PCIDEV_NAME(pdev)); + + if ( !pci_resource_start(pdev,0) + || !(pci_resource_flags(pdev,0) & IORESOURCE_IO) ) { + IRDA_ERROR("%s: bar 0 invalid", __FUNCTION__); + goto out_disable; + } + + ndev = alloc_irdadev(sizeof(*idev)); + if (ndev==NULL) { + IRDA_ERROR("%s: Unable to allocate device memory.\n", + __FUNCTION__); + goto out_disable; + } + + idev = ndev->priv; + + spin_lock_init(&idev->lock); + init_MUTEX(&idev->sem); + down(&idev->sem); + idev->pdev = pdev; + + if (vlsi_irda_init(ndev) < 0) + goto out_freedev; + + if (register_netdev(ndev) < 0) { + IRDA_ERROR("%s: register_netdev failed\n", __FUNCTION__); + goto out_freedev; + } + + if (vlsi_proc_root != NULL) { + struct proc_dir_entry *ent; + + ent = create_proc_entry(ndev->name, S_IFREG|S_IRUGO, vlsi_proc_root); + if (!ent) { + IRDA_WARNING("%s: failed to create proc entry\n", + __FUNCTION__); + } else { + ent->data = ndev; + ent->proc_fops = VLSI_PROC_FOPS; + ent->size = 0; + } + idev->proc_entry = ent; + } + IRDA_MESSAGE("%s: registered device %s\n", drivername, ndev->name); + + pci_set_drvdata(pdev, ndev); + up(&idev->sem); + + return 0; + +out_freedev: + up(&idev->sem); + free_netdev(ndev); +out_disable: + pci_disable_device(pdev); +out: + pci_set_drvdata(pdev, NULL); + return -ENODEV; +} + +static void __devexit vlsi_irda_remove(struct pci_dev *pdev) +{ + struct net_device *ndev = pci_get_drvdata(pdev); + vlsi_irda_dev_t *idev; + + if (!ndev) { + IRDA_ERROR("%s: lost netdevice?\n", drivername); + return; + } + + unregister_netdev(ndev); + + idev = ndev->priv; + down(&idev->sem); + if (idev->proc_entry) { + remove_proc_entry(ndev->name, vlsi_proc_root); + idev->proc_entry = NULL; + } + up(&idev->sem); + + free_netdev(ndev); + + pci_set_drvdata(pdev, NULL); + + IRDA_MESSAGE("%s: %s removed\n", drivername, PCIDEV_NAME(pdev)); +} + +#ifdef CONFIG_PM + +/* The Controller doesn't provide PCI PM capabilities as defined by PCI specs. + * Some of the Linux PCI-PM code however depends on this, for example in + * pci_set_power_state(). So we have to take care to perform the required + * operations on our own (particularly reflecting the pdev->current_state) + * otherwise we might get cheated by pci-pm. + */ + + +static int vlsi_irda_suspend(struct pci_dev *pdev, u32 state) +{ + struct net_device *ndev = pci_get_drvdata(pdev); + vlsi_irda_dev_t *idev; + + if (state < 1 || state > 3 ) { + IRDA_ERROR("%s - %s: invalid pm state request: %u\n", + __FUNCTION__, PCIDEV_NAME(pdev), state); + return 0; + } + if (!ndev) { + IRDA_ERROR("%s - %s: no netdevice \n", + __FUNCTION__, PCIDEV_NAME(pdev)); + return 0; + } + idev = ndev->priv; + down(&idev->sem); + if (pdev->current_state != 0) { /* already suspended */ + if (state > pdev->current_state) { /* simply go deeper */ + pci_set_power_state(pdev,state); + pdev->current_state = state; + } + else + IRDA_ERROR("%s - %s: invalid suspend request %u -> %u\n", __FUNCTION__, PCIDEV_NAME(pdev), pdev->current_state, state); + up(&idev->sem); + return 0; + } + + if (netif_running(ndev)) { + netif_device_detach(ndev); + vlsi_stop_hw(idev); + pci_save_state(pdev); + if (!idev->new_baud) + /* remember speed settings to restore on resume */ + idev->new_baud = idev->baud; + } + + pci_set_power_state(pdev,state); + pdev->current_state = state; + idev->resume_ok = 1; + up(&idev->sem); + return 0; +} + +static int vlsi_irda_resume(struct pci_dev *pdev) +{ + struct net_device *ndev = pci_get_drvdata(pdev); + vlsi_irda_dev_t *idev; + + if (!ndev) { + IRDA_ERROR("%s - %s: no netdevice \n", + __FUNCTION__, PCIDEV_NAME(pdev)); + return 0; + } + idev = ndev->priv; + down(&idev->sem); + if (pdev->current_state == 0) { + up(&idev->sem); + IRDA_WARNING("%s - %s: already resumed\n", + __FUNCTION__, PCIDEV_NAME(pdev)); + return 0; + } + + pci_set_power_state(pdev, 0); + pdev->current_state = 0; + + if (!idev->resume_ok) { + /* should be obsolete now - but used to happen due to: + * - pci layer initially setting pdev->current_state = 4 (unknown) + * - pci layer did not walk the save_state-tree (might be APM problem) + * so we could not refuse to suspend from undefined state + * - vlsi_irda_suspend detected invalid state and refused to save + * configuration for resume - but was too late to stop suspending + * - vlsi_irda_resume got screwed when trying to resume from garbage + * + * now we explicitly set pdev->current_state = 0 after enabling the + * device and independently resume_ok should catch any garbage config. + */ + IRDA_WARNING("%s - hm, nothing to resume?\n", __FUNCTION__); + up(&idev->sem); + return 0; + } + + if (netif_running(ndev)) { + pci_restore_state(pdev); + vlsi_start_hw(idev); + netif_device_attach(ndev); + } + idev->resume_ok = 0; + up(&idev->sem); + return 0; +} + +#endif /* CONFIG_PM */ + +/*********************************************************/ + +static struct pci_driver vlsi_irda_driver = { + .name = drivername, + .id_table = vlsi_irda_table, + .probe = vlsi_irda_probe, + .remove = __devexit_p(vlsi_irda_remove), +#ifdef CONFIG_PM + .suspend = vlsi_irda_suspend, + .resume = vlsi_irda_resume, +#endif +}; + +#define PROC_DIR ("driver/" DRIVER_NAME) + +static int __init vlsi_mod_init(void) +{ + int i, ret; + + if (clksrc < 0 || clksrc > 3) { + IRDA_ERROR("%s: invalid clksrc=%d\n", drivername, clksrc); + return -1; + } + + for (i = 0; i < 2; i++) { + switch(ringsize[i]) { + case 4: + case 8: + case 16: + case 32: + case 64: + break; + default: + IRDA_WARNING("%s: invalid %s ringsize %d, using default=8", drivername, (i)?"rx":"tx", ringsize[i]); + ringsize[i] = 8; + break; + } + } + + sirpulse = !!sirpulse; + + /* create_proc_entry returns NULL if !CONFIG_PROC_FS. + * Failure to create the procfs entry is handled like running + * without procfs - it's not required for the driver to work. + */ + vlsi_proc_root = create_proc_entry(PROC_DIR, S_IFDIR, NULL); + if (vlsi_proc_root) { + /* protect registered procdir against module removal. + * Because we are in the module init path there's no race + * window after create_proc_entry (and no barrier needed). + */ + vlsi_proc_root->owner = THIS_MODULE; + } + + ret = pci_module_init(&vlsi_irda_driver); + + if (ret && vlsi_proc_root) + remove_proc_entry(PROC_DIR, NULL); + return ret; + +} + +static void __exit vlsi_mod_exit(void) +{ + pci_unregister_driver(&vlsi_irda_driver); + if (vlsi_proc_root) + remove_proc_entry(PROC_DIR, NULL); +} + +module_init(vlsi_mod_init); +module_exit(vlsi_mod_exit); diff --git a/drivers/net/irda/vlsi_ir.h b/drivers/net/irda/vlsi_ir.h new file mode 100644 index 000000000000..414694abf588 --- /dev/null +++ b/drivers/net/irda/vlsi_ir.h @@ -0,0 +1,798 @@ + +/********************************************************************* + * + * vlsi_ir.h: VLSI82C147 PCI IrDA controller driver for Linux + * + * Version: 0.5 + * + * Copyright (c) 2001-2003 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. + * + * 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 IRDA_VLSI_FIR_H +#define IRDA_VLSI_FIR_H + +/* ================================================================ + * compatibility stuff + */ + +/* definitions not present in pci_ids.h */ + +#ifndef PCI_CLASS_WIRELESS_IRDA +#define PCI_CLASS_WIRELESS_IRDA 0x0d00 +#endif + +#ifndef PCI_CLASS_SUBCLASS_MASK +#define PCI_CLASS_SUBCLASS_MASK 0xffff +#endif + +/* in recent 2.5 interrupt handlers have non-void return value */ +#ifndef IRQ_RETVAL +typedef void irqreturn_t; +#define IRQ_NONE +#define IRQ_HANDLED +#define IRQ_RETVAL(x) +#endif + +/* some stuff need to check kernelversion. Not all 2.5 stuff was present + * in early 2.5.x - the test is merely to separate 2.4 from 2.5 + */ +#include <linux/version.h> + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + +/* PDE() introduced in 2.5.4 */ +#ifdef CONFIG_PROC_FS +#define PDE(inode) ((inode)->u.generic_ip) +#endif + +/* irda crc16 calculation exported in 2.5.42 */ +#define irda_calc_crc16(fcs,buf,len) (GOOD_FCS) + +/* we use this for unified pci device name access */ +#define PCIDEV_NAME(pdev) ((pdev)->name) + +#else /* 2.5 or later */ + +/* recent 2.5/2.6 stores pci device names at varying places ;-) */ +#ifdef CONFIG_PCI_NAMES +/* human readable name */ +#define PCIDEV_NAME(pdev) ((pdev)->pretty_name) +#else +/* whatever we get from the associated struct device - bus:slot:dev.fn id */ +#define PCIDEV_NAME(pdev) (pci_name(pdev)) +#endif + +#endif + +/* ================================================================ */ + +/* non-standard PCI registers */ + +enum vlsi_pci_regs { + VLSI_PCI_CLKCTL = 0x40, /* chip clock input control */ + VLSI_PCI_MSTRPAGE = 0x41, /* addr [31:24] for all busmaster cycles */ + VLSI_PCI_IRMISC = 0x42 /* mainly legacy UART related */ +}; + +/* ------------------------------------------ */ + +/* VLSI_PCI_CLKCTL: Clock Control Register (u8, rw) */ + +/* Three possible clock sources: either on-chip 48MHz PLL or + * external clock applied to EXTCLK pin. External clock may + * be either 48MHz or 40MHz, which is indicated by XCKSEL. + * CLKSTP controls whether the selected clock source gets + * connected to the IrDA block. + * + * On my HP OB-800 the BIOS sets external 40MHz clock as source + * when IrDA enabled and I've never detected any PLL lock success. + * Apparently the 14.3...MHz OSC input required for the PLL to work + * is not connected and the 40MHz EXTCLK is provided externally. + * At least this is what makes the driver working for me. + */ + +enum vlsi_pci_clkctl { + + /* PLL control */ + + CLKCTL_PD_INV = 0x04, /* PD#: inverted power down signal, + * i.e. PLL is powered, if PD_INV set */ + CLKCTL_LOCK = 0x40, /* (ro) set, if PLL is locked */ + + /* clock source selection */ + + CLKCTL_EXTCLK = 0x20, /* set to select external clock input, not PLL */ + CLKCTL_XCKSEL = 0x10, /* set to indicate EXTCLK is 40MHz, not 48MHz */ + + /* IrDA block control */ + + CLKCTL_CLKSTP = 0x80, /* set to disconnect from selected clock source */ + CLKCTL_WAKE = 0x08 /* set to enable wakeup feature: whenever IR activity + * is detected, PD_INV gets set(?) and CLKSTP cleared */ +}; + +/* ------------------------------------------ */ + +/* VLSI_PCI_MSTRPAGE: Master Page Register (u8, rw) and busmastering stuff */ + +#define DMA_MASK_USED_BY_HW 0xffffffff +#define DMA_MASK_MSTRPAGE 0x00ffffff +#define MSTRPAGE_VALUE (DMA_MASK_MSTRPAGE >> 24) + + /* PCI busmastering is somewhat special for this guy - in short: + * + * We select to operate using fixed MSTRPAGE=0, use ISA DMA + * address restrictions to make the PCI BM api aware of this, + * but ensure the hardware is dealing with real 32bit access. + * + * In detail: + * The chip executes normal 32bit busmaster cycles, i.e. + * drives all 32 address lines. These addresses however are + * composed of [0:23] taken from various busaddr-pointers + * and [24:31] taken from the MSTRPAGE register in the VLSI82C147 + * config space. Therefore _all_ busmastering must be + * targeted to/from one single 16MB (busaddr-) superpage! + * The point is to make sure all the allocations for memory + * locations with busmaster access (ring descriptors, buffers) + * are indeed bus-mappable to the same 16MB range (for x86 this + * means they must reside in the same 16MB physical memory address + * range). The only constraint we have which supports "several objects + * mappable to common 16MB range" paradigma, is the old ISA DMA + * restriction to the first 16MB of physical address range. + * Hence the approach here is to enable PCI busmaster support using + * the correct 32bit dma-mask used by the chip. Afterwards the device's + * dma-mask gets restricted to 24bit, which must be honoured somehow by + * all allocations for memory areas to be exposed to the chip ... + * + * Note: + * Don't be surprised to get "Setting latency timer..." messages every + * time when PCI busmastering is enabled for the chip. + * The chip has its PCI latency timer RO fixed at 0 - which is not a + * problem here, because it is never requesting _burst_ transactions. + */ + +/* ------------------------------------------ */ + +/* VLSI_PCIIRMISC: IR Miscellaneous Register (u8, rw) */ + +/* legacy UART emulation - not used by this driver - would require: + * (see below for some register-value definitions) + * + * - IRMISC_UARTEN must be set to enable UART address decoding + * - IRMISC_UARTSEL configured + * - IRCFG_MASTER must be cleared + * - IRCFG_SIR must be set + * - IRENABLE_PHYANDCLOCK must be asserted 0->1 (and hence IRENABLE_SIR_ON) + */ + +enum vlsi_pci_irmisc { + + /* IR transceiver control */ + + IRMISC_IRRAIL = 0x40, /* (ro?) IR rail power indication (and control?) + * 0=3.3V / 1=5V. Probably set during power-on? + * unclear - not touched by driver */ + IRMISC_IRPD = 0x08, /* transceiver power down, if set */ + + /* legacy UART control */ + + IRMISC_UARTTST = 0x80, /* UART test mode - "always write 0" */ + IRMISC_UARTEN = 0x04, /* enable UART address decoding */ + + /* bits [1:0] IRMISC_UARTSEL to select legacy UART address */ + + IRMISC_UARTSEL_3f8 = 0x00, + IRMISC_UARTSEL_2f8 = 0x01, + IRMISC_UARTSEL_3e8 = 0x02, + IRMISC_UARTSEL_2e8 = 0x03 +}; + +/* ================================================================ */ + +/* registers mapped to 32 byte PCI IO space */ + +/* note: better access all registers at the indicated u8/u16 size + * although some of them contain only 1 byte of information. + * some of them (particaluarly PROMPT and IRCFG) ignore + * access when using the wrong addressing mode! + */ + +enum vlsi_pio_regs { + VLSI_PIO_IRINTR = 0x00, /* interrupt enable/request (u8, rw) */ + VLSI_PIO_RINGPTR = 0x02, /* rx/tx ring pointer (u16, ro) */ + VLSI_PIO_RINGBASE = 0x04, /* [23:10] of ring address (u16, rw) */ + VLSI_PIO_RINGSIZE = 0x06, /* rx/tx ring size (u16, rw) */ + VLSI_PIO_PROMPT = 0x08, /* triggers ring processing (u16, wo) */ + /* 0x0a-0x0f: reserved / duplicated UART regs */ + VLSI_PIO_IRCFG = 0x10, /* configuration select (u16, rw) */ + VLSI_PIO_SIRFLAG = 0x12, /* BOF/EOF for filtered SIR (u16, ro) */ + VLSI_PIO_IRENABLE = 0x14, /* enable and status register (u16, rw/ro) */ + VLSI_PIO_PHYCTL = 0x16, /* physical layer current status (u16, ro) */ + VLSI_PIO_NPHYCTL = 0x18, /* next physical layer select (u16, rw) */ + VLSI_PIO_MAXPKT = 0x1a, /* [11:0] max len for packet receive (u16, rw) */ + VLSI_PIO_RCVBCNT = 0x1c /* current receive-FIFO byte count (u16, ro) */ + /* 0x1e-0x1f: reserved / duplicated UART regs */ +}; + +/* ------------------------------------------ */ + +/* VLSI_PIO_IRINTR: Interrupt Register (u8, rw) */ + +/* enable-bits: + * 1 = enable / 0 = disable + * interrupt condition bits: + * set according to corresponding interrupt source + * (regardless of the state of the enable bits) + * enable bit status indicates whether interrupt gets raised + * write-to-clear + * note: RPKTINT and TPKTINT behave different in legacy UART mode (which we don't use :-) + */ + +enum vlsi_pio_irintr { + IRINTR_ACTEN = 0x80, /* activity interrupt enable */ + IRINTR_ACTIVITY = 0x40, /* activity monitor (traffic detected) */ + IRINTR_RPKTEN = 0x20, /* receive packet interrupt enable*/ + IRINTR_RPKTINT = 0x10, /* rx-packet transfered from fifo to memory finished */ + IRINTR_TPKTEN = 0x08, /* transmit packet interrupt enable */ + IRINTR_TPKTINT = 0x04, /* last bit of tx-packet+crc shifted to ir-pulser */ + IRINTR_OE_EN = 0x02, /* UART rx fifo overrun error interrupt enable */ + IRINTR_OE_INT = 0x01 /* UART rx fifo overrun error (read LSR to clear) */ +}; + +/* we use this mask to check whether the (shared PCI) interrupt is ours */ + +#define IRINTR_INT_MASK (IRINTR_ACTIVITY|IRINTR_RPKTINT|IRINTR_TPKTINT) + +/* ------------------------------------------ */ + +/* VLSI_PIO_RINGPTR: Ring Pointer Read-Back Register (u16, ro) */ + +/* _both_ ring pointers are indices relative to the _entire_ rx,tx-ring! + * i.e. the referenced descriptor is located + * at RINGBASE + PTR * sizeof(descr) for rx and tx + * therefore, the tx-pointer has offset MAX_RING_DESCR + */ + +#define MAX_RING_DESCR 64 /* tx, rx rings may contain up to 64 descr each */ + +#define RINGPTR_RX_MASK (MAX_RING_DESCR-1) +#define RINGPTR_TX_MASK ((MAX_RING_DESCR-1)<<8) + +#define RINGPTR_GET_RX(p) ((p)&RINGPTR_RX_MASK) +#define RINGPTR_GET_TX(p) (((p)&RINGPTR_TX_MASK)>>8) + +/* ------------------------------------------ */ + +/* VLSI_PIO_RINGBASE: Ring Pointer Base Address Register (u16, ro) */ + +/* Contains [23:10] part of the ring base (bus-) address + * which must be 1k-alinged. [31:24] is taken from + * VLSI_PCI_MSTRPAGE above. + * The controller initiates non-burst PCI BM cycles to + * fetch and update the descriptors in the ring. + * Once fetched, the descriptor remains cached onchip + * until it gets closed and updated due to the ring + * processing state machine. + * The entire ring area is split in rx and tx areas with each + * area consisting of 64 descriptors of 8 bytes each. + * The rx(tx) ring is located at ringbase+0 (ringbase+64*8). + */ + +#define BUS_TO_RINGBASE(p) (((p)>>10)&0x3fff) + +/* ------------------------------------------ */ + +/* VLSI_PIO_RINGSIZE: Ring Size Register (u16, rw) */ + +/* bit mask to indicate the ring size to be used for rx and tx. + * possible values encoded bits + * 4 0000 + * 8 0001 + * 16 0011 + * 32 0111 + * 64 1111 + * located at [15:12] for tx and [11:8] for rx ([7:0] unused) + * + * note: probably a good idea to have IRCFG_MSTR cleared when writing + * this so the state machines are stopped and the RINGPTR is reset! + */ + +#define SIZE_TO_BITS(num) ((((num)-1)>>2)&0x0f) +#define TX_RX_TO_RINGSIZE(tx,rx) ((SIZE_TO_BITS(tx)<<12)|(SIZE_TO_BITS(rx)<<8)) +#define RINGSIZE_TO_RXSIZE(rs) ((((rs)&0x0f00)>>6)+4) +#define RINGSIZE_TO_TXSIZE(rs) ((((rs)&0xf000)>>10)+4) + + +/* ------------------------------------------ */ + +/* VLSI_PIO_PROMPT: Ring Prompting Register (u16, write-to-start) */ + +/* writing any value kicks the ring processing state machines + * for both tx, rx rings as follows: + * - active rings (currently owning an active descriptor) + * ignore the prompt and continue + * - idle rings fetch the next descr from the ring and start + * their processing + */ + +/* ------------------------------------------ */ + +/* VLSI_PIO_IRCFG: IR Config Register (u16, rw) */ + +/* notes: + * - not more than one SIR/MIR/FIR bit must be set at any time + * - SIR, MIR, FIR and CRC16 select the configuration which will + * be applied on next 0->1 transition of IRENABLE_PHYANDCLOCK (see below). + * - besides allowing the PCI interface to execute busmaster cycles + * and therefore the ring SM to operate, the MSTR bit has side-effects: + * when MSTR is cleared, the RINGPTR's get reset and the legacy UART mode + * (in contrast to busmaster access mode) gets enabled. + * - clearing ENRX or setting ENTX while data is received may stall the + * receive fifo until ENRX reenabled _and_ another packet arrives + * - SIRFILT means the chip performs the required unwrapping of hardware + * headers (XBOF's, BOF/EOF) and un-escaping in the _receive_ direction. + * Only the resulting IrLAP payload is copied to the receive buffers - + * but with the 16bit FCS still encluded. Question remains, whether it + * was already checked or we should do it before passing the packet to IrLAP? + */ + +enum vlsi_pio_ircfg { + IRCFG_LOOP = 0x4000, /* enable loopback test mode */ + IRCFG_ENTX = 0x1000, /* transmit enable */ + IRCFG_ENRX = 0x0800, /* receive enable */ + IRCFG_MSTR = 0x0400, /* master enable */ + IRCFG_RXANY = 0x0200, /* receive any packet */ + IRCFG_CRC16 = 0x0080, /* 16bit (not 32bit) CRC select for MIR/FIR */ + IRCFG_FIR = 0x0040, /* FIR 4PPM encoding mode enable */ + IRCFG_MIR = 0x0020, /* MIR HDLC encoding mode enable */ + IRCFG_SIR = 0x0010, /* SIR encoding mode enable */ + IRCFG_SIRFILT = 0x0008, /* enable SIR decode filter (receiver unwrapping) */ + IRCFG_SIRTEST = 0x0004, /* allow SIR decode filter when not in SIR mode */ + IRCFG_TXPOL = 0x0002, /* invert tx polarity when set */ + IRCFG_RXPOL = 0x0001 /* invert rx polarity when set */ +}; + +/* ------------------------------------------ */ + +/* VLSI_PIO_SIRFLAG: SIR Flag Register (u16, ro) */ + +/* register contains hardcoded BOF=0xc0 at [7:0] and EOF=0xc1 at [15:8] + * which is used for unwrapping received frames in SIR decode-filter mode + */ + +/* ------------------------------------------ */ + +/* VLSI_PIO_IRENABLE: IR Enable Register (u16, rw/ro) */ + +/* notes: + * - IREN acts as gate for latching the configured IR mode information + * from IRCFG and IRPHYCTL when IREN=reset and applying them when + * IREN gets set afterwards. + * - ENTXST reflects IRCFG_ENTX + * - ENRXST = IRCFG_ENRX && (!IRCFG_ENTX || IRCFG_LOOP) + */ + +enum vlsi_pio_irenable { + IRENABLE_PHYANDCLOCK = 0x8000, /* enable IR phy and gate the mode config (rw) */ + IRENABLE_CFGER = 0x4000, /* mode configuration error (ro) */ + IRENABLE_FIR_ON = 0x2000, /* FIR on status (ro) */ + IRENABLE_MIR_ON = 0x1000, /* MIR on status (ro) */ + IRENABLE_SIR_ON = 0x0800, /* SIR on status (ro) */ + IRENABLE_ENTXST = 0x0400, /* transmit enable status (ro) */ + IRENABLE_ENRXST = 0x0200, /* Receive enable status (ro) */ + IRENABLE_CRC16_ON = 0x0100 /* 16bit (not 32bit) CRC enabled status (ro) */ +}; + +#define IRENABLE_MASK 0xff00 /* Read mask */ + +/* ------------------------------------------ */ + +/* VLSI_PIO_PHYCTL: IR Physical Layer Current Control Register (u16, ro) */ + +/* read-back of the currently applied physical layer status. + * applied from VLSI_PIO_NPHYCTL at rising edge of IRENABLE_PHYANDCLOCK + * contents identical to VLSI_PIO_NPHYCTL (see below) + */ + +/* ------------------------------------------ */ + +/* VLSI_PIO_NPHYCTL: IR Physical Layer Next Control Register (u16, rw) */ + +/* latched during IRENABLE_PHYANDCLOCK=0 and applied at 0-1 transition + * + * consists of BAUD[15:10], PLSWID[9:5] and PREAMB[4:0] bits defined as follows: + * + * SIR-mode: BAUD = (115.2kHz / baudrate) - 1 + * PLSWID = (pulsetime * freq / (BAUD+1)) - 1 + * where pulsetime is the requested IrPHY pulse width + * and freq is 8(16)MHz for 40(48)MHz primary input clock + * PREAMB: don't care for SIR + * + * The nominal SIR pulse width is 3/16 bit time so we have PLSWID=12 + * fixed for all SIR speeds at 40MHz input clock (PLSWID=24 at 48MHz). + * IrPHY also allows shorter pulses down to the nominal pulse duration + * at 115.2kbaud (minus some tolerance) which is 1.41 usec. + * Using the expression PLSWID = 12/(BAUD+1)-1 (multiplied by two for 48MHz) + * we get the minimum acceptable PLSWID values according to the VLSI + * specification, which provides 1.5 usec pulse width for all speeds (except + * for 2.4kbaud getting 6usec). This is fine with IrPHY v1.3 specs and + * reduces the transceiver power which drains the battery. At 9.6kbaud for + * example this amounts to more than 90% battery power saving! + * + * MIR-mode: BAUD = 0 + * PLSWID = 9(10) for 40(48) MHz input clock + * to get nominal MIR pulse width + * PREAMB = 1 + * + * FIR-mode: BAUD = 0 + * PLSWID: don't care + * PREAMB = 15 + */ + +#define PHYCTL_BAUD_SHIFT 10 +#define PHYCTL_BAUD_MASK 0xfc00 +#define PHYCTL_PLSWID_SHIFT 5 +#define PHYCTL_PLSWID_MASK 0x03e0 +#define PHYCTL_PREAMB_SHIFT 0 +#define PHYCTL_PREAMB_MASK 0x001f + +#define PHYCTL_TO_BAUD(bwp) (((bwp)&PHYCTL_BAUD_MASK)>>PHYCTL_BAUD_SHIFT) +#define PHYCTL_TO_PLSWID(bwp) (((bwp)&PHYCTL_PLSWID_MASK)>>PHYCTL_PLSWID_SHIFT) +#define PHYCTL_TO_PREAMB(bwp) (((bwp)&PHYCTL_PREAMB_MASK)>>PHYCTL_PREAMB_SHIFT) + +#define BWP_TO_PHYCTL(b,w,p) ((((b)<<PHYCTL_BAUD_SHIFT)&PHYCTL_BAUD_MASK) \ + | (((w)<<PHYCTL_PLSWID_SHIFT)&PHYCTL_PLSWID_MASK) \ + | (((p)<<PHYCTL_PREAMB_SHIFT)&PHYCTL_PREAMB_MASK)) + +#define BAUD_BITS(br) ((115200/(br))-1) + +static inline unsigned +calc_width_bits(unsigned baudrate, unsigned widthselect, unsigned clockselect) +{ + unsigned tmp; + + if (widthselect) /* nominal 3/16 puls width */ + return (clockselect) ? 12 : 24; + + tmp = ((clockselect) ? 12 : 24) / (BAUD_BITS(baudrate)+1); + + /* intermediate result of integer division needed here */ + + return (tmp>0) ? (tmp-1) : 0; +} + +#define PHYCTL_SIR(br,ws,cs) BWP_TO_PHYCTL(BAUD_BITS(br),calc_width_bits((br),(ws),(cs)),0) +#define PHYCTL_MIR(cs) BWP_TO_PHYCTL(0,((cs)?9:10),1) +#define PHYCTL_FIR BWP_TO_PHYCTL(0,0,15) + +/* quite ugly, I know. But implementing these calculations here avoids + * having magic numbers in the code and allows some playing with pulsewidths + * without risk to violate the standards. + * FWIW, here is the table for reference: + * + * baudrate BAUD min-PLSWID nom-PLSWID PREAMB + * 2400 47 0(0) 12(24) 0 + * 9600 11 0(0) 12(24) 0 + * 19200 5 1(2) 12(24) 0 + * 38400 2 3(6) 12(24) 0 + * 57600 1 5(10) 12(24) 0 + * 115200 0 11(22) 12(24) 0 + * MIR 0 - 9(10) 1 + * FIR 0 - 0 15 + * + * note: x(y) means x-value for 40MHz / y-value for 48MHz primary input clock + */ + +/* ------------------------------------------ */ + + +/* VLSI_PIO_MAXPKT: Maximum Packet Length register (u16, rw) */ + +/* maximum acceptable length for received packets */ + +/* hw imposed limitation - register uses only [11:0] */ +#define MAX_PACKET_LENGTH 0x0fff + +/* IrLAP I-field (apparently not defined elsewhere) */ +#define IRDA_MTU 2048 + +/* complete packet consists of A(1)+C(1)+I(<=IRDA_MTU) */ +#define IRLAP_SKB_ALLOCSIZE (1+1+IRDA_MTU) + +/* the buffers we use to exchange frames with the hardware need to be + * larger than IRLAP_SKB_ALLOCSIZE because we may have up to 4 bytes FCS + * appended and, in SIR mode, a lot of frame wrapping bytes. The worst + * case appears to be a SIR packet with I-size==IRDA_MTU and all bytes + * requiring to be escaped to provide transparency. Furthermore, the peer + * might ask for quite a number of additional XBOFs: + * up to 115+48 XBOFS 163 + * regular BOF 1 + * A-field 1 + * C-field 1 + * I-field, IRDA_MTU, all escaped 4096 + * FCS (16 bit at SIR, escaped) 4 + * EOF 1 + * AFAICS nothing in IrLAP guarantees A/C field not to need escaping + * (f.e. 0xc0/0xc1 - i.e. BOF/EOF - are legal values there) so in the + * worst case we have 4269 bytes total frame size. + * However, the VLSI uses 12 bits only for all buffer length values, + * which limits the maximum useable buffer size <= 4095. + * Note this is not a limitation in the receive case because we use + * the SIR filtering mode where the hw unwraps the frame and only the + * bare packet+fcs is stored into the buffer - in contrast to the SIR + * tx case where we have to pass frame-wrapped packets to the hw. + * If this would ever become an issue in real life, the only workaround + * I see would be using the legacy UART emulation in SIR mode. + */ + +#define XFER_BUF_SIZE MAX_PACKET_LENGTH + +/* ------------------------------------------ */ + +/* VLSI_PIO_RCVBCNT: Receive Byte Count Register (u16, ro) */ + +/* receive packet counter gets incremented on every non-filtered + * byte which was put in the receive fifo and reset for each + * new packet. Used to decide whether we are just in the middle + * of receiving + */ + +/* better apply the [11:0] mask when reading, as some docs say the + * reserved [15:12] would return 1 when reading - which is wrong AFAICS + */ +#define RCVBCNT_MASK 0x0fff + +/******************************************************************/ + +/* descriptors for rx/tx ring + * + * accessed by hardware - don't change! + * + * the descriptor is owned by hardware, when the ACTIVE status bit + * is set and nothing (besides reading status to test the bit) + * shall be done. The bit gets cleared by hw, when the descriptor + * gets closed. Premature reaping of descriptors owned be the chip + * can be achieved by disabling IRCFG_MSTR + * + * Attention: Writing addr overwrites status! + * + * ### FIXME: depends on endianess (but there ain't no non-i586 ob800 ;-) + */ + +struct ring_descr_hw { + volatile u16 rd_count; /* tx/rx count [11:0] */ + u16 reserved; + union { + u32 addr; /* [23:0] of the buffer's busaddress */ + struct { + u8 addr_res[3]; + volatile u8 status; /* descriptor status */ + } rd_s __attribute__((packed)); + } rd_u __attribute((packed)); +} __attribute__ ((packed)); + +#define rd_addr rd_u.addr +#define rd_status rd_u.rd_s.status + +/* ring descriptor status bits */ + +#define RD_ACTIVE 0x80 /* descriptor owned by hw (both TX,RX) */ + +/* TX ring descriptor status */ + +#define RD_TX_DISCRC 0x40 /* do not send CRC (for SIR) */ +#define RD_TX_BADCRC 0x20 /* force a bad CRC */ +#define RD_TX_PULSE 0x10 /* send indication pulse after this frame (MIR/FIR) */ +#define RD_TX_FRCEUND 0x08 /* force underrun */ +#define RD_TX_CLRENTX 0x04 /* clear ENTX after this frame */ +#define RD_TX_UNDRN 0x01 /* TX fifo underrun (probably PCI problem) */ + +/* RX ring descriptor status */ + +#define RD_RX_PHYERR 0x40 /* physical encoding error */ +#define RD_RX_CRCERR 0x20 /* CRC error (MIR/FIR) */ +#define RD_RX_LENGTH 0x10 /* frame exceeds buffer length */ +#define RD_RX_OVER 0x08 /* RX fifo overrun (probably PCI problem) */ +#define RD_RX_SIRBAD 0x04 /* EOF missing: BOF follows BOF (SIR, filtered) */ + +#define RD_RX_ERROR 0x7c /* any error in received frame */ + +/* the memory required to hold the 2 descriptor rings */ +#define HW_RING_AREA_SIZE (2 * MAX_RING_DESCR * sizeof(struct ring_descr_hw)) + +/******************************************************************/ + +/* sw-ring descriptors consists of a bus-mapped transfer buffer with + * associated skb and a pointer to the hw entry descriptor + */ + +struct ring_descr { + struct ring_descr_hw *hw; + struct sk_buff *skb; + void *buf; +}; + +/* wrappers for operations on hw-exposed ring descriptors + * access to the hw-part of the descriptors must use these. + */ + +static inline int rd_is_active(struct ring_descr *rd) +{ + return ((rd->hw->rd_status & RD_ACTIVE) != 0); +} + +static inline void rd_activate(struct ring_descr *rd) +{ + rd->hw->rd_status |= RD_ACTIVE; +} + +static inline void rd_set_status(struct ring_descr *rd, u8 s) +{ + rd->hw->rd_status = s; /* may pass ownership to the hardware */ +} + +static inline void rd_set_addr_status(struct ring_descr *rd, dma_addr_t a, u8 s) +{ + /* order is important for two reasons: + * - overlayed: writing addr overwrites status + * - we want to write status last so we have valid address in + * case status has RD_ACTIVE set + */ + + if ((a & ~DMA_MASK_MSTRPAGE)>>24 != MSTRPAGE_VALUE) { + IRDA_ERROR("%s: pci busaddr inconsistency!\n", __FUNCTION__); + dump_stack(); + return; + } + + a &= DMA_MASK_MSTRPAGE; /* clear highbyte to make sure we won't write + * to status - just in case MSTRPAGE_VALUE!=0 + */ + rd->hw->rd_addr = cpu_to_le32(a); + wmb(); + rd_set_status(rd, s); /* may pass ownership to the hardware */ +} + +static inline void rd_set_count(struct ring_descr *rd, u16 c) +{ + rd->hw->rd_count = cpu_to_le16(c); +} + +static inline u8 rd_get_status(struct ring_descr *rd) +{ + return rd->hw->rd_status; +} + +static inline dma_addr_t rd_get_addr(struct ring_descr *rd) +{ + dma_addr_t a; + + a = le32_to_cpu(rd->hw->rd_addr); + return (a & DMA_MASK_MSTRPAGE) | (MSTRPAGE_VALUE << 24); +} + +static inline u16 rd_get_count(struct ring_descr *rd) +{ + return le16_to_cpu(rd->hw->rd_count); +} + +/******************************************************************/ + +/* sw descriptor rings for rx, tx: + * + * operations follow producer-consumer paradigm, with the hw + * in the middle doing the processing. + * ring size must be power of two. + * + * producer advances r->tail after inserting for processing + * consumer advances r->head after removing processed rd + * ring is empty if head==tail / full if (tail+1)==head + */ + +struct vlsi_ring { + struct pci_dev *pdev; + int dir; + unsigned len; + unsigned size; + unsigned mask; + atomic_t head, tail; + struct ring_descr *rd; +}; + +/* ring processing helpers */ + +static inline struct ring_descr *ring_last(struct vlsi_ring *r) +{ + int t; + + t = atomic_read(&r->tail) & r->mask; + return (((t+1) & r->mask) == (atomic_read(&r->head) & r->mask)) ? NULL : &r->rd[t]; +} + +static inline struct ring_descr *ring_put(struct vlsi_ring *r) +{ + atomic_inc(&r->tail); + return ring_last(r); +} + +static inline struct ring_descr *ring_first(struct vlsi_ring *r) +{ + int h; + + h = atomic_read(&r->head) & r->mask; + return (h == (atomic_read(&r->tail) & r->mask)) ? NULL : &r->rd[h]; +} + +static inline struct ring_descr *ring_get(struct vlsi_ring *r) +{ + atomic_inc(&r->head); + return ring_first(r); +} + +/******************************************************************/ + +/* our private compound VLSI-PCI-IRDA device information */ + +typedef struct vlsi_irda_dev { + struct pci_dev *pdev; + struct net_device_stats stats; + + struct irlap_cb *irlap; + + struct qos_info qos; + + unsigned mode; + int baud, new_baud; + + dma_addr_t busaddr; + void *virtaddr; + struct vlsi_ring *tx_ring, *rx_ring; + + struct timeval last_rx; + + spinlock_t lock; + struct semaphore sem; + + u8 resume_ok; + struct proc_dir_entry *proc_entry; + +} vlsi_irda_dev_t; + +/********************************************************/ + +/* the remapped error flags we use for returning from frame + * post-processing in vlsi_process_tx/rx() after it was completed + * by the hardware. These functions either return the >=0 number + * of transfered bytes in case of success or the negative (-) + * of the or'ed error flags. + */ + +#define VLSI_TX_DROP 0x0001 +#define VLSI_TX_FIFO 0x0002 + +#define VLSI_RX_DROP 0x0100 +#define VLSI_RX_OVER 0x0200 +#define VLSI_RX_LENGTH 0x0400 +#define VLSI_RX_FRAME 0x0800 +#define VLSI_RX_CRC 0x1000 + +/********************************************************/ + +#endif /* IRDA_VLSI_FIR_H */ + diff --git a/drivers/net/irda/w83977af.h b/drivers/net/irda/w83977af.h new file mode 100644 index 000000000000..04476c2e9121 --- /dev/null +++ b/drivers/net/irda/w83977af.h @@ -0,0 +1,53 @@ +#ifndef W83977AF_H +#define W83977AF_H + +#define W977_EFIO_BASE 0x370 +#define W977_EFIO2_BASE 0x3f0 +#define W977_DEVICE_IR 0x06 + + +/* + * Enter extended function mode + */ +static inline void w977_efm_enter(unsigned int efio) +{ + outb(0x87, efio); + outb(0x87, efio); +} + +/* + * Select a device to configure + */ + +static inline void w977_select_device(__u8 devnum, unsigned int efio) +{ + outb(0x07, efio); + outb(devnum, efio+1); +} + +/* + * Write a byte to a register + */ +static inline void w977_write_reg(__u8 reg, __u8 value, unsigned int efio) +{ + outb(reg, efio); + outb(value, efio+1); +} + +/* + * read a byte from a register + */ +static inline __u8 w977_read_reg(__u8 reg, unsigned int efio) +{ + outb(reg, efio); + return inb(efio+1); +} + +/* + * Exit extended function mode + */ +static inline void w977_efm_exit(unsigned int efio) +{ + outb(0xAA, efio); +} +#endif diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c new file mode 100644 index 000000000000..0ea65c4c6f85 --- /dev/null +++ b/drivers/net/irda/w83977af_ir.c @@ -0,0 +1,1379 @@ +/********************************************************************* + * + * Filename: w83977af_ir.c + * Version: 1.0 + * Description: FIR driver for the Winbond W83977AF Super I/O chip + * Status: Experimental. + * Author: Paul VanderSpek + * Created at: Wed Nov 4 11:46:16 1998 + * Modified at: Fri Jan 28 12:10:59 2000 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no> + * Copyright (c) 1998-1999 Rebel.com + * + * 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 Paul VanderSpek nor Rebel.com admit liability nor provide + * warranty for any of this software. This material is provided "AS-IS" + * and at no charge. + * + * If you find bugs in this file, its very likely that the same bug + * will also be in pc87108.c since the implementations are quite + * similar. + * + * Notice that all functions that needs to access the chip in _any_ + * way, must save BSR register on entry, and restore it on exit. + * It is _very_ important to follow this policy! + * + * __u8 bank; + * + * bank = inb( iobase+BSR); + * + * do_your_stuff_here(); + * + * outb( bank, iobase+BSR); + * + ********************************************************************/ + +#include <linux/module.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/rtnetlink.h> +#include <linux/dma-mapping.h> + +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/byteorder.h> + +#include <net/irda/irda.h> +#include <net/irda/wrapper.h> +#include <net/irda/irda_device.h> +#include "w83977af.h" +#include "w83977af_ir.h" + +#ifdef CONFIG_ARCH_NETWINDER /* Adjust to NetWinder differences */ +#undef CONFIG_NETWINDER_TX_DMA_PROBLEMS /* Not needed */ +#define CONFIG_NETWINDER_RX_DMA_PROBLEMS /* Must have this one! */ +#endif +#undef CONFIG_USE_INTERNAL_TIMER /* Just cannot make that timer work */ +#define CONFIG_USE_W977_PNP /* Currently needed */ +#define PIO_MAX_SPEED 115200 + +static char *driver_name = "w83977af_ir"; +static int qos_mtt_bits = 0x07; /* 1 ms or more */ + +#define CHIP_IO_EXTENT 8 + +static unsigned int io[] = { 0x180, ~0, ~0, ~0 }; +#ifdef CONFIG_ARCH_NETWINDER /* Adjust to NetWinder differences */ +static unsigned int irq[] = { 6, 0, 0, 0 }; +#else +static unsigned int irq[] = { 11, 0, 0, 0 }; +#endif +static unsigned int dma[] = { 1, 0, 0, 0 }; +static unsigned int efbase[] = { W977_EFIO_BASE, W977_EFIO2_BASE }; +static unsigned int efio = W977_EFIO_BASE; + +static struct w83977af_ir *dev_self[] = { NULL, NULL, NULL, NULL}; + +/* Some prototypes */ +static int w83977af_open(int i, unsigned int iobase, unsigned int irq, + unsigned int dma); +static int w83977af_close(struct w83977af_ir *self); +static int w83977af_probe(int iobase, int irq, int dma); +static int w83977af_dma_receive(struct w83977af_ir *self); +static int w83977af_dma_receive_complete(struct w83977af_ir *self); +static int w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev); +static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size); +static void w83977af_dma_write(struct w83977af_ir *self, int iobase); +static void w83977af_change_speed(struct w83977af_ir *self, __u32 speed); +static int w83977af_is_receiving(struct w83977af_ir *self); + +static int w83977af_net_open(struct net_device *dev); +static int w83977af_net_close(struct net_device *dev); +static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static struct net_device_stats *w83977af_net_get_stats(struct net_device *dev); + +/* + * Function w83977af_init () + * + * Initialize chip. Just try to find out how many chips we are dealing with + * and where they are + */ +static int __init w83977af_init(void) +{ + int i; + + IRDA_DEBUG(0, "%s()\n", __FUNCTION__ ); + + for (i=0; (io[i] < 2000) && (i < 4); i++) { + if (w83977af_open(i, io[i], irq[i], dma[i]) == 0) + return 0; + } + return -ENODEV; +} + +/* + * Function w83977af_cleanup () + * + * Close all configured chips + * + */ +static void __exit w83977af_cleanup(void) +{ + int i; + + IRDA_DEBUG(4, "%s()\n", __FUNCTION__ ); + + for (i=0; i < 4; i++) { + if (dev_self[i]) + w83977af_close(dev_self[i]); + } +} + +/* + * Function w83977af_open (iobase, irq) + * + * Open driver instance + * + */ +int w83977af_open(int i, unsigned int iobase, unsigned int irq, + unsigned int dma) +{ + struct net_device *dev; + struct w83977af_ir *self; + int err; + + IRDA_DEBUG(0, "%s()\n", __FUNCTION__ ); + + /* Lock the port that we need */ + if (!request_region(iobase, CHIP_IO_EXTENT, driver_name)) { + IRDA_DEBUG(0, "%s(), can't get iobase of 0x%03x\n", + __FUNCTION__ , iobase); + return -ENODEV; + } + + if (w83977af_probe(iobase, irq, dma) == -1) { + err = -1; + goto err_out; + } + /* + * Allocate new instance of the driver + */ + dev = alloc_irdadev(sizeof(struct w83977af_ir)); + if (dev == NULL) { + printk( KERN_ERR "IrDA: Can't allocate memory for " + "IrDA control block!\n"); + err = -ENOMEM; + goto err_out; + } + + self = dev->priv; + spin_lock_init(&self->lock); + + + /* Initialize IO */ + self->io.fir_base = iobase; + self->io.irq = irq; + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.dma = dma; + self->io.fifo_size = 32; + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&self->qos); + + /* The only value we must override it the baudrate */ + + /* FIXME: The HP HDLS-1100 does not support 1152000! */ + self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| + IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); + + /* The HP HDLS-1100 needs 1 ms according to the specs */ + self->qos.min_turn_time.bits = qos_mtt_bits; + irda_qos_bits_to_value(&self->qos); + + /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ + self->rx_buff.truesize = 14384; + self->tx_buff.truesize = 4000; + + /* Allocate memory if needed */ + self->rx_buff.head = + dma_alloc_coherent(NULL, self->rx_buff.truesize, + &self->rx_buff_dma, GFP_KERNEL); + if (self->rx_buff.head == NULL) { + err = -ENOMEM; + goto err_out1; + } + + memset(self->rx_buff.head, 0, self->rx_buff.truesize); + + self->tx_buff.head = + dma_alloc_coherent(NULL, self->tx_buff.truesize, + &self->tx_buff_dma, GFP_KERNEL); + if (self->tx_buff.head == NULL) { + err = -ENOMEM; + goto err_out2; + } + 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->netdev = dev; + + /* Keep track of module usage */ + SET_MODULE_OWNER(dev); + + /* Override the network functions we need to use */ + dev->hard_start_xmit = w83977af_hard_xmit; + dev->open = w83977af_net_open; + dev->stop = w83977af_net_close; + dev->do_ioctl = w83977af_net_ioctl; + dev->get_stats = w83977af_net_get_stats; + + err = register_netdev(dev); + if (err) { + IRDA_ERROR("%s(), register_netdevice() failed!\n", __FUNCTION__); + goto err_out3; + } + IRDA_MESSAGE("IrDA: Registered device %s\n", dev->name); + + /* Need to store self somewhere */ + dev_self[i] = self; + + return 0; +err_out3: + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); +err_out2: + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); +err_out1: + free_netdev(dev); +err_out: + release_region(iobase, CHIP_IO_EXTENT); + return err; +} + +/* + * Function w83977af_close (self) + * + * Close driver instance + * + */ +static int w83977af_close(struct w83977af_ir *self) +{ + int iobase; + + IRDA_DEBUG(0, "%s()\n", __FUNCTION__ ); + + iobase = self->io.fir_base; + +#ifdef CONFIG_USE_W977_PNP + /* enter PnP configuration mode */ + w977_efm_enter(efio); + + w977_select_device(W977_DEVICE_IR, efio); + + /* Deactivate device */ + w977_write_reg(0x30, 0x00, efio); + + w977_efm_exit(efio); +#endif /* CONFIG_USE_W977_PNP */ + + /* Remove netdevice */ + unregister_netdev(self->netdev); + + /* Release the PORT that this driver is using */ + IRDA_DEBUG(0 , "%s(), Releasing Region %03x\n", + __FUNCTION__ , self->io.fir_base); + release_region(self->io.fir_base, self->io.fir_ext); + + if (self->tx_buff.head) + dma_free_coherent(NULL, self->tx_buff.truesize, + self->tx_buff.head, self->tx_buff_dma); + + if (self->rx_buff.head) + dma_free_coherent(NULL, self->rx_buff.truesize, + self->rx_buff.head, self->rx_buff_dma); + + free_netdev(self->netdev); + + return 0; +} + +int w83977af_probe( int iobase, int irq, int dma) +{ + int version; + int i; + + for (i=0; i < 2; i++) { + IRDA_DEBUG( 0, "%s()\n", __FUNCTION__ ); +#ifdef CONFIG_USE_W977_PNP + /* Enter PnP configuration mode */ + w977_efm_enter(efbase[i]); + + w977_select_device(W977_DEVICE_IR, efbase[i]); + + /* Configure PnP port, IRQ, and DMA channel */ + w977_write_reg(0x60, (iobase >> 8) & 0xff, efbase[i]); + w977_write_reg(0x61, (iobase) & 0xff, efbase[i]); + + w977_write_reg(0x70, irq, efbase[i]); +#ifdef CONFIG_ARCH_NETWINDER + /* Netwinder uses 1 higher than Linux */ + w977_write_reg(0x74, dma+1, efbase[i]); +#else + w977_write_reg(0x74, dma, efbase[i]); +#endif /*CONFIG_ARCH_NETWINDER */ + w977_write_reg(0x75, 0x04, efbase[i]); /* Disable Tx DMA */ + + /* Set append hardware CRC, enable IR bank selection */ + w977_write_reg(0xf0, APEDCRC|ENBNKSEL, efbase[i]); + + /* Activate device */ + w977_write_reg(0x30, 0x01, efbase[i]); + + w977_efm_exit(efbase[i]); +#endif /* CONFIG_USE_W977_PNP */ + /* Disable Advanced mode */ + switch_bank(iobase, SET2); + outb(iobase+2, 0x00); + + /* Turn on UART (global) interrupts */ + switch_bank(iobase, SET0); + outb(HCR_EN_IRQ, iobase+HCR); + + /* Switch to advanced mode */ + switch_bank(iobase, SET2); + outb(inb(iobase+ADCR1) | ADCR1_ADV_SL, iobase+ADCR1); + + /* Set default IR-mode */ + switch_bank(iobase, SET0); + outb(HCR_SIR, iobase+HCR); + + /* Read the Advanced IR ID */ + switch_bank(iobase, SET3); + version = inb(iobase+AUID); + + /* Should be 0x1? */ + if (0x10 == (version & 0xf0)) { + efio = efbase[i]; + + /* Set FIFO size to 32 */ + switch_bank(iobase, SET2); + outb(ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2); + + /* Set FIFO threshold to TX17, RX16 */ + switch_bank(iobase, SET0); + outb(UFR_RXTL|UFR_TXTL|UFR_TXF_RST|UFR_RXF_RST| + UFR_EN_FIFO,iobase+UFR); + + /* Receiver frame length */ + switch_bank(iobase, SET4); + outb(2048 & 0xff, iobase+6); + outb((2048 >> 8) & 0x1f, iobase+7); + + /* + * Init HP HSDL-1100 transceiver. + * + * Set IRX_MSL since we have 2 * receive paths IRRX, + * and IRRXH. Clear IRSL0D since we want IRSL0 * to + * be a input pin used for IRRXH + * + * IRRX pin 37 connected to receiver + * IRTX pin 38 connected to transmitter + * FIRRX pin 39 connected to receiver (IRSL0) + * CIRRX pin 40 connected to pin 37 + */ + switch_bank(iobase, SET7); + outb(0x40, iobase+7); + + IRDA_MESSAGE("W83977AF (IR) driver loaded. " + "Version: 0x%02x\n", version); + + return 0; + } else { + /* Try next extented function register address */ + IRDA_DEBUG( 0, "%s(), Wrong chip version", __FUNCTION__ ); + } + } + return -1; +} + +void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) +{ + int ir_mode = HCR_SIR; + int iobase; + __u8 set; + + iobase = self->io.fir_base; + + /* Update accounting for new speed */ + self->io.speed = speed; + + /* Save current bank */ + set = inb(iobase+SSR); + + /* Disable interrupts */ + switch_bank(iobase, SET0); + outb(0, iobase+ICR); + + /* Select Set 2 */ + switch_bank(iobase, SET2); + outb(0x00, iobase+ABHL); + + switch (speed) { + case 9600: outb(0x0c, iobase+ABLL); break; + case 19200: outb(0x06, iobase+ABLL); break; + case 38400: outb(0x03, iobase+ABLL); break; + case 57600: outb(0x02, iobase+ABLL); break; + case 115200: outb(0x01, iobase+ABLL); break; + case 576000: + ir_mode = HCR_MIR_576; + IRDA_DEBUG(0, "%s(), handling baud of 576000\n", __FUNCTION__ ); + break; + case 1152000: + ir_mode = HCR_MIR_1152; + IRDA_DEBUG(0, "%s(), handling baud of 1152000\n", __FUNCTION__ ); + break; + case 4000000: + ir_mode = HCR_FIR; + IRDA_DEBUG(0, "%s(), handling baud of 4000000\n", __FUNCTION__ ); + break; + default: + ir_mode = HCR_FIR; + IRDA_DEBUG(0, "%s(), unknown baud rate of %d\n", __FUNCTION__ , speed); + break; + } + + /* Set speed mode */ + switch_bank(iobase, SET0); + outb(ir_mode, iobase+HCR); + + /* set FIFO size to 32 */ + switch_bank(iobase, SET2); + outb(ADCR2_RXFS32|ADCR2_TXFS32, iobase+ADCR2); + + /* set FIFO threshold to TX17, RX16 */ + switch_bank(iobase, SET0); + outb(0x00, iobase+UFR); /* Reset */ + outb(UFR_EN_FIFO, iobase+UFR); /* First we must enable FIFO */ + outb(0xa7, iobase+UFR); + + netif_wake_queue(self->netdev); + + /* Enable some interrupts so we can receive frames */ + switch_bank(iobase, SET0); + if (speed > PIO_MAX_SPEED) { + outb(ICR_EFSFI, iobase+ICR); + w83977af_dma_receive(self); + } else + outb(ICR_ERBRI, iobase+ICR); + + /* Restore SSR */ + outb(set, iobase+SSR); +} + +/* + * Function w83977af_hard_xmit (skb, dev) + * + * Sets up a DMA transfer to send the current frame. + * + */ +int w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct w83977af_ir *self; + __s32 speed; + int iobase; + __u8 set; + int mtt; + + self = (struct w83977af_ir *) dev->priv; + + iobase = self->io.fir_base; + + IRDA_DEBUG(4, "%s(%ld), skb->len=%d\n", __FUNCTION__ , jiffies, + (int) skb->len); + + /* Lock transmit buffer */ + netif_stop_queue(dev); + + /* 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) { + w83977af_change_speed(self, speed); + dev->trans_start = jiffies; + dev_kfree_skb(skb); + return 0; + } else + self->new_speed = speed; + } + + /* Save current set */ + set = inb(iobase+SSR); + + /* Decide if we should use PIO or DMA transfer */ + if (self->io.speed > PIO_MAX_SPEED) { + self->tx_buff.data = self->tx_buff.head; + memcpy(self->tx_buff.data, skb->data, skb->len); + self->tx_buff.len = skb->len; + + mtt = irda_get_mtt(skb); +#ifdef CONFIG_USE_INTERNAL_TIMER + if (mtt > 50) { + /* Adjust for timer resolution */ + mtt /= 1000+1; + + /* Setup timer */ + switch_bank(iobase, SET4); + outb(mtt & 0xff, iobase+TMRL); + outb((mtt >> 8) & 0x0f, iobase+TMRH); + + /* Start timer */ + outb(IR_MSL_EN_TMR, iobase+IR_MSL); + self->io.direction = IO_XMIT; + + /* Enable timer interrupt */ + switch_bank(iobase, SET0); + outb(ICR_ETMRI, iobase+ICR); + } else { +#endif + IRDA_DEBUG(4, "%s(%ld), mtt=%d\n", __FUNCTION__ , jiffies, mtt); + if (mtt) + udelay(mtt); + + /* Enable DMA interrupt */ + switch_bank(iobase, SET0); + outb(ICR_EDMAI, iobase+ICR); + w83977af_dma_write(self, iobase); +#ifdef CONFIG_USE_INTERNAL_TIMER + } +#endif + } else { + self->tx_buff.data = self->tx_buff.head; + self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, + self->tx_buff.truesize); + + /* Add interrupt on tx low level (will fire immediately) */ + switch_bank(iobase, SET0); + outb(ICR_ETXTHI, iobase+ICR); + } + dev->trans_start = jiffies; + dev_kfree_skb(skb); + + /* Restore set register */ + outb(set, iobase+SSR); + + return 0; +} + +/* + * Function w83977af_dma_write (self, iobase) + * + * Send frame using DMA + * + */ +static void w83977af_dma_write(struct w83977af_ir *self, int iobase) +{ + __u8 set; +#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS + unsigned long flags; + __u8 hcr; +#endif + IRDA_DEBUG(4, "%s(), len=%d\n", __FUNCTION__ , self->tx_buff.len); + + /* Save current set */ + set = inb(iobase+SSR); + + /* Disable DMA */ + switch_bank(iobase, SET0); + outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR); + + /* Choose transmit DMA channel */ + switch_bank(iobase, SET2); + outb(ADCR1_D_CHSW|/*ADCR1_DMA_F|*/ADCR1_ADV_SL, iobase+ADCR1); +#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS + spin_lock_irqsave(&self->lock, flags); + + disable_dma(self->io.dma); + clear_dma_ff(self->io.dma); + set_dma_mode(self->io.dma, DMA_MODE_READ); + set_dma_addr(self->io.dma, self->tx_buff_dma); + set_dma_count(self->io.dma, self->tx_buff.len); +#else + irda_setup_dma(self->io.dma, self->tx_buff_dma, self->tx_buff.len, + DMA_MODE_WRITE); +#endif + self->io.direction = IO_XMIT; + + /* Enable DMA */ + switch_bank(iobase, SET0); +#ifdef CONFIG_NETWINDER_TX_DMA_PROBLEMS + hcr = inb(iobase+HCR); + outb(hcr | HCR_EN_DMA, iobase+HCR); + enable_dma(self->io.dma); + spin_unlock_irqrestore(&self->lock, flags); +#else + outb(inb(iobase+HCR) | HCR_EN_DMA | HCR_TX_WT, iobase+HCR); +#endif + + /* Restore set register */ + outb(set, iobase+SSR); +} + +/* + * Function w83977af_pio_write (iobase, buf, len, fifo_size) + * + * + * + */ +static int w83977af_pio_write(int iobase, __u8 *buf, int len, int fifo_size) +{ + int actual = 0; + __u8 set; + + IRDA_DEBUG(4, "%s()\n", __FUNCTION__ ); + + /* Save current bank */ + set = inb(iobase+SSR); + + switch_bank(iobase, SET0); + if (!(inb_p(iobase+USR) & USR_TSRE)) { + IRDA_DEBUG(4, + "%s(), warning, FIFO not empty yet!\n", __FUNCTION__ ); + + fifo_size -= 17; + IRDA_DEBUG(4, "%s(), %d bytes left in tx fifo\n", + __FUNCTION__ , fifo_size); + } + + /* Fill FIFO with current frame */ + while ((fifo_size-- > 0) && (actual < len)) { + /* Transmit next byte */ + outb(buf[actual++], iobase+TBR); + } + + IRDA_DEBUG(4, "%s(), fifo_size %d ; %d sent of %d\n", + __FUNCTION__ , fifo_size, actual, len); + + /* Restore bank */ + outb(set, iobase+SSR); + + return actual; +} + +/* + * Function w83977af_dma_xmit_complete (self) + * + * The transfer of a frame in finished. So do the necessary things + * + * + */ +static void w83977af_dma_xmit_complete(struct w83977af_ir *self) +{ + int iobase; + __u8 set; + + IRDA_DEBUG(4, "%s(%ld)\n", __FUNCTION__ , jiffies); + + IRDA_ASSERT(self != NULL, return;); + + iobase = self->io.fir_base; + + /* Save current set */ + set = inb(iobase+SSR); + + /* Disable DMA */ + switch_bank(iobase, SET0); + outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR); + + /* Check for underrrun! */ + if (inb(iobase+AUDR) & AUDR_UNDR) { + IRDA_DEBUG(0, "%s(), Transmit underrun!\n", __FUNCTION__ ); + + self->stats.tx_errors++; + self->stats.tx_fifo_errors++; + + /* Clear bit, by writing 1 to it */ + outb(AUDR_UNDR, iobase+AUDR); + } else + self->stats.tx_packets++; + + + if (self->new_speed) { + w83977af_change_speed(self, self->new_speed); + self->new_speed = 0; + } + + /* Unlock tx_buff and request another frame */ + /* Tell the network layer, that we want more frames */ + netif_wake_queue(self->netdev); + + /* Restore set */ + outb(set, iobase+SSR); +} + +/* + * Function w83977af_dma_receive (self) + * + * Get ready for receiving a frame. The device will initiate a DMA + * if it starts to receive a frame. + * + */ +int w83977af_dma_receive(struct w83977af_ir *self) +{ + int iobase; + __u8 set; +#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS + unsigned long flags; + __u8 hcr; +#endif + IRDA_ASSERT(self != NULL, return -1;); + + IRDA_DEBUG(4, "%s\n", __FUNCTION__ ); + + iobase= self->io.fir_base; + + /* Save current set */ + set = inb(iobase+SSR); + + /* Disable DMA */ + switch_bank(iobase, SET0); + outb(inb(iobase+HCR) & ~HCR_EN_DMA, iobase+HCR); + + /* Choose DMA Rx, DMA Fairness, and Advanced mode */ + switch_bank(iobase, SET2); + outb((inb(iobase+ADCR1) & ~ADCR1_D_CHSW)/*|ADCR1_DMA_F*/|ADCR1_ADV_SL, + iobase+ADCR1); + + self->io.direction = IO_RECV; + self->rx_buff.data = self->rx_buff.head; + +#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS + spin_lock_irqsave(&self->lock, flags); + + disable_dma(self->io.dma); + clear_dma_ff(self->io.dma); + set_dma_mode(self->io.dma, DMA_MODE_READ); + set_dma_addr(self->io.dma, self->rx_buff_dma); + set_dma_count(self->io.dma, self->rx_buff.truesize); +#else + irda_setup_dma(self->io.dma, self->rx_buff_dma, self->rx_buff.truesize, + DMA_MODE_READ); +#endif + /* + * Reset Rx FIFO. This will also flush the ST_FIFO, it's very + * important that we don't reset the Tx FIFO since it might not + * be finished transmitting yet + */ + switch_bank(iobase, SET0); + outb(UFR_RXTL|UFR_TXTL|UFR_RXF_RST|UFR_EN_FIFO, iobase+UFR); + self->st_fifo.len = self->st_fifo.tail = self->st_fifo.head = 0; + + /* Enable DMA */ + switch_bank(iobase, SET0); +#ifdef CONFIG_NETWINDER_RX_DMA_PROBLEMS + hcr = inb(iobase+HCR); + outb(hcr | HCR_EN_DMA, iobase+HCR); + enable_dma(self->io.dma); + spin_unlock_irqrestore(&self->lock, flags); +#else + outb(inb(iobase+HCR) | HCR_EN_DMA, iobase+HCR); +#endif + /* Restore set */ + outb(set, iobase+SSR); + + return 0; +} + +/* + * Function w83977af_receive_complete (self) + * + * Finished with receiving a frame + * + */ +int w83977af_dma_receive_complete(struct w83977af_ir *self) +{ + struct sk_buff *skb; + struct st_fifo *st_fifo; + int len; + int iobase; + __u8 set; + __u8 status; + + IRDA_DEBUG(4, "%s\n", __FUNCTION__ ); + + st_fifo = &self->st_fifo; + + iobase = self->io.fir_base; + + /* Save current set */ + set = inb(iobase+SSR); + + iobase = self->io.fir_base; + + /* Read status FIFO */ + switch_bank(iobase, SET5); + while ((status = inb(iobase+FS_FO)) & FS_FO_FSFDR) { + st_fifo->entries[st_fifo->tail].status = status; + + st_fifo->entries[st_fifo->tail].len = inb(iobase+RFLFL); + st_fifo->entries[st_fifo->tail].len |= inb(iobase+RFLFH) << 8; + + st_fifo->tail++; + st_fifo->len++; + } + + while (st_fifo->len) { + /* Get first entry */ + status = st_fifo->entries[st_fifo->head].status; + len = st_fifo->entries[st_fifo->head].len; + st_fifo->head++; + st_fifo->len--; + + /* Check for errors */ + if (status & FS_FO_ERR_MSK) { + if (status & FS_FO_LST_FR) { + /* Add number of lost frames to stats */ + self->stats.rx_errors += len; + } else { + /* Skip frame */ + self->stats.rx_errors++; + + self->rx_buff.data += len; + + if (status & FS_FO_MX_LEX) + self->stats.rx_length_errors++; + + if (status & FS_FO_PHY_ERR) + self->stats.rx_frame_errors++; + + if (status & FS_FO_CRC_ERR) + self->stats.rx_crc_errors++; + } + /* The errors below can be reported in both cases */ + if (status & FS_FO_RX_OV) + self->stats.rx_fifo_errors++; + + if (status & FS_FO_FSF_OV) + self->stats.rx_fifo_errors++; + + } else { + /* Check if we have transferred all data to memory */ + switch_bank(iobase, SET0); + if (inb(iobase+USR) & USR_RDR) { +#ifdef CONFIG_USE_INTERNAL_TIMER + /* Put this entry back in fifo */ + st_fifo->head--; + st_fifo->len++; + st_fifo->entries[st_fifo->head].status = status; + st_fifo->entries[st_fifo->head].len = len; + + /* Restore set register */ + outb(set, iobase+SSR); + + return FALSE; /* I'll be back! */ +#else + udelay(80); /* Should be enough!? */ +#endif + } + + skb = dev_alloc_skb(len+1); + if (skb == NULL) { + printk(KERN_INFO + "%s(), memory squeeze, dropping frame.\n", __FUNCTION__); + /* Restore set register */ + outb(set, iobase+SSR); + + return FALSE; + } + + /* Align to 20 bytes */ + skb_reserve(skb, 1); + + /* Copy frame without CRC */ + if (self->io.speed < 4000000) { + skb_put(skb, len-2); + memcpy(skb->data, self->rx_buff.data, len-2); + } else { + skb_put(skb, len-4); + memcpy(skb->data, self->rx_buff.data, len-4); + } + + /* Move to next frame */ + self->rx_buff.data += len; + self->stats.rx_packets++; + + skb->dev = self->netdev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + netif_rx(skb); + self->netdev->last_rx = jiffies; + } + } + /* Restore set register */ + outb(set, iobase+SSR); + + return TRUE; +} + +/* + * Function pc87108_pio_receive (self) + * + * Receive all data in receiver FIFO + * + */ +static void w83977af_pio_receive(struct w83977af_ir *self) +{ + __u8 byte = 0x00; + int iobase; + + IRDA_DEBUG(4, "%s()\n", __FUNCTION__ ); + + IRDA_ASSERT(self != NULL, return;); + + iobase = self->io.fir_base; + + /* Receive all characters in Rx FIFO */ + do { + byte = inb(iobase+RBR); + async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, + byte); + } while (inb(iobase+USR) & USR_RDR); /* Data available */ +} + +/* + * Function w83977af_sir_interrupt (self, eir) + * + * Handle SIR interrupt + * + */ +static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr) +{ + int actual; + __u8 new_icr = 0; + __u8 set; + int iobase; + + IRDA_DEBUG(4, "%s(), isr=%#x\n", __FUNCTION__ , isr); + + iobase = self->io.fir_base; + /* Transmit FIFO low on data */ + if (isr & ISR_TXTH_I) { + /* Write data left in transmit buffer */ + actual = w83977af_pio_write(self->io.fir_base, + self->tx_buff.data, + self->tx_buff.len, + self->io.fifo_size); + + self->tx_buff.data += actual; + self->tx_buff.len -= actual; + + self->io.direction = IO_XMIT; + + /* Check if finished */ + if (self->tx_buff.len > 0) { + new_icr |= ICR_ETXTHI; + } else { + set = inb(iobase+SSR); + switch_bank(iobase, SET0); + outb(AUDR_SFEND, iobase+AUDR); + outb(set, iobase+SSR); + + self->stats.tx_packets++; + + /* Feed me more packets */ + netif_wake_queue(self->netdev); + new_icr |= ICR_ETBREI; + } + } + /* Check if transmission has completed */ + if (isr & ISR_TXEMP_I) { + /* Check if we need to change the speed? */ + if (self->new_speed) { + IRDA_DEBUG(2, + "%s(), Changing speed!\n", __FUNCTION__ ); + w83977af_change_speed(self, self->new_speed); + self->new_speed = 0; + } + + /* Turn around and get ready to receive some data */ + self->io.direction = IO_RECV; + new_icr |= ICR_ERBRI; + } + + /* Rx FIFO threshold or timeout */ + if (isr & ISR_RXTH_I) { + w83977af_pio_receive(self); + + /* Keep receiving */ + new_icr |= ICR_ERBRI; + } + return new_icr; +} + +/* + * Function pc87108_fir_interrupt (self, eir) + * + * Handle MIR/FIR interrupt + * + */ +static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr) +{ + __u8 new_icr = 0; + __u8 set; + int iobase; + + iobase = self->io.fir_base; + set = inb(iobase+SSR); + + /* End of frame detected in FIFO */ + if (isr & (ISR_FEND_I|ISR_FSF_I)) { + if (w83977af_dma_receive_complete(self)) { + + /* Wait for next status FIFO interrupt */ + new_icr |= ICR_EFSFI; + } else { + /* DMA not finished yet */ + + /* Set timer value, resolution 1 ms */ + switch_bank(iobase, SET4); + outb(0x01, iobase+TMRL); /* 1 ms */ + outb(0x00, iobase+TMRH); + + /* Start timer */ + outb(IR_MSL_EN_TMR, iobase+IR_MSL); + + new_icr |= ICR_ETMRI; + } + } + /* Timer finished */ + if (isr & ISR_TMR_I) { + /* Disable timer */ + switch_bank(iobase, SET4); + outb(0, iobase+IR_MSL); + + /* Clear timer event */ + /* switch_bank(iobase, SET0); */ +/* outb(ASCR_CTE, iobase+ASCR); */ + + /* Check if this is a TX timer interrupt */ + if (self->io.direction == IO_XMIT) { + w83977af_dma_write(self, iobase); + + new_icr |= ICR_EDMAI; + } else { + /* Check if DMA has now finished */ + w83977af_dma_receive_complete(self); + + new_icr |= ICR_EFSFI; + } + } + /* Finished with DMA */ + if (isr & ISR_DMA_I) { + w83977af_dma_xmit_complete(self); + + /* Check if there are more frames to be transmitted */ + /* if (irda_device_txqueue_empty(self)) { */ + + /* Prepare for receive + * + * ** Netwinder Tx DMA likes that we do this anyway ** + */ + w83977af_dma_receive(self); + new_icr = ICR_EFSFI; + /* } */ + } + + /* Restore set */ + outb(set, iobase+SSR); + + return new_icr; +} + +/* + * Function w83977af_interrupt (irq, dev_id, regs) + * + * An interrupt from the chip has arrived. Time to do some work + * + */ +static irqreturn_t w83977af_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct w83977af_ir *self; + __u8 set, icr, isr; + int iobase; + + if (!dev) { + printk(KERN_WARNING "%s: irq %d for unknown device.\n", + driver_name, irq); + return IRQ_NONE; + } + self = (struct w83977af_ir *) dev->priv; + + iobase = self->io.fir_base; + + /* Save current bank */ + set = inb(iobase+SSR); + switch_bank(iobase, SET0); + + icr = inb(iobase+ICR); + isr = inb(iobase+ISR) & icr; /* Mask out the interesting ones */ + + outb(0, iobase+ICR); /* Disable interrupts */ + + if (isr) { + /* Dispatch interrupt handler for the current speed */ + if (self->io.speed > PIO_MAX_SPEED ) + icr = w83977af_fir_interrupt(self, isr); + else + icr = w83977af_sir_interrupt(self, isr); + } + + outb(icr, iobase+ICR); /* Restore (new) interrupts */ + outb(set, iobase+SSR); /* Restore bank register */ + return IRQ_RETVAL(isr); +} + +/* + * Function w83977af_is_receiving (self) + * + * Return TRUE is we are currently receiving a frame + * + */ +static int w83977af_is_receiving(struct w83977af_ir *self) +{ + int status = FALSE; + int iobase; + __u8 set; + + IRDA_ASSERT(self != NULL, return FALSE;); + + if (self->io.speed > 115200) { + iobase = self->io.fir_base; + + /* Check if rx FIFO is not empty */ + set = inb(iobase+SSR); + switch_bank(iobase, SET2); + if ((inb(iobase+RXFDTH) & 0x3f) != 0) { + /* We are receiving something */ + status = TRUE; + } + outb(set, iobase+SSR); + } else + status = (self->rx_buff.state != OUTSIDE_FRAME); + + return status; +} + +/* + * Function w83977af_net_open (dev) + * + * Start the device + * + */ +static int w83977af_net_open(struct net_device *dev) +{ + struct w83977af_ir *self; + int iobase; + char hwname[32]; + __u8 set; + + IRDA_DEBUG(0, "%s()\n", __FUNCTION__ ); + + IRDA_ASSERT(dev != NULL, return -1;); + self = (struct w83977af_ir *) dev->priv; + + IRDA_ASSERT(self != NULL, return 0;); + + iobase = self->io.fir_base; + + if (request_irq(self->io.irq, w83977af_interrupt, 0, dev->name, + (void *) dev)) { + return -EAGAIN; + } + /* + * Always allocate the DMA channel after the IRQ, + * and clean up on failure. + */ + if (request_dma(self->io.dma, dev->name)) { + free_irq(self->io.irq, self); + return -EAGAIN; + } + + /* Save current set */ + set = inb(iobase+SSR); + + /* Enable some interrupts so we can receive frames again */ + switch_bank(iobase, SET0); + if (self->io.speed > 115200) { + outb(ICR_EFSFI, iobase+ICR); + w83977af_dma_receive(self); + } else + outb(ICR_ERBRI, iobase+ICR); + + /* Restore bank register */ + outb(set, iobase+SSR); + + /* Ready to play! */ + netif_start_queue(dev); + + /* Give self a hardware name */ + sprintf(hwname, "w83977af @ 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); + + return 0; +} + +/* + * Function w83977af_net_close (dev) + * + * Stop the device + * + */ +static int w83977af_net_close(struct net_device *dev) +{ + struct w83977af_ir *self; + int iobase; + __u8 set; + + IRDA_DEBUG(0, "%s()\n", __FUNCTION__ ); + + IRDA_ASSERT(dev != NULL, return -1;); + + self = (struct w83977af_ir *) dev->priv; + + IRDA_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; + + disable_dma(self->io.dma); + + /* Save current set */ + set = inb(iobase+SSR); + + /* Disable interrupts */ + switch_bank(iobase, SET0); + outb(0, iobase+ICR); + + free_irq(self->io.irq, dev); + free_dma(self->io.dma); + + /* Restore bank register */ + outb(set, iobase+SSR); + + return 0; +} + +/* + * Function w83977af_net_ioctl (dev, rq, cmd) + * + * Process IOCTL commands for this device + * + */ +static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct w83977af_ir *self; + unsigned long flags; + int ret = 0; + + IRDA_ASSERT(dev != NULL, return -1;); + + self = dev->priv; + + IRDA_ASSERT(self != NULL, return -1;); + + IRDA_DEBUG(2, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__ , dev->name, cmd); + + spin_lock_irqsave(&self->lock, flags); + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + goto out; + } + w83977af_change_speed(self, irq->ifr_baudrate); + break; + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) { + ret = -EPERM; + goto out; + } + irda_device_set_media_busy(self->netdev, TRUE); + break; + case SIOCGRECEIVING: /* Check if we are receiving right now */ + irq->ifr_receiving = w83977af_is_receiving(self); + break; + default: + ret = -EOPNOTSUPP; + } +out: + spin_unlock_irqrestore(&self->lock, flags); + return ret; +} + +static struct net_device_stats *w83977af_net_get_stats(struct net_device *dev) +{ + struct w83977af_ir *self = (struct w83977af_ir *) dev->priv; + + return &self->stats; +} + +MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); +MODULE_DESCRIPTION("Winbond W83977AF IrDA Device Driver"); +MODULE_LICENSE("GPL"); + + +module_param(qos_mtt_bits, int, 0); +MODULE_PARM_DESC(qos_mtt_bits, "Mimimum Turn Time"); +module_param_array(io, int, NULL, 0); +MODULE_PARM_DESC(io, "Base I/O addresses"); +module_param_array(irq, int, NULL, 0); +MODULE_PARM_DESC(irq, "IRQ lines"); + +/* + * Function init_module (void) + * + * + * + */ +module_init(w83977af_init); + +/* + * Function cleanup_module (void) + * + * + * + */ +module_exit(w83977af_cleanup); diff --git a/drivers/net/irda/w83977af_ir.h b/drivers/net/irda/w83977af_ir.h new file mode 100644 index 000000000000..0b7661deafee --- /dev/null +++ b/drivers/net/irda/w83977af_ir.h @@ -0,0 +1,199 @@ +/********************************************************************* + * + * Filename: w83977af_ir.h + * Version: + * Description: + * Status: Experimental. + * Author: Paul VanderSpek + * Created at: Thu Nov 19 13:55:34 1998 + * Modified at: Tue Jan 11 13:08:19 2000 + * Modified by: Dag Brattli <dagb@cs.uit.no> + * + * Copyright (c) 1998-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. + * + * 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. + * + ********************************************************************/ + +#ifndef W83977AF_IR_H +#define W83977AF_IR_H + +#include <asm/io.h> +#include <linux/types.h> + +/* Flags for configuration register CRF0 */ +#define ENBNKSEL 0x01 +#define APEDCRC 0x02 +#define TXW4C 0x04 +#define RXW4C 0x08 + +/* Bank 0 */ +#define RBR 0x00 /* Receiver buffer register */ +#define TBR 0x00 /* Transmitter buffer register */ + +#define ICR 0x01 /* Interrupt configuration register */ +#define ICR_ERBRI 0x01 /* Receiver buffer register interrupt */ +#define ICR_ETBREI 0x02 /* Transeiver empty interrupt */ +#define ICR_EUSRI 0x04//* IR status interrupt */ +#define ICR_EHSRI 0x04 +#define ICR_ETXURI 0x04 /* Tx underrun */ +#define ICR_EDMAI 0x10 /* DMA interrupt */ +#define ICR_ETXTHI 0x20 /* Transmitter threshold interrupt */ +#define ICR_EFSFI 0x40 /* Frame status FIFO interrupt */ +#define ICR_ETMRI 0x80 /* Timer interrupt */ + +#define UFR 0x02 /* FIFO control register */ +#define UFR_EN_FIFO 0x01 /* Enable FIFO's */ +#define UFR_RXF_RST 0x02 /* Reset Rx FIFO */ +#define UFR_TXF_RST 0x04 /* Reset Tx FIFO */ +#define UFR_RXTL 0x80 /* Rx FIFO threshold (set to 16) */ +#define UFR_TXTL 0x20 /* Tx FIFO threshold (set to 17) */ + +#define ISR 0x02 /* Interrupt status register */ +#define ISR_RXTH_I 0x01 /* Receive threshold interrupt */ +#define ISR_TXEMP_I 0x02 /* Transmitter empty interrupt */ +#define ISR_FEND_I 0x04 +#define ISR_DMA_I 0x10 +#define ISR_TXTH_I 0x20 /* Transmitter threshold interrupt */ +#define ISR_FSF_I 0x40 +#define ISR_TMR_I 0x80 /* Timer interrupt */ + +#define UCR 0x03 /* Uart control register */ +#define UCR_DLS8 0x03 /* 8N1 */ + +#define SSR 0x03 /* Sets select register */ +#define SET0 UCR_DLS8 /* Make sure we keep 8N1 */ +#define SET1 (0x80|UCR_DLS8) /* Make sure we keep 8N1 */ +#define SET2 0xE0 +#define SET3 0xE4 +#define SET4 0xE8 +#define SET5 0xEC +#define SET6 0xF0 +#define SET7 0xF4 + +#define HCR 0x04 +#define HCR_MODE_MASK ~(0xD0) +#define HCR_SIR 0x60 +#define HCR_MIR_576 0x20 +#define HCR_MIR_1152 0x80 +#define HCR_FIR 0xA0 +#define HCR_EN_DMA 0x04 +#define HCR_EN_IRQ 0x08 +#define HCR_TX_WT 0x08 + +#define USR 0x05 /* IR status register */ +#define USR_RDR 0x01 /* Receive data ready */ +#define USR_TSRE 0x40 /* Transmitter empty? */ + +#define AUDR 0x07 +#define AUDR_SFEND 0x08 /* Set a frame end */ +#define AUDR_RXBSY 0x20 /* Rx busy */ +#define AUDR_UNDR 0x40 /* Transeiver underrun */ + +/* Set 2 */ +#define ABLL 0x00 /* Advanced baud rate divisor latch (low byte) */ +#define ABHL 0x01 /* Advanced baud rate divisor latch (high byte) */ + +#define ADCR1 0x02 +#define ADCR1_ADV_SL 0x01 +#define ADCR1_D_CHSW 0x08 /* the specs are wrong. its bit 3, not 4 */ +#define ADCR1_DMA_F 0x02 + +#define ADCR2 0x04 +#define ADCR2_TXFS32 0x01 +#define ADCR2_RXFS32 0x04 + +#define RXFDTH 0x07 + +/* Set 3 */ +#define AUID 0x00 + +/* Set 4 */ +#define TMRL 0x00 /* Timer value register (low byte) */ +#define TMRH 0x01 /* Timer value register (high byte) */ + +#define IR_MSL 0x02 /* Infrared mode select */ +#define IR_MSL_EN_TMR 0x01 /* Enable timer */ + +#define TFRLL 0x04 /* Transmitter frame length (low byte) */ +#define TFRLH 0x05 /* Transmitter frame length (high byte) */ +#define RFRLL 0x06 /* Receiver frame length (low byte) */ +#define RFRLH 0x07 /* Receiver frame length (high byte) */ + +/* Set 5 */ + +#define FS_FO 0x05 /* Frame status FIFO */ +#define FS_FO_FSFDR 0x80 /* Frame status FIFO data ready */ +#define FS_FO_LST_FR 0x40 /* Frame lost */ +#define FS_FO_MX_LEX 0x10 /* Max frame len exceeded */ +#define FS_FO_PHY_ERR 0x08 /* Physical layer error */ +#define FS_FO_CRC_ERR 0x04 +#define FS_FO_RX_OV 0x02 /* Receive overrun */ +#define FS_FO_FSF_OV 0x01 /* Frame status FIFO overrun */ +#define FS_FO_ERR_MSK 0x5f /* Error mask */ + +#define RFLFL 0x06 +#define RFLFH 0x07 + +/* Set 6 */ +#define IR_CFG2 0x00 +#define IR_CFG2_DIS_CRC 0x02 + +/* Set 7 */ +#define IRM_CR 0x07 /* Infrared module control register */ +#define IRM_CR_IRX_MSL 0x40 +#define IRM_CR_AF_MNT 0x80 /* Automatic format */ + +/* For storing entries in the status FIFO */ +struct st_fifo_entry { + int status; + int len; +}; + +struct st_fifo { + struct st_fifo_entry entries[10]; + int head; + int tail; + int len; +}; + +/* Private data for each instance */ +struct w83977af_ir { + struct st_fifo st_fifo; + + int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */ + int tx_len; /* Number of frames in tx_buff */ + + 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 */ + struct qos_info qos; /* QoS capabilities for this device */ + + chipio_t io; /* IrDA controller information */ + iobuff_t tx_buff; /* Transmit buffer */ + iobuff_t rx_buff; /* Receive buffer */ + dma_addr_t tx_buff_dma; + dma_addr_t rx_buff_dma; + + /* Note : currently locking is *very* incomplete, but this + * will get you started. Check in nsc-ircc.c for a proper + * locking strategy. - Jean II */ + spinlock_t lock; /* For serializing operations */ + + __u32 new_speed; +}; + +static inline void switch_bank( int iobase, int set) +{ + outb(set, iobase+SSR); +} + +#endif |