summaryrefslogtreecommitdiffstats
path: root/drivers/net/irda
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/irda')
-rw-r--r--drivers/net/irda/.cvsignore2
-rw-r--r--drivers/net/irda/Config.in17
-rw-r--r--drivers/net/irda/Makefile80
-rw-r--r--drivers/net/irda/actisys.c314
-rw-r--r--drivers/net/irda/esi.c194
-rw-r--r--drivers/net/irda/irport.c403
-rw-r--r--drivers/net/irda/irtty.c748
-rw-r--r--drivers/net/irda/pc87108.c1502
-rw-r--r--drivers/net/irda/tekram.c314
-rw-r--r--drivers/net/irda/uircc.c914
-rw-r--r--drivers/net/irda/w83977af_ir.c1273
11 files changed, 5761 insertions, 0 deletions
diff --git a/drivers/net/irda/.cvsignore b/drivers/net/irda/.cvsignore
new file mode 100644
index 000000000..857dd22e9
--- /dev/null
+++ b/drivers/net/irda/.cvsignore
@@ -0,0 +1,2 @@
+.depend
+.*.flags
diff --git a/drivers/net/irda/Config.in b/drivers/net/irda/Config.in
new file mode 100644
index 000000000..c40b6f5c8
--- /dev/null
+++ b/drivers/net/irda/Config.in
@@ -0,0 +1,17 @@
+mainmenu_option next_comment
+comment 'Infrared-port device drivers'
+
+dep_tristate 'IrTTY (uses serial driver)' CONFIG_IRTTY_SIR $CONFIG_IRDA
+if [ "$CONFIG_IRTTY_SIR" != "n" ]; then
+ comment ' Dongle support'
+ bool ' Serial dongle support' CONFIG_DONGLE
+ if [ "$CONFIG_DONGLE" != "n" ]; then
+ dep_tristate ' ESI JetEye PC dongle' CONFIG_ESI_DONGLE $CONFIG_IRTTY_SIR
+ dep_tristate ' ACTiSYS IR-220L and IR220L+ dongle' CONFIG_ACTISYS_DONGLE $CONFIG_IRTTY_SIR
+ dep_tristate ' Tekram IrMate 210B dongle' CONFIG_TEKRAM_DONGLE $CONFIG_IRTTY_SIR
+ fi
+fi
+dep_tristate ' NSC PC87108' CONFIG_NSC_FIR $CONFIG_IRDA
+dep_tristate ' Winbond W83977AF (IR)' CONFIG_WINBOND_FIR $CONFIG_IRDA
+dep_tristate ' Sharp UIRCC' CONFIG_SHARP_FIR $CONFIG_IRDA
+endmenu
diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile
new file mode 100644
index 000000000..05880d5e1
--- /dev/null
+++ b/drivers/net/irda/Makefile
@@ -0,0 +1,80 @@
+# File: drivers/irda/Makefile
+#
+# Makefile for the Linux IrDA infrared port device drivers.
+#
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+L_TARGET := irda_drivers.a
+L_OBJS :=
+M_OBJS :=
+MOD_LIST_NAME := IRDA_MODULES
+
+ifeq ($(CONFIG_IRTTY_SIR),y)
+L_OBJS += irtty.o
+else
+ ifeq ($(CONFIG_IRTTY_SIR),m)
+ M_OBJS += irtty.o
+ endif
+endif
+
+ifeq ($(CONFIG_NSC_FIR),y)
+L_OBJS += pc87108.o
+else
+ ifeq ($(CONFIG_NSC_FIR),m)
+ M_OBJS += pc87108.o
+ endif
+endif
+
+ifeq ($(CONFIG_WINBOND_FIR),y)
+L_OBJS += w83977af_ir.o
+else
+ ifeq ($(CONFIG_WINBOND_FIR),m)
+ M_OBJS += w83977af_ir.o
+ endif
+endif
+
+ifeq ($(CONFIG_SHARP_FIR),y)
+L_OBJS += uircc.o irport.o
+else
+ ifeq ($(CONFIG_SHARP_FIR),m)
+ M_OBJS += uircc.o irport.o
+ endif
+endif
+
+ifeq ($(CONFIG_ESI_DONGLE),y)
+L_OBJS += esi.o
+else
+ ifeq ($(CONFIG_ESI_DONGLE),m)
+ M_OBJS += esi.o
+ endif
+endif
+
+ifeq ($(CONFIG_TEKRAM_DONGLE),y)
+L_OBJS += tekram.o
+else
+ ifeq ($(CONFIG_TEKRAM_DONGLE),m)
+ M_OBJS += tekram.o
+ endif
+endif
+
+ifeq ($(CONFIG_ACTISYS_DONGLE),y)
+L_OBJS += actisys.o
+else
+ ifeq ($(CONFIG_ACTISYS_DONGLE),m)
+ M_OBJS += actisys.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
+clean:
+ rm -f core *.o *.a *.s
+
+
+
+
+
+
diff --git a/drivers/net/irda/actisys.c b/drivers/net/irda/actisys.c
new file mode 100644
index 000000000..97fb5fa27
--- /dev/null
+++ b/drivers/net/irda/actisys.c
@@ -0,0 +1,314 @@
+/*********************************************************************
+ *
+ * Filename: actisys.c
+ * Version: 0.4
+ * Description: Implementation for the ACTiSYS IR-220L and IR-220L+
+ * dongles
+ * Status: Experimental.
+ * Author: Dag Brattli <dagb@cs.uit.no>
+ * Created at: Wed Oct 21 20:02:35 1998
+ * Modified at: Mon Jan 18 11:30:25 1999
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ *
+ * Copyright (c) 1998 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/sched.h>
+#include <linux/init.h>
+
+#include <asm/ioctls.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/irda_device.h>
+#include <net/irda/irtty.h>
+#include <net/irda/dongle.h>
+
+static void actisys_reset( struct irda_device *dev, int unused);
+static void actisys_open( struct irda_device *idev, int type);
+static void actisys_close( struct irda_device *dev);
+static void actisys_change_speed( struct irda_device *dev, int baudrate);
+static void actisys_reset( struct irda_device *dev, int unused);
+static void actisys_init_qos( struct irda_device *idev, struct qos_info *qos);
+
+/* These are the baudrates supported */
+static int baud_rates[] = { 9600, 19200, 57600, 115200, 38400};
+
+static struct dongle dongle = {
+ ACTISYS_DONGLE,
+ actisys_open,
+ actisys_close,
+ actisys_reset,
+ actisys_change_speed,
+ actisys_init_qos,
+};
+
+__initfunc(void actisys_init(void))
+{
+ irtty_register_dongle(&dongle);
+}
+
+void actisys_cleanup(void)
+{
+ irtty_unregister_dongle(&dongle);
+}
+
+static void actisys_open( struct irda_device *idev, int type)
+{
+ strcat( idev->name, " <-> actisys");
+
+ idev->io.dongle_id = type;
+
+ MOD_INC_USE_COUNT;
+}
+
+static void actisys_close( struct irda_device *dev)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * Function actisys_change_speed (tty, baud)
+ *
+ * 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 ms.
+ * To be compatible with the new IR-220L+, we have to reset the dongle
+ * first since its not possible cycle around anymore and still be
+ * compatible with both dongles :-(
+ */
+static void actisys_change_speed( struct irda_device *idev, int baudrate)
+{
+ struct irtty_cb *self;
+ struct tty_struct *tty;
+ int arg;
+ struct termios old_termios;
+ int cflag;
+ int current_baudrate;
+ int index = 0;
+ mm_segment_t fs;
+
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ self = (struct irtty_cb *) idev->priv;
+
+ ASSERT( self != NULL, return;);
+ ASSERT( self->magic == IRTTY_MAGIC, return;);
+
+ current_baudrate = idev->qos.baud_rate.value;
+
+ /* Find the correct baudrate index for the currently used baudrate */
+ while ( current_baudrate != baud_rates[index])
+ index++;
+
+ DEBUG( 0, __FUNCTION__ "(), index=%d\n", index);
+
+ if ( !self->tty)
+ return;
+
+ tty = self->tty;
+
+ /* Cycle through avaiable baudrates until we reach the correct one */
+ while ( current_baudrate != baudrate) {
+ DEBUG( 0, __FUNCTION__ "(), current baudrate = %d\n",
+ baud_rates[index]);
+ DEBUG( 0, __FUNCTION__ "(), Clearing RTS\n");
+
+ /* Set DTR, clear RTS */
+ arg = TIOCM_DTR|TIOCM_OUT2;
+
+ fs = get_fs();
+ set_fs( get_ds());
+
+ if ( tty->driver.ioctl( tty, NULL, TIOCMSET,
+ (unsigned long) &arg)) {
+ DEBUG( 0, __FUNCTION__
+ "Error clearing RTS!\n");
+ }
+ set_fs(fs);
+
+ /* Wait at a few ms */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(2);
+
+ /* Set DTR, Set RTS */
+ arg = TIOCM_DTR | TIOCM_RTS |TIOCM_OUT2;
+
+ fs = get_fs();
+ set_fs( get_ds());
+
+ if ( tty->driver.ioctl( tty, NULL, TIOCMSET,
+ (unsigned long) &arg)) {
+ DEBUG( 0, __FUNCTION__ "Error setting RTS!\n");
+ }
+ set_fs(fs);
+
+ /* Wait at a few ms again */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout( 2);
+
+ /* Go to next baudrate */
+ if ( idev->io.dongle_id == ACTISYS_DONGLE)
+ index = (index+1) % 4; /* IR-220L */
+ else
+ index = (index+1) % 5; /* IR-220L+ */
+
+ current_baudrate = baud_rates[index];
+ }
+ DEBUG( 0, __FUNCTION__ "(), current baudrate = %d\n",
+ baud_rates[index]);
+
+ /* Now change the speed of the serial port */
+ old_termios = *(tty->termios);
+ cflag = tty->termios->c_cflag;
+
+ cflag &= ~CBAUD;
+
+ switch ( baudrate) {
+ case 9600:
+ default:
+ cflag |= B9600;
+ break;
+ case 19200:
+ cflag |= B19200;
+ break;
+ case 38400:
+ cflag |= B38400;
+ break;
+ case 57600:
+ cflag |= B57600;
+ break;
+ case 115200:
+ cflag |= B115200;
+ break;
+ }
+
+ tty->termios->c_cflag = cflag;
+
+ DEBUG( 0, __FUNCTION__ "(), Setting the speed of the serial port\n");
+ tty->driver.set_termios( tty, &old_termios);
+}
+
+/*
+ * Function actisys_reset (dev)
+ *
+ * Reset the Actisys type dongle. Warning, this function must only be
+ * called with a process context!
+ *
+ * 1. Clear DTR for a few ms.
+ *
+ */
+static void actisys_reset( struct irda_device *idev, int unused)
+{
+ struct irtty_cb *self;
+ struct tty_struct *tty;
+ int arg = 0;
+ mm_segment_t fs;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ self = (struct irtty_cb *) idev->priv;
+
+ ASSERT( self != NULL, return;);
+ ASSERT( self->magic == IRTTY_MAGIC, return;);
+
+ tty = self->tty;
+ if ( !tty)
+ return;
+
+ DEBUG( 0, __FUNCTION__ "(), Clearing DTR\n");
+ arg = TIOCM_RTS | TIOCM_OUT2;
+
+ fs = get_fs();
+ set_fs( get_ds());
+
+ if ( tty->driver.ioctl( tty, NULL, TIOCMSET,
+ (unsigned long) &arg))
+ {
+ DEBUG( 0, __FUNCTION__"(), ioctl error!\n");
+ }
+ set_fs(fs);
+
+ /* Sleep 10-20 ms*/
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(2);
+
+ DEBUG( 0, __FUNCTION__ "(), Setting DTR\n");
+ arg = TIOCM_RTS | TIOCM_DTR | TIOCM_OUT2;
+
+ fs = get_fs();
+ set_fs( get_ds());
+
+ if ( tty->driver.ioctl( tty, NULL, TIOCMSET,
+ (unsigned long) &arg))
+ {
+ DEBUG( 0, __FUNCTION__"(), ioctl error!\n");
+ }
+ set_fs(fs);
+
+ idev->qos.baud_rate.value = 9600;
+}
+
+/*
+ * Function actisys_init_qos (qos)
+ *
+ * Initialize QoS capabilities
+ *
+ */
+static void actisys_init_qos( struct irda_device *idev, struct qos_info *qos)
+{
+ 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 ( idev->io.dongle_id == ACTISYS_DONGLE)
+ qos->baud_rate.bits &= ~IR_38400;
+
+ qos->min_turn_time.bits &= 0xfe; /* All except 0 ms */
+}
+
+#ifdef MODULE
+
+/*
+ * Function init_module (void)
+ *
+ * Initialize Actisys module
+ *
+ */
+int init_module(void)
+{
+ actisys_init();
+ return(0);
+}
+
+/*
+ * Function cleanup_module (void)
+ *
+ * Cleanup Actisys module
+ *
+ */
+void cleanup_module(void)
+{
+ actisys_cleanup();
+}
+
+#endif
diff --git a/drivers/net/irda/esi.c b/drivers/net/irda/esi.c
new file mode 100644
index 000000000..a93e167aa
--- /dev/null
+++ b/drivers/net/irda/esi.c
@@ -0,0 +1,194 @@
+/*********************************************************************
+ *
+ * Filename: esi.c
+ * Version: 1.1
+ * Description: Driver for the Extended Systems JetEye PC
+ * Status: Experimental.
+ * Author: Thomas Davis, <ratbert@radiks.net>
+ * Created at: Sat Feb 21 18:54:38 1998
+ * Modified at: Mon Jan 18 11:30:32 1999
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ * Sources: esi.c
+ *
+ * Copyright (c) 1998, Thomas Davis, <ratbert@radiks.net>,
+ * Copyright (c) 1998, 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.
+ *
+ * I, Thomas Davis, provide no 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/sched.h>
+#include <linux/init.h>
+
+#include <asm/ioctls.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/irda_device.h>
+#include <net/irda/irtty.h>
+#include <net/irda/dongle.h>
+
+static void esi_open( struct irda_device *idev, int type);
+static void esi_close( struct irda_device *driver);
+static void esi_change_speed( struct irda_device *idev, int baud);
+static void esi_reset( struct irda_device *idev, int unused);
+static void esi_qos_init( struct irda_device *idev, struct qos_info *qos);
+
+static struct dongle dongle = {
+ ESI_DONGLE,
+ esi_open,
+ esi_close,
+ esi_reset,
+ esi_change_speed,
+ esi_qos_init,
+};
+
+__initfunc(void esi_init(void))
+{
+ irtty_register_dongle( &dongle);
+}
+
+void esi_cleanup(void)
+{
+ irtty_unregister_dongle( &dongle);
+}
+
+static void esi_open( struct irda_device *idev, int type)
+{
+ strcat( idev->description, " <-> esi");
+
+ idev->io.dongle_id = type;
+
+ MOD_INC_USE_COUNT;
+}
+
+static void esi_close( struct irda_device *driver)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * Function esi_change_speed (tty, baud)
+ *
+ * Set the speed for the Extended Systems JetEye PC ESI-9680 type dongle
+ *
+ */
+static void esi_change_speed( struct irda_device *idev, int baud)
+{
+ struct irtty_cb *self;
+ struct tty_struct *tty;
+ int arg = TIOCM_OUT2;
+ struct termios old_termios;
+ int cflag;
+ mm_segment_t fs;
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ self = (struct irtty_cb *) idev->priv;
+
+ ASSERT( self != NULL, return;);
+ ASSERT( self->magic == IRTTY_MAGIC, return;);
+
+ if ( !self->tty)
+ return;
+
+ tty = self->tty;
+
+ old_termios = *(tty->termios);
+ cflag = tty->termios->c_cflag;
+
+ cflag &= ~CBAUD;
+
+ switch (baud) {
+ case 19200:
+ cflag |= B19200;
+ arg |= TIOCM_DTR;
+ break;
+ case 115200:
+ cflag |= B115200;
+ arg |= TIOCM_RTS | TIOCM_DTR;
+ break;
+ case 9600:
+ default:
+ cflag |= B9600;
+ arg |= TIOCM_RTS;
+ break;
+ }
+
+ tty->termios->c_cflag = cflag;
+ tty->driver.set_termios( tty, &old_termios);
+
+ /*
+ * The ioctl function, or actually set_modem_info in serial.c
+ * expects a pointer to the argument in user space. To hack us
+ * around this we use the set_fs function to fool the routines
+ * that check if they are called from user space. We also need
+ * to send a pointer to the argument so get_user() gets happy.
+ * DB.
+ */
+ fs = get_fs();
+ set_fs( get_ds());
+
+ if ( tty->driver.ioctl( tty, NULL, TIOCMSET, (unsigned long) &arg)) {
+ DEBUG( 0, __FUNCTION__ "(), error setting ESI speed!\n");
+ }
+ set_fs(fs);
+}
+
+static void esi_reset( struct irda_device *idev, int unused)
+{
+ /* Empty */
+}
+
+/*
+ * Function esi_qos_init (qos)
+ *
+ * Init QoS capabilities for the dongle
+ *
+ */
+static void esi_qos_init( struct irda_device *idev, struct qos_info *qos)
+{
+ qos->baud_rate.bits &= IR_9600|IR_19200|IR_115200;
+}
+
+#ifdef MODULE
+
+/*
+ * Function init_module (void)
+ *
+ * Initialize ESI module
+ *
+ */
+int init_module(void)
+{
+ esi_init();
+ return(0);
+}
+
+/*
+ * Function cleanup_module (void)
+ *
+ * Cleanup ESI module
+ *
+ */
+void cleanup_module(void)
+{
+ esi_cleanup();
+}
+
+#endif
+
diff --git a/drivers/net/irda/irport.c b/drivers/net/irda/irport.c
new file mode 100644
index 000000000..ae8bcb27b
--- /dev/null
+++ b/drivers/net/irda/irport.c
@@ -0,0 +1,403 @@
+/*********************************************************************
+ *
+ * Filename: irport.c
+ * Version: 0.8
+ * 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: Sat May 23 23:15:20 1998
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ * Sources: serial.c by Linus Torvalds
+ * serial_serial.c by Aage Kvalnes <aage@cs.uit.no>
+ *
+ * Copyright (c) 1997,1998 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.
+ *
+ * NOTICE:
+ *
+ * This driver is ment to be a small serial driver to be used for
+ * IR-chipsets that has a UART (16550) compatibility mode. If your
+ * chipset is is UART only, you should probably use IrTTY instead since
+ * the Linux serial driver is probably more robust and optimized.
+ *
+ * The functions in this file may be used by FIR drivers, but this
+ * driver knows nothing about FIR drivers so don't ever insert such
+ * code into this file. Instead you should code your FIR driver in a
+ * separate file, and then call the functions in this file if
+ * necessary. This is becase it is difficult to use the Linux serial
+ * driver with a FIR driver becase they must share interrupts etc. Most
+ * FIR chipsets can function in advanced SIR mode, and you should
+ * probably use that mode instead of the UART compatibility mode (and
+ * then just forget about this file)
+ *
+ ********************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <linux/skbuff.h>
+#include <linux/serial_reg.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/irport.h>
+
+#define IO_EXTENT 8
+
+static unsigned int io[] = { 0x3e8, ~0, ~0, ~0 };
+static unsigned int irq[] = { 11, 0, 0, 0 };
+
+static void irport_write_wakeup( struct irda_device *idev);
+static int irport_write( int iobase, int fifo_size, __u8 *buf, int len);
+static void irport_receive( struct irda_device *idev);
+
+__initfunc(int irport_init(void))
+{
+/* int i; */
+
+/* for ( i=0; (io[i] < 2000) && (i < 4); i++) { */
+/* int ioaddr = io[i]; */
+/* if (check_region(ioaddr, IO_EXTENT)) */
+/* continue; */
+/* if (irport_open( i, io[i], io2[i], irq[i], dma[i]) == 0) */
+/* return 0; */
+/* } */
+/* return -ENODEV; */
+ return 0;
+}
+
+/*
+ * Function pc87108_cleanup ()
+ *
+ * Close all configured chips
+ *
+ */
+#ifdef MODULE
+static void irport_cleanup(void)
+{
+ int i;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ /* for ( i=0; i < 4; i++) { */
+/* if ( dev_self[i]) */
+/* irport_close( &(dev_self[i]->idev)); */
+/* } */
+}
+#endif /* MODULE */
+
+/*
+ * Function irport_open (void)
+ *
+ * Start IO port
+ *
+ */
+int irport_open( int iobase)
+{
+ DEBUG( 0, __FUNCTION__ "(), iobase=%#x\n", iobase);
+
+ /* Initialize UART */
+ outb( UART_LCR_WLEN8, iobase+UART_LCR); /* Reset DLAB */
+ outb(( UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase+UART_MCR);
+
+ /* Turn on interrups */
+ outb(( UART_IER_THRI |UART_IER_RLSI | UART_IER_RDI), iobase+UART_IER);
+
+ return 0;
+}
+
+/*
+ * Function irport_cleanup ()
+ *
+ * Stop IO port
+ *
+ */
+void irport_close( int iobase)
+{
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ /* Reset UART */
+ outb( 0, iobase+UART_MCR);
+
+ /* Turn off interrupts */
+ outb( 0, iobase+UART_IER);
+}
+
+/*
+ * Function irport_change_speed (idev, speed)
+ *
+ * Set speed of port to specified baudrate
+ *
+ */
+void irport_change_speed( int iobase, int speed)
+{
+ int fcr; /* FIFO control reg */
+ int lcr; /* Line control reg */
+ int divisor;
+
+ DEBUG( 0, __FUNCTION__ "(), Setting speed to: %d\n", speed);
+
+ DEBUG( 0, __FUNCTION__ "(), iobase=%#x\n", iobase);
+
+ /* Turn off interrupts */
+ outb( 0, iobase+UART_IER);
+
+ divisor = SPEED_MAX/speed;
+
+ fcr = UART_FCR_ENABLE_FIFO | 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_THRI|UART_IER_RLSI|UART_IER_RDI, iobase+UART_IER);
+}
+
+/*
+ * Function irport_interrupt (irq, dev_id, regs)
+ *
+ *
+ */
+void irport_interrupt( int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct irda_device *idev = (struct irda_device *) dev_id;
+
+ int iobase, status;
+ int iir;
+
+ DEBUG( 4, __FUNCTION__ "(), irq %d\n", irq);
+
+ if ( !idev) {
+ printk( KERN_WARNING __FUNCTION__
+ "() irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ idev->netdev.interrupt = 1;
+
+ iobase = idev->io.iobase2;
+
+ iir = inb(iobase + UART_IIR);
+ do {
+ status = inb( iobase+UART_LSR);
+
+ if (status & UART_LSR_DR) {
+ /* Receive interrupt */
+ irport_receive(idev);
+ }
+ if (status & UART_LSR_THRE) {
+ /* Transmitter ready for data */
+ irport_write_wakeup(idev);
+ }
+ } while (!(inb(iobase+UART_IIR) & UART_IIR_NO_INT));
+
+ idev->netdev.interrupt = 0;
+}
+
+/*
+ * Function irport_write_wakeup (tty)
+ *
+ * Called by the driver when there's room for more data. If we have
+ * more packets to send, we send them here.
+ *
+ */
+static void irport_write_wakeup( struct irda_device *idev)
+{
+ int actual = 0, count;
+
+ DEBUG( 4, __FUNCTION__ "() <%ld>\n", jiffies);
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ /* Finished with frame? */
+ if ( idev->tx_buff.offset == idev->tx_buff.len) {
+
+ /*
+ * Now serial buffer is almost free & we can start
+ * transmission of another packet
+ */
+ DEBUG( 4, __FUNCTION__ "(), finished with frame!\n");
+
+ idev->netdev.tbusy = 0; /* Unlock */
+ idev->stats.tx_packets++;
+
+ /* Schedule network layer, so we can get some more frames */
+ mark_bh( NET_BH);
+
+ return;
+ }
+
+ /* Write data left in transmit buffer */
+ count = idev->tx_buff.len - idev->tx_buff.offset;
+ actual = irport_write( idev->io.iobase2, idev->io.fifo_size,
+ idev->tx_buff.head, count);
+ idev->tx_buff.offset += actual;
+ idev->tx_buff.head += actual;
+}
+
+/*
+ * Function irport_write (driver)
+ *
+ *
+ *
+ */
+static int irport_write( int iobase, int fifo_size, __u8 *buf, int len)
+{
+ int actual = 0;
+
+ /* Tx FIFO should be empty! */
+ if (!(inb( iobase+UART_LSR) & UART_LSR_THRE)) {
+ DEBUG( 0, __FUNCTION__ "(), failed, fifo not empty!\n");
+ return -1;
+ }
+
+ /* Fill FIFO with current frame */
+ while (( fifo_size-- > 0) && (actual < len)) {
+ /* Transmit next byte */
+ outb( buf[actual], iobase+UART_TX);
+
+ actual++;
+ }
+
+ DEBUG( 4, __FUNCTION__ "(), fifo_size %d ; %d sent of %d\n",
+ fifo_size, actual, len);
+
+ return actual;
+}
+
+/*
+ * Function irport_xmit (void)
+ *
+ * Transmits the current frame until FIFO is full, then
+ * waits until the next transmitt interrupt, and continues until the
+ * frame is transmited.
+ */
+int irport_hard_xmit( struct sk_buff *skb, struct device *dev)
+{
+ struct irda_device *idev;
+ int xbofs;
+ int actual;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ ASSERT( dev != NULL, return -1;);
+
+ if ( dev->tbusy) {
+ DEBUG( 4, __FUNCTION__ "(), tbusy==TRUE\n");
+
+ return -EBUSY;
+ }
+
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT( idev != NULL, return -1;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+ /* Lock transmit buffer */
+ if ( irda_lock( (void *) &dev->tbusy) == FALSE)
+ return -EBUSY;
+
+ /*
+ * Transfer skb to tx_buff while wrapping, stuffing and making CRC
+ */
+ idev->tx_buff.len = async_wrap_skb( skb, idev->tx_buff.data,
+ idev->tx_buff.truesize);
+
+ actual = irport_write( idev->io.iobase2, idev->io.fifo_size,
+ idev->tx_buff.data, idev->tx_buff.len);
+
+ idev->tx_buff.offset = actual;
+ idev->tx_buff.head = idev->tx_buff.data + actual;
+
+ dev_kfree_skb( skb);
+
+ return 0;
+}
+
+/*
+ * Function irport_receive (void)
+ *
+ * Receive one frame from the infrared port
+ *
+ */
+static void irport_receive( struct irda_device *idev)
+{
+ int iobase;
+
+ if ( !idev)
+ return;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ iobase = idev->io.iobase2;
+
+ if ( idev->rx_buff.len == 0)
+ idev->rx_buff.head = idev->rx_buff.data;
+
+ /*
+ * Receive all characters in Rx FIFO, unwrap and unstuff them.
+ * async_unwrap_char will deliver all found frames
+ */
+ do {
+ async_unwrap_char( idev, inb( iobase+UART_RX));
+
+ } while ( inb( iobase+UART_LSR) & UART_LSR_DR);
+}
+
+#ifdef MODULE
+
+/*
+ * Function cleanup_module (void)
+ *
+ *
+ *
+ */
+void cleanup_module(void)
+{
+ irport_cleanup();
+}
+
+/*
+ * Function init_module (void)
+ *
+ *
+ */
+int init_module(void)
+{
+ if (irport_init() < 0) {
+ cleanup_module();
+ return 1;
+ }
+ return(0);
+}
+
+#endif /* MODULE */
+
diff --git a/drivers/net/irda/irtty.c b/drivers/net/irda/irtty.c
new file mode 100644
index 000000000..09853b5b6
--- /dev/null
+++ b/drivers/net/irda/irtty.c
@@ -0,0 +1,748 @@
+/*********************************************************************
+ *
+ * Filename: irtty.c
+ * Version: 1.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: Mon Jan 18 15:32:03 1999
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ * Sources: slip.c by Laurence Culhane, <loz@holmes.demon.co.uk>
+ * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *
+ * Copyright (c) 1998 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/config.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/tty.h>
+#include <asm/segment.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/irtty.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/irlap.h>
+#include <net/irda/timer.h>
+#include <net/irda/irda_device.h>
+#include <linux/kmod.h>
+
+static hashbin_t *irtty = NULL;
+static hashbin_t *dongles = NULL;
+
+static struct tty_ldisc irda_ldisc;
+
+static int irtty_hard_xmit( struct sk_buff *skb, struct device *dev);
+static void irtty_wait_until_sent( struct irda_device *driver);
+static int irtty_is_receiving( struct irda_device *idev);
+static int irtty_net_init( struct device *dev);
+static int irtty_net_open(struct device *dev);
+static int irtty_net_close(struct device *dev);
+
+static int irtty_open( struct tty_struct *tty);
+static void irtty_close( struct tty_struct *tty);
+static int irtty_ioctl( struct tty_struct *, void *, int, void *);
+static int irtty_receive_room( struct tty_struct *tty);
+static void irtty_change_speed( struct irda_device *dev, int baud);
+static void irtty_write_wakeup( struct tty_struct *tty);
+
+static void irtty_receive_buf( struct tty_struct *, const unsigned char *,
+ char *, int);
+char *driver_name = "irtty";
+
+__initfunc(int irtty_init(void))
+{
+ int status;
+
+ irtty = hashbin_new( HB_LOCAL);
+ if ( irtty == NULL) {
+ printk( KERN_WARNING "IrDA: Can't allocate irtty hashbin!\n");
+ return -ENOMEM;
+ }
+
+ dongles = hashbin_new( HB_LOCAL);
+ if ( dongles == NULL) {
+ printk( KERN_WARNING
+ "IrDA: Can't allocate dongles hashbin!\n");
+ return -ENOMEM;
+ }
+
+ /* Fill in our line protocol discipline, and register it */
+ memset( &irda_ldisc, 0, sizeof( irda_ldisc));
+
+ irda_ldisc.magic = TTY_LDISC_MAGIC;
+ irda_ldisc.name = "irda";
+ irda_ldisc.flags = 0;
+ irda_ldisc.open = irtty_open;
+ irda_ldisc.close = irtty_close;
+ irda_ldisc.read = NULL;
+ irda_ldisc.write = NULL;
+ irda_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *,
+ unsigned int, unsigned long)) irtty_ioctl;
+ irda_ldisc.poll = NULL;
+ irda_ldisc.receive_buf = irtty_receive_buf;
+ irda_ldisc.receive_room = irtty_receive_room;
+ irda_ldisc.write_wakeup = irtty_write_wakeup;
+
+ if (( status = tty_register_ldisc( N_IRDA, &irda_ldisc)) != 0) {
+ printk( KERN_ERR
+ "IrDA: can't register line discipline (err = %d)\n",
+ status);
+ }
+
+ return status;
+}
+
+/*
+ * Function irtty_cleanup ( )
+ *
+ * Called when the irda module is removed. Here we remove all instances
+ * of the driver, and the master array.
+ */
+#ifdef MODULE
+static void irtty_cleanup(void)
+{
+ int ret;
+
+ /*
+ * Unregister tty line-discipline
+ */
+ if (( ret = tty_register_ldisc( N_IRDA, NULL))) {
+ printk( KERN_ERR
+ "IrTTY: can't unregister line discipline (err = %d)\n",
+ ret);
+ }
+
+ /*
+ * The TTY should care of deallocating the instances by using the
+ * callback to irtty_close(), therefore we do give any deallocation
+ * function to hashbin_destroy().
+ */
+ hashbin_delete( irtty, NULL);
+ hashbin_delete( dongles, NULL);
+}
+#endif /* MODULE */
+
+/*
+ * 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 irtty_cb *self;
+ char name[16];
+
+ ASSERT( tty != NULL, return -EEXIST;);
+
+ /* First make sure we're not already connected. */
+ self = (struct irtty_cb *) tty->disc_data;
+ if ( self != NULL && self->magic == IRTTY_MAGIC)
+ return -EEXIST;
+
+ /*
+ * Allocate new instance of the driver
+ */
+ self = kmalloc( sizeof(struct irtty_cb), GFP_KERNEL);
+ if ( self == NULL) {
+ printk( KERN_ERR "IrDA: Can't allocate memory for "
+ "IrDA control block!\n");
+ return -ENOMEM;
+ }
+ memset( self, 0, sizeof(struct irtty_cb));
+
+ self->tty = tty;
+ tty->disc_data = self;
+
+ /* Give self a name */
+ sprintf( name, "%s%d", tty->driver.name,
+ MINOR(tty->device) - tty->driver.minor_start +
+ tty->driver.name_base);
+
+ /* hashbin_insert( irtty, (QUEUE*) self, 0, self->name); */
+ hashbin_insert( irtty, (QUEUE*) self, (int) self, NULL);
+
+ if (tty->driver.flush_buffer) {
+ tty->driver.flush_buffer(tty);
+ }
+
+ if (tty->ldisc.flush_buffer) {
+ tty->ldisc.flush_buffer(tty);
+ }
+
+ self->magic = IRTTY_MAGIC;
+
+ /*
+ * Initialize driver
+ */
+ /* self->idev.flags |= SIR_MODE | IO_PIO; */
+ self->idev.rx_buff.state = OUTSIDE_FRAME;
+
+ /*
+ * Initialize QoS capabilities, we fill in all the stuff that
+ * we support. Be careful not to place any restrictions on values
+ * that are not device dependent (such as link disconnect time) so
+ * this parameter can be set by IrLAP (or the user) instead. DB
+ */
+ irda_init_max_qos_capabilies( &self->idev.qos);
+
+ /* The only value we must override it the baudrate */
+ self->idev.qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+ IR_115200;
+ self->idev.qos.min_turn_time.bits = 0x03;
+ irda_qos_bits_to_value( &self->idev.qos);
+
+ /* Specify which buffer allocation policy we need */
+ self->idev.rx_buff.flags = GFP_KERNEL;
+ self->idev.tx_buff.flags = GFP_KERNEL;
+
+ /* Specify how much memory we want */
+ self->idev.rx_buff.truesize = 4000;
+ self->idev.tx_buff.truesize = 4000;
+
+ /* Initialize callbacks */
+ self->idev.change_speed = irtty_change_speed;
+ self->idev.is_receiving = irtty_is_receiving;
+ /* self->idev.is_tbusy = irtty_is_tbusy; */
+ self->idev.wait_until_sent = irtty_wait_until_sent;
+
+ /* Override the network functions we need to use */
+ self->idev.netdev.init = irtty_net_init;
+ self->idev.netdev.hard_start_xmit = irtty_hard_xmit;
+ self->idev.netdev.open = irtty_net_open;
+ self->idev.netdev.stop = irtty_net_close;
+
+ /* Open the IrDA device */
+ irda_device_open( &self->idev, name, self);
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+/*
+ * 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 irtty_cb *self = (struct irtty_cb *) tty->disc_data;
+
+ /* First make sure we're connected. */
+ ASSERT( self != NULL, return;);
+ ASSERT( self->magic == IRTTY_MAGIC, return;);
+
+ /* We are not using any dongle anymore! */
+ if ( self->dongle_q)
+ self->dongle_q->dongle->close( &self->idev);
+
+ /* Remove driver */
+ irda_device_close( &self->idev);
+
+ /* Stop tty */
+ tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ tty->disc_data = 0;
+
+ self->tty = NULL;
+ self->magic = 0;
+
+ /* hashbin_remove( irtty, 0, self->name); */
+ self = hashbin_remove( irtty, (int) self, NULL);
+
+ if ( self != NULL)
+ kfree( self);
+
+ MOD_DEC_USE_COUNT;
+
+ DEBUG( 4, "IrTTY: close() -->\n");
+}
+
+/*
+ * Function irtty_change_speed ( self, baud)
+ *
+ * Change the speed of the serial port. The driver layer must check that
+ * all transmission has finished using the irtty_wait_until_sent()
+ * function.
+ */
+static void irtty_change_speed( struct irda_device *idev, int baud)
+{
+ struct termios old_termios;
+ struct irtty_cb *self;
+ int cflag;
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ self = (struct irtty_cb *) idev->priv;
+
+ ASSERT( self != NULL, return;);
+ ASSERT( self->magic == IRTTY_MAGIC, return;);
+
+ old_termios = *(self->tty->termios);
+ cflag = self->tty->termios->c_cflag;
+
+ cflag &= ~CBAUD;
+
+ DEBUG( 4, __FUNCTION__ "(), Setting speed to %d\n", baud);
+
+ switch( baud) {
+ 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;
+ }
+
+ self->tty->termios->c_cflag = cflag;
+ self->tty->driver.set_termios( self->tty, &old_termios);
+}
+
+/*
+ * Function irtty_init_dongle (self, type)
+ *
+ * Initialize attached dongle. Warning, must be called with a process
+ * context!
+ */
+static void irtty_init_dongle( struct irtty_cb *self, int type)
+{
+ struct dongle_q *node;
+
+ ASSERT( self != NULL, return;);
+ ASSERT( self->magic == IRTTY_MAGIC, return;);
+
+#ifdef CONFIG_KMOD
+ /* Try to load the module needed */
+ switch( type) {
+ case ESI_DONGLE:
+ DEBUG( 0, __FUNCTION__ "(), ESI dongle!\n");
+ request_module( "esi");
+ break;
+ case TEKRAM_DONGLE:
+ DEBUG( 0, __FUNCTION__ "(), Tekram dongle!\n");
+ request_module( "tekram");
+ break;
+ case ACTISYS_DONGLE:
+ DEBUG( 0, __FUNCTION__ "(), ACTiSYS dongle!\n");
+ request_module( "actisys");
+ break;
+ default:
+ DEBUG( 0, __FUNCTION__ "(), Unknown dongle type!\n");
+ return;
+ break;
+ }
+#endif /* CONFIG_KMOD */
+
+ node = hashbin_find( dongles, type, NULL);
+ if ( !node) {
+ DEBUG( 0, __FUNCTION__
+ "(), Unable to find requested dongle\n");
+ return;
+ }
+ self->dongle_q = node;
+
+ /* Use this change speed function instead of the default */
+ self->idev.change_speed = node->dongle->change_speed;
+
+ /*
+ * Now initialize the dongle!
+ */
+ node->dongle->open( &self->idev, type);
+ node->dongle->qos_init( &self->idev, &self->idev.qos);
+
+ /* Reset dongle */
+ node->dongle->reset( &self->idev, 0);
+
+ /* Set to default baudrate */
+ node->dongle->change_speed( &self->idev, 9600);
+}
+
+/*
+ * Function irtty_ioctl (tty, file, cmd, arg)
+ *
+ * The Swiss army knife of system calls :-)
+ *
+ */
+static int irtty_ioctl( struct tty_struct *tty, void *file, int cmd,
+ void *arg)
+{
+ struct irtty_cb *self;
+ int err = 0;
+ int size = _IOC_SIZE(cmd);
+
+ self = (struct irtty_cb *) tty->disc_data;
+
+ ASSERT( self != NULL, return -ENODEV;);
+ ASSERT( self->magic == IRTTY_MAGIC, return -EBADR;);
+
+ if ( _IOC_DIR(cmd) & _IOC_READ)
+ err = verify_area( VERIFY_WRITE, (void *) arg, size);
+ else if ( _IOC_DIR(cmd) & _IOC_WRITE)
+ err = verify_area( VERIFY_READ, (void *) arg, size);
+ if ( err)
+ return err;
+
+ switch(cmd) {
+ case TCGETS:
+ case TCGETA:
+ return n_tty_ioctl( tty, (struct file *) file, cmd,
+ (unsigned long) arg);
+ break;
+ case IRTTY_IOCTDONGLE:
+ /* Initialize dongle */
+ irtty_init_dongle( self, (int) arg);
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+/*
+ * 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
+ */
+static void irtty_receive_buf( struct tty_struct *tty, const unsigned
+ char *cp, char *fp, int count)
+{
+ struct irtty_cb *self = (struct irtty_cb *) tty->disc_data;
+
+ ASSERT( self != NULL, return;);
+ ASSERT( self->magic == IRTTY_MAGIC, return;);
+
+ /* Read the characters out of the buffer */
+ while (count--) {
+ /*
+ * Characters received with a parity error, etc?
+ */
+ if (fp && *fp++) {
+ DEBUG( 0, "Framing or parity error!\n");
+ irda_device_set_media_busy( &self->idev, TRUE);
+ /* sl->rx_errors++; */
+ cp++;
+ continue;
+ }
+ /*
+ * Unwrap and destuff one byte
+ */
+ async_unwrap_char( &self->idev, *cp++);
+ /* self->rx_over_errors++; */
+ }
+}
+
+/*
+ * Function irtty_hard_xmit (skb, dev)
+ *
+ * Transmit skb
+ *
+ */
+static int irtty_hard_xmit( struct sk_buff *skb, struct device *dev)
+{
+ struct irtty_cb *self;
+ struct irda_device *idev;
+ int actual = 0;
+
+ ASSERT( dev != NULL, return 0;);
+ ASSERT( skb != NULL, return 0;);
+
+ if ( dev->tbusy) {
+ DEBUG( 4, __FUNCTION__ "(), tbusy==TRUE\n");
+
+ return -EBUSY;
+ }
+
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT( idev != NULL, return 0;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+ self = (struct irtty_cb *) idev->priv;
+
+ ASSERT( self != NULL, return 0;);
+ ASSERT( self->magic == IRTTY_MAGIC, return 0;);
+
+ /* Lock transmit buffer */
+ if ( irda_lock( (void *) &dev->tbusy) == FALSE)
+ return 0;
+
+ /*
+ * Transfer skb to tx_buff while wrapping, stuffing and making CRC
+ */
+ idev->tx_buff.len = async_wrap_skb( skb, idev->tx_buff.data,
+ idev->tx_buff.truesize);
+
+ self->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+
+ dev->trans_start = jiffies;
+
+ if ( self->tty->driver.write)
+ actual = self->tty->driver.write( self->tty, 0,
+ idev->tx_buff.data,
+ idev->tx_buff.len);
+
+ idev->tx_buff.offset = actual;
+ idev->tx_buff.head = idev->tx_buff.data + actual;
+#if 0
+ /*
+ * Did we transmit the whole frame? Commented out for now since
+ * I must check if this optimalization really works. DB.
+ */
+ if (( idev->tx.count - idev->tx.ptr) <= 0) {
+ DEBUG( 4, "irtty_xmit_buf: finished with frame!\n");
+ self->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ irda_unlock( &self->tbusy);
+ }
+#endif
+
+ dev_kfree_skb( skb);
+
+ return 0;
+}
+
+/*
+ * 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)
+{
+ 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)
+{
+ int actual = 0, count;
+ struct irtty_cb *self = (struct irtty_cb *) tty->disc_data;
+ struct irda_device *idev;
+
+ /*
+ * First make sure we're connected.
+ */
+ ASSERT( self != NULL, return;);
+ ASSERT( self->magic == IRTTY_MAGIC, return;);
+
+ idev = &self->idev;
+
+ /*
+ * Finished with frame?
+ */
+ if ( idev->tx_buff.offset == idev->tx_buff.len) {
+
+ /*
+ * Now serial buffer is almost free & we can start
+ * transmission of another packet
+ */
+ DEBUG( 4, __FUNCTION__ "(), finished with frame!\n");
+
+ tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+
+ idev->netdev.tbusy = 0; /* Unlock */
+ idev->stats.tx_packets++;
+ idev->stats.tx_bytes += idev->tx_buff.len;
+
+ /* Tell network layer that we want more frames */
+ mark_bh( NET_BH);
+
+ return;
+ }
+ /*
+ * Write data left in transmit buffer
+ */
+ count = idev->tx_buff.len - idev->tx_buff.offset;
+ actual = tty->driver.write( tty, 0, idev->tx_buff.head, count);
+ idev->tx_buff.offset += actual;
+ idev->tx_buff.head += actual;
+
+ DEBUG( 4, "actual=%d, sent %d\n", actual, count);
+}
+
+/*
+ * Function irtty_is_receiving (idev)
+ *
+ * Return TRUE is we are currently receiving a frame
+ *
+ */
+static int irtty_is_receiving( struct irda_device *idev)
+{
+ return ( idev->rx_buff.state != OUTSIDE_FRAME);
+}
+
+/*
+ * Function irtty_change_speed_ready (idev)
+ *
+ * Are we completely finished with transmitting frames so its possible
+ * to change the speed of the serial port. Warning this function must
+ * be called with a process context!
+ */
+static void irtty_wait_until_sent( struct irda_device *idev)
+{
+ struct irtty_cb *self = (struct irtty_cb *) idev->priv;
+
+ ASSERT( self != NULL, return;);
+ ASSERT( self->magic == IRTTY_MAGIC, return;);
+
+ DEBUG( 4, "Chars in buffer %d\n",
+ self->tty->driver.chars_in_buffer( self->tty));
+
+ tty_wait_until_sent( self->tty, 0);
+}
+
+int irtty_register_dongle( struct dongle *dongle)
+{
+ struct dongle_q *new;
+
+ /* Check if this compressor has been registred before */
+ if ( hashbin_find ( dongles, dongle->type, NULL)) {
+ DEBUG( 0, __FUNCTION__ "(), Dongle already registered\n");
+ return 0;
+ }
+
+ /* Make new IrDA dongle */
+ new = (struct dongle_q *) kmalloc (sizeof (struct dongle_q),
+ GFP_KERNEL);
+ if (new == NULL) {
+ return 1;
+
+ }
+ memset( new, 0, sizeof( struct dongle_q));
+ new->dongle = dongle;
+
+ /* Insert IrDA compressor into hashbin */
+ hashbin_insert( dongles, (QUEUE *) new, dongle->type, NULL);
+
+ return 0;
+}
+
+void irtty_unregister_dongle( struct dongle *dongle)
+{
+ struct dongle_q *node;
+
+ node = hashbin_remove( dongles, dongle->type, NULL);
+ if ( !node) {
+ DEBUG( 0, __FUNCTION__ "(), dongle not found!\n");
+ return;
+ }
+ kfree( node);
+}
+
+static int irtty_net_init( struct device *dev)
+{
+ /* Set up to be a normal IrDA network device driver */
+ irda_device_setup( dev);
+
+ /* Insert overrides below this line! */
+
+ return 0;
+}
+
+
+static int irtty_net_open( struct device *dev)
+{
+ ASSERT( dev != NULL, return -1;);
+
+ /* Ready to play! */
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+static int irtty_net_close(struct device *dev)
+{
+ ASSERT( dev != NULL, return -1;);
+
+ /* Stop device */
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+#ifdef MODULE
+
+/*
+ * Function init_module (void)
+ *
+ * Initialize IrTTY module
+ *
+ */
+int init_module(void)
+{
+ irtty_init();
+ return(0);
+}
+
+/*
+ * Function cleanup_module (void)
+ *
+ * Cleanup IrTTY module
+ *
+ */
+void cleanup_module(void)
+{
+ irtty_cleanup();
+}
+
+#endif /* MODULE */
+
+
+
+
+
+
+
diff --git a/drivers/net/irda/pc87108.c b/drivers/net/irda/pc87108.c
new file mode 100644
index 000000000..faf9eea33
--- /dev/null
+++ b/drivers/net/irda/pc87108.c
@@ -0,0 +1,1502 @@
+/*********************************************************************
+ *
+ * Filename: pc87108.c
+ * Version: 0.8
+ * Description: FIR/MIR driver for the NS PC87108 chip
+ * Status: Experimental.
+ * Author: Dag Brattli <dagb@cs.uit.no>
+ * Created at: Sat Nov 7 21:43:15 1998
+ * Modified at: Mon Dec 28 08:46:16 1998
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ *
+ * Copyright (c) 1998 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 is 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/malloc.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/byteorder.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/pc87108.h>
+
+#define BROKEN_DONGLE_ID
+
+static char *driver_name = "pc87108";
+
+#define CHIP_IO_EXTENT 8
+
+static unsigned int io[] = { 0x2f8, ~0, ~0, ~0 };
+static unsigned int io2[] = { 0x150, 0, 0, 0};
+static unsigned int irq[] = { 3, 0, 0, 0 };
+static unsigned int dma[] = { 0, 0, 0, 0 };
+
+static struct pc87108 *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 pc87108_open( int i, unsigned int iobase, unsigned int board_addr,
+ unsigned int irq, unsigned int dma);
+static int pc87108_close( struct irda_device *idev);
+static int pc87108_probe( int iobase, int board_addr, int irq, int dma);
+static void pc87108_pio_receive( struct irda_device *idev);
+static int pc87108_dma_receive( struct irda_device *idev);
+static int pc87108_dma_receive_complete(struct irda_device *idev, int iobase);
+static int pc87108_hard_xmit( struct sk_buff *skb, struct device *dev);
+static int pc87108_pio_write( int iobase, __u8 *buf, int len, int fifo_size);
+static void pc87108_dma_write( struct irda_device *idev, int iobase);
+static void pc87108_change_speed( struct irda_device *idev, int baud);
+static void pc87108_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void pc87108_wait_until_sent( struct irda_device *idev);
+static int pc87108_is_receiving( struct irda_device *idev);
+static int pc87108_read_dongle_id ( int iobase);
+static void pc87108_init_dongle_interface ( int iobase, int dongle_id);
+
+static int pc87108_net_init( struct device *dev);
+static int pc87108_net_open( struct device *dev);
+static int pc87108_net_close( struct device *dev);
+
+/*
+ * Function pc87108_init ()
+ *
+ * Initialize chip. Just try to find out how many chips we are dealing with
+ * and where they are
+ */
+__initfunc(int pc87108_init(void))
+{
+ int i;
+
+ for ( i=0; (io[i] < 2000) && (i < 4); i++) {
+ int ioaddr = io[i];
+ if (check_region(ioaddr, CHIP_IO_EXTENT))
+ continue;
+ if (pc87108_open( i, io[i], io2[i], irq[i], dma[i]) == 0)
+ return 0;
+ }
+ return -ENODEV;
+}
+
+/*
+ * Function pc87108_cleanup ()
+ *
+ * Close all configured chips
+ *
+ */
+#ifdef MODULE
+static void pc87108_cleanup(void)
+{
+ int i;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ for ( i=0; i < 4; i++) {
+ if ( dev_self[i])
+ pc87108_close( &(dev_self[i]->idev));
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Function pc87108_open (iobase, irq)
+ *
+ * Open driver instance
+ *
+ */
+static int pc87108_open( int i, unsigned int iobase, unsigned int board_addr,
+ unsigned int irq, unsigned int dma)
+{
+ struct pc87108 *self;
+ struct irda_device *idev;
+ int ret;
+ int dongle_id;
+
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ if (( dongle_id = pc87108_probe( iobase, board_addr, irq, dma)) == -1)
+ return -1;
+
+ /*
+ * Allocate new instance of the driver
+ */
+ self = kmalloc( sizeof(struct pc87108), GFP_KERNEL);
+ if ( self == NULL) {
+ printk( KERN_ERR "IrDA: Can't allocate memory for "
+ "IrDA control block!\n");
+ return -ENOMEM;
+ }
+ memset( self, 0, sizeof(struct pc87108));
+
+ /* Need to store self somewhere */
+ dev_self[i] = self;
+
+ idev = &self->idev;
+
+ /* Initialize IO */
+ idev->io.iobase = iobase;
+ idev->io.irq = irq;
+ idev->io.io_ext = CHIP_IO_EXTENT;
+ idev->io.dma = dma;
+ idev->io.fifo_size = 32;
+
+ /* Lock the port that we need */
+ ret = check_region( idev->io.iobase, idev->io.io_ext);
+ if ( ret < 0) {
+ DEBUG( 0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
+ idev->io.iobase);
+ /* pc87108_cleanup( self->idev); */
+ return -ENODEV;
+ }
+ request_region( idev->io.iobase, idev->io.io_ext, idev->name);
+
+ /* Initialize QoS for this device */
+ irda_init_max_qos_capabilies( &idev->qos);
+
+ /* The only value we must override it the baudrate */
+ idev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+ IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8);
+
+ idev->qos.min_turn_time.bits = 0x07;
+ irda_qos_bits_to_value( &idev->qos);
+
+ /* Specify which buffer allocation policy we need */
+ idev->rx_buff.flags = GFP_KERNEL | GFP_DMA;
+ idev->tx_buff.flags = GFP_KERNEL | GFP_DMA;
+
+ /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
+ idev->rx_buff.truesize = 14384;
+ idev->tx_buff.truesize = 4000;
+
+ /* Initialize callbacks */
+ idev->hard_xmit = pc87108_hard_xmit;
+ idev->change_speed = pc87108_change_speed;
+ idev->wait_until_sent = pc87108_wait_until_sent;
+ idev->is_receiving = pc87108_is_receiving;
+
+ /* Override the network functions we need to use */
+ idev->netdev.init = pc87108_net_init;
+ idev->netdev.hard_start_xmit = pc87108_hard_xmit;
+ idev->netdev.open = pc87108_net_open;
+ idev->netdev.stop = pc87108_net_close;
+
+ idev->io.dongle_id = dongle_id;
+ pc87108_init_dongle_interface( iobase, dongle_id);
+
+ /* Open the IrDA device */
+ irda_device_open( idev, driver_name, self);
+
+ return 0;
+}
+
+/*
+ * Function pc87108_close (idev)
+ *
+ * Close driver instance
+ *
+ */
+static int pc87108_close( struct irda_device *idev)
+{
+ int iobase;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ ASSERT( idev != NULL, return -1;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+ iobase = idev->io.iobase;
+
+ /* Release the PORT that this driver is using */
+ DEBUG( 4, __FUNCTION__ "(), Releasing Region %03x\n",
+ idev->io.iobase);
+ release_region( idev->io.iobase, idev->io.io_ext);
+
+ irda_device_close( idev);
+
+ return 0;
+}
+
+/*
+ * Function pc87108_probe (iobase, board_addr, irq, dma)
+ *
+ * Returns non-negative on success.
+ *
+ */
+static int pc87108_probe( int iobase, int board_addr, int irq, int dma)
+{
+ int version;
+ __u8 temp=0;
+ int dongle_id;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ /* Base Address and Interrupt Control Register BAIC */
+ outb(0, board_addr);
+ switch ( iobase) {
+ case 0x3E8: outb( 0x14, board_addr+1); break;
+ case 0x2E8: outb( 0x15, board_addr+1); break;
+ case 0x3F8: outb( 0x16, board_addr+1); break;
+ case 0x2F8: outb( 0x17, board_addr+1); break;
+ default: DEBUG(0, __FUNCTION__ "(), invalid base_address");
+ }
+
+ /* Control Signal Routing Register CSRT */
+ switch (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: DEBUG( 0, __FUNCTION__ "(), invalid irq");
+ }
+ outb( 1, board_addr);
+
+ switch (dma) {
+ case 0: outb( 0x08+temp, board_addr+1); break;
+ case 1: outb( 0x10+temp, board_addr+1); break;
+ case 3: outb( 0x18+temp, board_addr+1); break;
+ default: DEBUG( 0, __FUNCTION__ "(), invalid dma");
+ }
+
+ /* Mode Control Register MCTL */
+ outb( 2, board_addr);
+ outb( 0x03, board_addr+1);
+
+ /* read the Module ID */
+ switch_bank( iobase, BANK3);
+ version = inb( iobase+MID);
+
+ /* should be 0x2? */
+ if (0x20 != (version & 0xf0))
+ {
+ DEBUG( 0, __FUNCTION__ "(), Wrong chip version");
+ return -1;
+ }
+
+ /* Switch to advanced mode */
+ switch_bank( iobase, BANK2);
+ outb( ECR1_EXT_SL, iobase+ECR1);
+ switch_bank( iobase, BANK0);
+
+ dongle_id = pc87108_read_dongle_id( iobase);
+ DEBUG( 0, __FUNCTION__ "(), Found dongle: %s\n",
+ dongle_types[ dongle_id]);
+
+ /* 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);
+
+ /* Set FIFO size to 32 */
+ switch_bank( iobase, BANK2);
+ outb( EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2);
+
+ /* IRCR2: FEND_MD is set */
+ switch_bank( iobase, BANK5);
+ outb( 0x2a, 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 */
+ outb( 0x2a, iobase+4); /* Set beginning frag, and preamble length */
+
+ /* Receiver frame length */
+ switch_bank( iobase, BANK4);
+ outb( 2048 & 0xff, iobase+6);
+ outb(( 2048 >> 8) & 0x1f, iobase+7);
+
+ /* Transmitter frame length */
+ outb( 2048 & 0xff, iobase+4);
+ outb(( 2048 >> 8) & 0x1f, iobase+5);
+
+ DEBUG( 0, "PC87108 driver loaded. Version: 0x%02x\n", version);
+
+ /* Enable receive interrupts */
+ switch_bank( iobase, BANK0);
+ outb( IER_RXHDL_IE, iobase+IER);
+
+ return dongle_id;
+}
+
+/*
+ * Function pc87108_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 pc87108_read_dongle_id ( int iobase)
+{
+ int dongle_id;
+ __u8 bank;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ 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);
+
+ DEBUG( 0, __FUNCTION__ "(), Dongle = %#x\n", dongle_id);
+
+ outb( bank, iobase+BSR);
+
+ return dongle_id;
+}
+
+/*
+ * Function pc87108_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 pc87108_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 */
+ DEBUG( 0, __FUNCTION__ "(), %s not defined by irda yet\n",
+ dongle_types[dongle_id]);
+ break;
+ case 0x02: /* same as */
+ case 0x03: /* Reserved */
+ DEBUG( 0, __FUNCTION__ "(), %s not defined by irda yet\n",
+ dongle_types[dongle_id]);
+ break;
+ case 0x04: /* Sharp RY5HD01 */
+ DEBUG( 0, __FUNCTION__ "(), %s not supported yet\n",
+ dongle_types[dongle_id]);
+ break;
+ case 0x05: /* Reserved */
+ DEBUG( 0, __FUNCTION__ "(), %s not defined by irda yet",
+ dongle_types[dongle_id]);
+ break;
+ case 0x06: /* Single-ended serial interface */
+ DEBUG( 0, __FUNCTION__ "(), %s not defined by irda yet\n",
+ dongle_types[dongle_id]);
+ break;
+ case 0x07: /* Consumer-IR only */
+ DEBUG( 0, __FUNCTION__ "(), %s is not for IrDA mode\n",
+ dongle_types[dongle_id]);
+ break;
+ case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */
+ DEBUG( 0, __FUNCTION__ "(), %s not supported yet\n",
+ dongle_types[dongle_id]);
+ break;
+ case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */
+ outb_p( 0x28, iobase+7); /* Set irsl[0-2] as output */
+ break;
+ case 0x0A: /* same as */
+ case 0x0B: /* Reserved */
+ DEBUG( 0, __FUNCTION__ "(), %s not defined by irda yet\n",
+ 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 */
+ DEBUG( 0, __FUNCTION__ "(), %s\n",
+ dongle_types[dongle_id]);
+ DEBUG( 0, "***\n");
+
+ switch_bank( iobase, BANK0);
+ outb( 0x62, iobase+MCR);
+ break;
+ default:
+ DEBUG( 0, __FUNCTION__ "(), invalid dongle_id %#x", 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 pc87108_change_dongle_speed (iobase, speed, dongle_id)
+ *
+ * Change speed of the attach dongle
+ *
+ */
+static void pc87108_change_dongle_speed( int iobase, int speed, int dongle_id)
+{
+ unsigned long flags;
+ __u8 bank;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ /* 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 */
+ DEBUG( 0, __FUNCTION__ "(), %s not defined by irda yet\n",
+ dongle_types[dongle_id]);
+ break;
+ case 0x02: /* same as */
+ case 0x03: /* Reserved */
+ DEBUG( 0, __FUNCTION__ "(), %s not defined by irda yet\n",
+ dongle_types[dongle_id]);
+ break;
+ case 0x04: /* Sharp RY5HD01 */
+ DEBUG( 0, __FUNCTION__ "(), %s not supported yet\n",
+ dongle_types[dongle_id]);
+ case 0x05: /* Reserved */
+ DEBUG( 0, __FUNCTION__ "(), %s not defined by irda yet\n",
+ dongle_types[dongle_id]);
+ break;
+ case 0x06: /* Single-ended serial interface */
+ DEBUG( 0, __FUNCTION__ "(), %s not defined by irda yet\n",
+ dongle_types[dongle_id]);
+ break;
+ case 0x07: /* Consumer-IR only */
+ DEBUG( 0, __FUNCTION__ "(), %s is not for IrDA mode\n",
+ dongle_types[dongle_id]);
+ break;
+ case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */
+ DEBUG( 0, __FUNCTION__ "(), %s not supported yet\n",
+ dongle_types[dongle_id]);
+ case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */
+ switch_bank( iobase, BANK7);
+ outb_p( 0x01, iobase+4);
+
+ if ( speed == 4000000) {
+ save_flags(flags);
+ cli();
+ outb( 0x81, iobase+4);
+ outb( 0x80, iobase+4);
+ restore_flags(flags);
+ }
+ else
+ outb_p( 0x00, iobase+4);
+ break;
+ case 0x0A: /* same as */
+ case 0x0B: /* Reserved */
+ DEBUG( 0, __FUNCTION__ "(), %s not defined by irda yet\n",
+ 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 */
+ DEBUG( 0, __FUNCTION__ "(), %s is not for IrDA mode\n",
+ dongle_types[dongle_id]);
+
+ switch_bank( iobase, BANK0);
+ outb( 0x62, iobase+MCR);
+ break;
+ default:
+ DEBUG( 0, __FUNCTION__ "(), invalid data_rate\n");
+ }
+ /* Restore bank register */
+ outb( bank, iobase+BSR);
+}
+
+/*
+ * Function pc87108_change_speed (idev, baud)
+ *
+ * Change the speed of the device
+ *
+ */
+static void pc87108_change_speed( struct irda_device *idev, int speed)
+{
+ __u8 mcr = MCR_SIR;
+ __u8 bank;
+ int iobase;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ iobase = idev->io.iobase;
+
+ /* Update accounting for new speed */
+ idev->io.baudrate = 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 37600: 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;
+ DEBUG(0, __FUNCTION__ "(), handling baud of 576000\n");
+ break;
+ case 1152000:
+ mcr = MCR_MIR;
+ DEBUG(0, __FUNCTION__ "(), handling baud of 1152000\n");
+ break;
+ case 4000000:
+ mcr = MCR_FIR;
+ DEBUG(0, __FUNCTION__ "(), handling baud of 4000000\n");
+ break;
+ default:
+ mcr = MCR_FIR;
+ DEBUG( 0, __FUNCTION__ "(), unknown baud rate of %d\n", speed);
+ break;
+ }
+
+ /* Set appropriate speed mode */
+ switch_bank(iobase, BANK0);
+ outb( mcr|MCR_TX_DFR, iobase+MCR);
+
+ /* Give some hits to the transceiver */
+ pc87108_change_dongle_speed( iobase, speed, idev->io.dongle_id);
+
+ /* Set FIFO threshold to TX17, RX16 */
+ switch_bank( iobase, BANK0);
+ 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);
+ /* outb( 0xa7, iobase+FCR); */
+
+ /* Set FIFO size to 32 */
+ switch_bank( iobase, BANK2);
+ outb( EXCR2_RFSIZ|EXCR2_TFSIZ, iobase+EXCR2);
+
+ idev->netdev.tbusy = 0;
+
+ /* Enable some interrupts so we can receive frames */
+ switch_bank( iobase, BANK0);
+ if ( speed > 115200) {
+ outb( IER_SFIF_IE, iobase+IER);
+ pc87108_dma_receive( idev);
+ } else
+ outb( IER_RXHDL_IE, iobase+IER);
+
+ /* Restore BSR */
+ outb( bank, iobase+BSR);
+}
+
+/*
+ * Function pc87108_hard_xmit (skb, dev)
+ *
+ * Transmit the frame!
+ *
+ */
+static int pc87108_hard_xmit( struct sk_buff *skb, struct device *dev)
+{
+ struct irda_device *idev;
+ int iobase;
+ __u8 bank;
+ int mtt;
+
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT( idev != NULL, return 0;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+ iobase = idev->io.iobase;
+
+ DEBUG(4, __FUNCTION__ "(%ld), skb->len=%d\n", jiffies, (int) skb->len);
+
+ if ( dev->tbusy) {
+ DEBUG( 4, __FUNCTION__ "(), tbusy==TRUE\n");
+
+ return -EBUSY;
+ }
+
+ /* Lock transmit buffer */
+ if ( irda_lock( (void *) &dev->tbusy) == FALSE)
+ return -EBUSY;
+
+ /* Save current bank */
+ bank = inb( iobase+BSR);
+
+ /* Decide if we should use PIO or DMA transfer */
+ if ( idev->io.baudrate > 115200) {
+ memcpy( idev->tx_buff.data, skb->data, skb->len);
+ idev->tx_buff.len = skb->len;
+ idev->tx_buff.head = idev->tx_buff.data;
+ idev->tx_buff.offset = 0;
+
+ mtt = irda_get_mtt( skb);
+ if ( mtt > 50) {
+ /* Adjust for timer resolution */
+ mtt = mtt / 125 + 1;
+
+ /* 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);
+ idev->io.direction = IO_XMIT;
+
+ /* Enable timer interrupt */
+ switch_bank( iobase, BANK0);
+ outb( IER_TMR_IE, iobase+IER);
+ } else {
+ /* Use udelay for delays less than 50 us. */
+ if (mtt)
+ udelay( mtt);
+
+ /* Enable DMA interrupt */
+ switch_bank( iobase, BANK0);
+ outb( IER_DMA_IE, iobase+IER);
+ pc87108_dma_write( idev, iobase);
+ }
+ } else {
+ idev->tx_buff.len = async_wrap_skb( skb, idev->tx_buff.data,
+ idev->tx_buff.truesize);
+
+ idev->tx_buff.offset = 0;
+ idev->tx_buff.head = idev->tx_buff.data;
+
+ /* Add interrupt on tx low level (will fire immediately) */
+ switch_bank( iobase, BANK0);
+ outb( IER_TXLDL_IE, iobase+IER);
+ }
+ dev_kfree_skb( skb);
+
+ /* Restore bank register */
+ outb( bank, iobase+BSR);
+
+ return 0;
+}
+
+/*
+ * Function pc87108_dma_xmit (idev, iobase)
+ *
+ * Transmit data using DMA
+ *
+ */
+static void pc87108_dma_write( struct irda_device *idev, int iobase)
+{
+ int bsr;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ /* Save current bank */
+ bsr = inb( iobase+BSR);
+
+ /* Disable DMA */
+ switch_bank(iobase, BANK0);
+ outb( inb( iobase+MCR) & ~MCR_DMA_EN, iobase+MCR);
+
+ setup_dma( idev->io.dma, idev->tx_buff.data, idev->tx_buff.len,
+ DMA_MODE_WRITE);
+
+ /* idev->media_busy = TRUE; */
+ idev->io.direction = IO_XMIT;
+
+ /* Choose transmit DMA channel */
+ switch_bank(iobase, BANK2);
+ outb( inb( iobase+ECR1) | ECR1_DMASWP|ECR1_DMANF|ECR1_EXT_SL,
+ iobase+ECR1);
+
+ /* Enable DMA */
+ switch_bank( iobase, BANK0);
+ outb( inb( iobase+MCR)|MCR_DMA_EN, iobase+MCR);
+
+ /* Restore bank register */
+ outb( bsr, iobase+BSR);
+}
+
+/*
+ * Function pc87108_pio_xmit (idev, iobase)
+ *
+ * Transmit data using PIO. Returns the number of bytes that actually
+ * got transfered
+ *
+ */
+static int pc87108_pio_write( int iobase, __u8 *buf, int len, int fifo_size)
+{
+ int actual = 0;
+ __u8 bank;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ /* Save current bank */
+ bank = inb( iobase+BSR);
+
+ switch_bank( iobase, BANK0);
+ if (!(inb_p( iobase+LSR) & LSR_TXEMP)) {
+ DEBUG( 4, __FUNCTION__ "(), warning, FIFO not empty yet!\n");
+
+ fifo_size -= 17;
+ DEBUG( 4, __FUNCTION__ "%d bytes left in tx fifo\n", fifo_size);
+ }
+
+ /* Fill FIFO with current frame */
+ while (( fifo_size-- > 0) && (actual < len)) {
+ /* Transmit next byte */
+ outb( buf[actual++], iobase+TXD);
+ }
+
+ DEBUG( 4, __FUNCTION__ "(), fifo_size %d ; %d sent of %d\n",
+ fifo_size, actual, len);
+
+ /* Restore bank */
+ outb( bank, iobase+BSR);
+
+ return actual;
+}
+
+/*
+ * Function pc87108_dma_xmit_complete (idev)
+ *
+ * The transfer of a frame in finished. This function will only be called
+ * by the interrupt handler
+ *
+ */
+static void pc87108_dma_xmit_complete( struct irda_device *idev)
+{
+ int iobase;
+ __u8 bank;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ iobase = idev->io.iobase;
+
+ /* 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) {
+ idev->stats.tx_errors++;
+ idev->stats.tx_fifo_errors++;
+
+ /* Clear bit, by writing 1 into it */
+ outb( ASCR_TXUR, iobase+ASCR);
+ } else {
+ idev->stats.tx_packets++;
+ idev->stats.tx_bytes += idev->tx_buff.len;
+ }
+
+ /* Unlock tx_buff and request another frame */
+ idev->netdev.tbusy = 0; /* Unlock */
+ idev->media_busy = FALSE;
+
+ /* Tell the network layer, that we can accept more frames */
+ mark_bh( NET_BH);
+
+ /* Restore bank */
+ outb( bank, iobase+BSR);
+}
+
+/*
+ * Function pc87108_dma_receive (idev)
+ *
+ * Get ready for receiving a frame. The device will initiate a DMA
+ * if it starts to receive a frame.
+ *
+ */
+static int pc87108_dma_receive( struct irda_device *idev)
+{
+ struct pc87108 *self;
+ int iobase;
+ __u8 bsr;
+
+ ASSERT( idev != NULL, return -1;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+ DEBUG( 4, __FUNCTION__ "\n");
+
+ self = idev->priv;
+ iobase= idev->io.iobase;
+
+ /* Save current bank */
+ bsr = inb( iobase+BSR);
+
+ /* Disable DMA */
+ switch_bank( iobase, BANK0);
+ outb( inb(iobase+MCR) & ~MCR_DMA_EN, iobase+MCR);
+
+ setup_dma( idev->io.dma, idev->rx_buff.data, idev->rx_buff.truesize,
+ DMA_MODE_READ);
+
+ /* driver->media_busy = FALSE; */
+ idev->io.direction = IO_RECV;
+ idev->rx_buff.head = idev->rx_buff.data;
+ idev->rx_buff.offset = 0;
+
+ /* Reset Rx FIFO. This will also flush the ST_FIFO */
+ outb( FCR_RXTH|FCR_TXTH|FCR_RXSR|FCR_FIFO_EN, iobase+FCR);
+ self->st_fifo.len = self->st_fifo.tail = self->st_fifo.head = 0;
+
+ /* Choose DMA Rx, DMA Fairness, and Advanced mode */
+ switch_bank(iobase, BANK2);
+ outb(( inb( iobase+ECR1) & ~ECR1_DMASWP)|ECR1_DMANF|ECR1_EXT_SL,
+ iobase+ECR1);
+
+ /* enable DMA */
+ switch_bank(iobase, BANK0);
+ outb( inb( iobase+MCR)|MCR_DMA_EN, iobase+MCR);
+
+ /* Restore bank register */
+ outb( bsr, iobase+BSR);
+
+ DEBUG( 4, __FUNCTION__ "(), done!\n");
+
+ return 0;
+}
+
+/*
+ * Function pc87108_dma_receive_complete (idev)
+ *
+ * Finished with receiving frames
+ *
+ *
+ */
+static int pc87108_dma_receive_complete( struct irda_device *idev, int iobase)
+{
+ struct sk_buff *skb;
+ struct pc87108 *self;
+ struct st_fifo *st_fifo;
+ int len;
+ __u8 bank;
+ __u8 status;
+
+ self = idev->priv;
+ st_fifo = &self->st_fifo;
+
+ /* Save current bank */
+ bank = inb( iobase+BSR);
+
+ iobase = idev->io.iobase;
+
+ /* Read status FIFO */
+ switch_bank(iobase, BANK5);
+ while (( status = inb( iobase+FRM_ST)) & FRM_ST_VLD) {
+ 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++;
+ }
+
+ /* Try to process all entries in status FIFO */
+ switch_bank( iobase, BANK0);
+ 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 & FRM_ST_ERR_MSK) {
+ if ( status & FRM_ST_LOST_FR) {
+ /* Add number of lost frames to stats */
+ idev->stats.rx_errors += len;
+ } else {
+ /* Skip frame */
+ idev->stats.rx_errors++;
+
+ idev->rx_buff.offset += len;
+ idev->rx_buff.head += len;
+
+ if ( status & FRM_ST_MAX_LEN)
+ idev->stats.rx_length_errors++;
+
+ if ( status & FRM_ST_PHY_ERR)
+ idev->stats.rx_frame_errors++;
+
+ if ( status & FRM_ST_BAD_CRC)
+ idev->stats.rx_crc_errors++;
+ }
+ /* The errors below can be reported in both cases */
+ if ( status & FRM_ST_OVR1)
+ idev->stats.rx_fifo_errors++;
+
+ if ( status & FRM_ST_OVR2)
+ idev->stats.rx_fifo_errors++;
+
+ } else {
+ /* Check if we have transfered all data to memory */
+ if ( inb( iobase+LSR) & LSR_RXDA) {
+ /* 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 bank register */
+ outb( bank, iobase+BSR);
+
+ return FALSE; /* I'll be back! */
+ }
+
+ /* Should be OK then */
+ skb = dev_alloc_skb( len+1);
+ if (skb == NULL) {
+ printk( KERN_INFO __FUNCTION__
+ "(), memory squeeze, dropping frame.\n");
+ /* Restore bank register */
+ outb( bank, iobase+BSR);
+
+ return FALSE;
+ }
+
+ /* Make sure IP header gets aligned */
+ skb_reserve( skb, 1);
+
+ /* Copy frame without CRC */
+ if ( idev->io.baudrate < 4000000) {
+ skb_put( skb, len-2);
+ memcpy( skb->data, idev->rx_buff.head, len-2);
+ } else {
+ skb_put( skb, len-4);
+ memcpy( skb->data, idev->rx_buff.head, len-4);
+ }
+
+ /* Move to next frame */
+ idev->rx_buff.offset += len;
+ idev->rx_buff.head += len;
+ idev->stats.rx_packets++;
+
+ skb->dev = &idev->netdev;
+ skb->mac.raw = skb->data;
+ skb->protocol = htons(ETH_P_IRDA);
+ netif_rx( skb);
+ }
+ }
+ /* Restore bank register */
+ outb( bank, iobase+BSR);
+
+ return TRUE;
+}
+
+/*
+ * Function pc87108_pio_receive (idev)
+ *
+ * Receive all data in receiver FIFO
+ *
+ */
+static void pc87108_pio_receive( struct irda_device *idev)
+{
+ __u8 byte = 0x00;
+ int iobase;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ iobase = idev->io.iobase;
+
+ if ( idev->rx_buff.len == 0) {
+ idev->rx_buff.head = idev->rx_buff.data;
+ }
+
+ /* Receive all characters in Rx FIFO */
+ do {
+ byte = inb( iobase+RXD);
+ async_unwrap_char( idev, byte);
+
+ } while ( inb( iobase+LSR) & LSR_RXDA); /* Data available */
+}
+
+/*
+ * Function pc87108_sir_interrupt (idev, eir)
+ *
+ * Handle SIR interrupt
+ *
+ */
+static __u8 pc87108_sir_interrupt( struct irda_device *idev, int eir)
+{
+ int len;
+ int actual;
+ __u8 new_ier = 0;
+
+ /* Transmit FIFO low on data */
+ if ( eir & EIR_TXLDL_EV) {
+ /* Write data left in transmit buffer */
+ len = idev->tx_buff.len - idev->tx_buff.offset;
+
+ ASSERT( len > 0, return 0;);
+ actual = pc87108_pio_write( idev->io.iobase,
+ idev->tx_buff.head,
+ len, idev->io.fifo_size);
+ idev->tx_buff.offset += actual;
+ idev->tx_buff.head += actual;
+
+ idev->io.direction = IO_XMIT;
+ ASSERT( actual <= len, return 0;);
+
+ /* Check if finished */
+ if ( actual == len) {
+ DEBUG( 4, __FUNCTION__ "(), finished with frame!\n");
+ idev->netdev.tbusy = 0; /* Unlock */
+ idev->stats.tx_packets++;
+
+ mark_bh(NET_BH);
+
+ new_ier |= IER_TXEMP_IE;
+ } else
+ new_ier |= IER_TXLDL_IE;
+ }
+ /* Check if transmission has completed */
+ if ( eir & EIR_TXEMP_EV) {
+
+ /* Turn around and get ready to receive some data */
+ idev->io.direction = IO_RECV;
+ new_ier |= IER_RXHDL_IE;
+ }
+
+ /* Rx FIFO threshold or timeout */
+ if ( eir & EIR_RXHDL_EV) {
+ pc87108_pio_receive( idev);
+
+ /* Keep receiving */
+ new_ier |= IER_RXHDL_IE;
+ }
+ return new_ier;
+}
+
+/*
+ * Function pc87108_fir_interrupt (idev, eir)
+ *
+ * Handle MIR/FIR interrupt
+ *
+ */
+static __u8 pc87108_fir_interrupt( struct irda_device *idev, int iobase,
+ int eir)
+{
+ __u8 new_ier = 0;
+ __u8 bank;
+
+ bank = inb( iobase+BSR);
+
+ /* Status event, or end of frame detected in FIFO */
+ if ( eir & (EIR_SFIF_EV|EIR_LS_EV)) {
+ if ( pc87108_dma_receive_complete( idev, iobase)) {
+
+ /* Wait for next status FIFO interrupt */
+ new_ier |= IER_SFIF_IE;
+ } else {
+ /* DMA not finished yet */
+
+ /* Set timer value, resolution 125 us */
+ switch_bank( iobase, BANK4);
+ outb( 0x0f, iobase+TMRL); /* 125 us */
+ outb( 0x00, iobase+TMRH);
+
+ /* Start timer */
+ outb( IRCR1_TMR_EN, iobase+IRCR1);
+
+ new_ier |= IER_TMR_IE;
+ }
+ }
+ /* Timer finished */
+ if ( eir & EIR_TMR_EV) {
+ /* 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 ( idev->io.direction == IO_XMIT) {
+ pc87108_dma_write( idev, iobase);
+
+ /* Interrupt on DMA */
+ new_ier |= IER_DMA_IE;
+ } else {
+ /* Check if DMA has now finished */
+ pc87108_dma_receive_complete( idev, iobase);
+
+ new_ier |= IER_SFIF_IE;
+ }
+ }
+ /* Finished with transmission */
+ if ( eir & EIR_DMA_EV) {
+ pc87108_dma_xmit_complete( idev);
+
+ /* Check if there are more frames to be transmitted */
+ if ( irda_device_txqueue_empty( idev)) {
+ /* Prepare for receive */
+ pc87108_dma_receive( idev);
+
+ new_ier = IER_LS_IE|IER_SFIF_IE;
+ }
+ }
+ outb( bank, iobase+BSR);
+
+ return new_ier;
+}
+
+/*
+ * Function pc87108_interrupt (irq, dev_id, regs)
+ *
+ * An interrupt from the chip has arrived. Time to do some work
+ *
+ */
+static void pc87108_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ __u8 bsr, eir, ier;
+ int iobase;
+
+ struct irda_device *idev = (struct irda_device *) dev_id;
+
+ if (idev == NULL) {
+ printk( KERN_WARNING "%s: irq %d for unknown device.\n",
+ driver_name, irq);
+ return;
+ }
+
+ idev->netdev.interrupt = 1;
+
+ iobase = idev->io.iobase;
+
+ /* Save current bank */
+ bsr = inb( iobase+BSR);
+
+ switch_bank( iobase, BANK0);
+ ier = inb( iobase+IER);
+ eir = inb( iobase+EIR) & ier; /* Mask out the interesting ones */
+
+ outb( 0, iobase+IER); /* Disable interrupts */
+
+ if ( eir) {
+ /* Dispatch interrupt handler for the current speed */
+ if ( idev->io.baudrate > 115200)
+ ier = pc87108_fir_interrupt( idev, iobase, eir);
+ else
+ ier = pc87108_sir_interrupt( idev, eir);
+ }
+
+ outb( ier, iobase+IER); /* Restore interrupts */
+ outb( bsr, iobase+BSR); /* Restore bank register */
+
+ idev->netdev.interrupt = 0;
+}
+
+/*
+ * Function pc87108_wait_until_sent (idev)
+ *
+ * This function should put the current thread to sleep until all data
+ * have been sent, so it is safe to f.eks. change the speed.
+ */
+static void pc87108_wait_until_sent( struct irda_device *idev)
+{
+ /* Just delay 60 ms */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(6);
+}
+
+/*
+ * Function pc87108_is_receiving (idev)
+ *
+ * Return TRUE is we are currently receiving a frame
+ *
+ */
+static int pc87108_is_receiving( struct irda_device *idev)
+{
+ int status = FALSE;
+ int iobase;
+ __u8 bank;
+
+ ASSERT( idev != NULL, return FALSE;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return FALSE;);
+
+ if ( idev->io.baudrate > 115200) {
+ iobase = idev->io.iobase;
+
+ /* 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 = ( idev->rx_buff.state != OUTSIDE_FRAME);
+
+ return status;
+}
+
+/*
+ * Function pc87108_net_init (dev)
+ *
+ * Initialize network device
+ *
+ */
+static int pc87108_net_init( struct device *dev)
+{
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ /* Setup to be a normal IrDA network device driver */
+ irda_device_setup( dev);
+
+ /* Insert overrides below this line! */
+
+ return 0;
+}
+
+
+/*
+ * Function pc87108_net_open (dev)
+ *
+ * Start the device
+ *
+ */
+static int pc87108_net_open( struct device *dev)
+{
+ struct irda_device *idev;
+ int iobase;
+ __u8 bank;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ ASSERT( dev != NULL, return -1;);
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT( idev != NULL, return 0;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+ iobase = idev->io.iobase;
+
+ if (request_irq( idev->io.irq, pc87108_interrupt, 0, idev->name,
+ (void *) idev)) {
+ return -EAGAIN;
+ }
+ /*
+ * Always allocate the DMA channel after the IRQ,
+ * and clean up on failure.
+ */
+ if (request_dma(idev->io.dma, idev->name)) {
+ free_irq( idev->io.irq, idev);
+ return -EAGAIN;
+ }
+
+ /* Ready to play! */
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ /* 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);
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+/*
+ * Function pc87108_net_close (dev)
+ *
+ * Stop the device
+ *
+ */
+static int pc87108_net_close(struct device *dev)
+{
+ struct irda_device *idev;
+ int iobase;
+ __u8 bank;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ /* Stop device */
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ ASSERT( dev != NULL, return -1;);
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT( idev != NULL, return 0;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+ iobase = idev->io.iobase;
+
+ disable_dma( idev->io.dma);
+
+ /* Save current bank */
+ bank = inb( iobase+BSR);
+
+ /* Disable interrupts */
+ switch_bank( iobase, BANK0);
+ outb( 0, iobase+IER);
+
+ free_irq( idev->io.irq, idev);
+ free_dma( idev->io.dma);
+
+ /* Restore bank register */
+ outb( bank, iobase+BSR);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+#ifdef MODULE
+
+/*
+ * Function init_module (void)
+ *
+ *
+ *
+ */
+int init_module(void)
+{
+ pc87108_init();
+
+ return(0);
+}
+
+/*
+ * Function cleanup_module (void)
+ *
+ *
+ *
+ */
+void cleanup_module(void)
+{
+ pc87108_cleanup();
+}
+
+#endif
+
diff --git a/drivers/net/irda/tekram.c b/drivers/net/irda/tekram.c
new file mode 100644
index 000000000..bdada4afa
--- /dev/null
+++ b/drivers/net/irda/tekram.c
@@ -0,0 +1,314 @@
+/*********************************************************************
+ *
+ * Filename: tekram.c
+ * Version: 0.4
+ * 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: Mon Jan 18 11:30:38 1999
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ *
+ * Copyright (c) 1998 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/sched.h>
+#include <linux/init.h>
+
+#include <asm/ioctls.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/irda_device.h>
+#include <net/irda/irtty.h>
+#include <net/irda/dongle.h>
+
+static void tekram_reset( struct irda_device *dev, int unused);
+static void tekram_open( struct irda_device *dev, int type);
+static void tekram_close( struct irda_device *dev);
+static void tekram_change_speed( struct irda_device *dev, int baud);
+static void tekram_init_qos( struct irda_device *idev, struct qos_info *qos);
+
+static struct dongle dongle = {
+ TEKRAM_DONGLE,
+ tekram_open,
+ tekram_close,
+ tekram_reset,
+ tekram_change_speed,
+ tekram_init_qos,
+};
+
+__initfunc(void tekram_init(void))
+{
+ irtty_register_dongle( &dongle);
+}
+
+void tekram_cleanup(void)
+{
+ irtty_unregister_dongle( &dongle);
+}
+
+static void tekram_open( struct irda_device *dev, int type)
+{
+ strcat( dev->name, " <-> tekram");
+
+ MOD_INC_USE_COUNT;
+}
+
+static void tekram_close( struct irda_device *dev)
+{
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * Function tekram_change_speed (tty, baud)
+ *
+ * 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 void tekram_change_speed( struct irda_device *dev, int baud)
+{
+ struct irtty_cb *self;
+ struct tty_struct *tty;
+ struct termios old_termios;
+ int arg = 0;
+ int cflag;
+ __u8 byte;
+ int actual;
+ mm_segment_t fs;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ ASSERT( dev != NULL, return;);
+ ASSERT( dev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ self = (struct irtty_cb *) dev->priv;
+
+ ASSERT( self != NULL, return;);
+ ASSERT( self->magic == IRTTY_MAGIC, return;);
+
+ if ( !self->tty)
+ return;
+
+ tty = self->tty;
+
+ old_termios = *(tty->termios);
+ cflag = tty->termios->c_cflag;
+
+ cflag &= ~CBAUD;
+
+ switch (baud) {
+ case 9600:
+ default:
+ cflag |= B9600;
+ byte = 4;
+ break;
+ case 19200:
+ cflag |= B19200;
+ byte = 3;
+ break;
+ case 34800:
+ cflag |= B38400;
+ byte = 2;
+ break;
+ case 57600:
+ cflag |= B57600;
+ byte = 1;
+ break;
+ case 115200:
+ cflag |= B115200;
+ byte = 0;
+ break;
+ }
+
+ /* Set DTR, Clear RTS */
+ DEBUG( 0, __FUNCTION__ "(), Setting DTR, Clearing RTS\n");
+ arg = TIOCM_DTR | TIOCM_OUT2;
+
+ fs = get_fs();
+ set_fs( get_ds());
+
+ if ( tty->driver.ioctl( tty, NULL, TIOCMSET,
+ (unsigned long) &arg)) {
+ DEBUG( 0, "error setting Tekram speed!\n");
+ }
+ set_fs(fs);
+
+ /* Wait at least 7us */
+ udelay( 7);
+
+ DEBUG( 0, __FUNCTION__ "(), Writing control byte\n");
+ /* Write control byte */
+ if ( tty->driver.write)
+ actual = tty->driver.write( self->tty, 0, &byte, 1);
+
+ /* Wait at least 100 ms */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout( 10);
+
+ /* Set DTR, Set RTS */
+ DEBUG( 0, __FUNCTION__ "(), Setting DTR, Setting RTS\n");
+ arg = TIOCM_DTR | TIOCM_RTS | TIOCM_OUT2;
+
+ fs = get_fs();
+ set_fs( get_ds());
+
+ if ( tty->driver.ioctl( tty, NULL, TIOCMSET,
+ (unsigned long) &arg)) {
+ DEBUG( 0, "error setting Tekram speed!\n");
+ }
+ set_fs(fs);
+
+ DEBUG( 0, __FUNCTION__ "(), Setting new speed on serial port\n");
+ /* Now change the speed of the serial port */
+ tty->termios->c_cflag = cflag;
+ tty->driver.set_termios( tty, &old_termios);
+}
+
+/*
+ * Function tekram_reset (driver)
+ *
+ * This function resets the tekram dongle. Warning, this function
+ * must be called with a process context!!
+ *
+ * Algorithm:
+ * 0. set 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
+ */
+void tekram_reset( struct irda_device *dev, int unused)
+{
+ struct irtty_cb *self;
+ struct tty_struct *tty;
+ int arg = 0;
+ mm_segment_t fs;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ ASSERT( dev != NULL, return;);
+ ASSERT( dev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ self = (struct irtty_cb *) dev->priv;
+
+ ASSERT( self != NULL, return;);
+ ASSERT( self->magic == IRTTY_MAGIC, return;);
+
+ tty = self->tty;
+ if ( !tty)
+ return;
+
+ DEBUG( 0, __FUNCTION__ "(), Power off dongle\n");
+ arg = TIOCM_RTS | TIOCM_DTR | TIOCM_OUT2;
+
+ fs = get_fs();
+ set_fs( get_ds());
+
+ if ( tty->driver.ioctl( tty, NULL, TIOCMSET,
+ (unsigned long) &arg))
+ {
+ DEBUG(0, "error setting ESI speed!\n");
+ }
+ set_fs(fs);
+
+ /* Sleep 50 ms */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(5);
+
+ DEBUG( 0, __FUNCTION__ "(), Set DTR, clear RTS\n");
+ /* Set DTR, clear RTS */
+ arg = TIOCM_DTR | TIOCM_OUT2;
+
+ fs = get_fs();
+ set_fs( get_ds());
+
+ if ( tty->driver.ioctl( tty, NULL, TIOCMSET,
+ (unsigned long) &arg)) {
+ DEBUG( 0, "Error setting Tekram speed!\n");
+ }
+ set_fs(fs);
+
+ /* Should sleep 1 ms, but 10-20 should not do any harm */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(2);
+
+ DEBUG( 0, __FUNCTION__ "(), STATE3\n");
+ /* Clear DTR, clear RTS */
+ arg = TIOCM_OUT2;
+
+ fs = get_fs();
+ set_fs( get_ds());
+
+ if ( tty->driver.ioctl( tty, NULL, TIOCMSET, (unsigned long) &arg)) {
+ DEBUG( 0, "error setting Tekram speed!\n");
+ }
+ set_fs(fs);
+
+ /* Finished! */
+}
+
+/*
+ * Function tekram_init_qos (qos)
+ *
+ * Initialize QoS capabilities
+ *
+ */
+static void tekram_init_qos( struct irda_device *idev, struct qos_info *qos)
+{
+ qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
+ qos->min_turn_time.bits &= 0xfe; /* All except 0 ms */
+}
+
+#ifdef MODULE
+
+/*
+ * Function init_module (void)
+ *
+ * Initialize Tekram module
+ *
+ */
+int init_module(void)
+{
+ tekram_init();
+ return(0);
+}
+
+/*
+ * Function cleanup_module (void)
+ *
+ * Cleanup Tekram module
+ *
+ */
+void cleanup_module(void)
+{
+ tekram_cleanup();
+}
+
+#endif
diff --git a/drivers/net/irda/uircc.c b/drivers/net/irda/uircc.c
new file mode 100644
index 000000000..05da78a00
--- /dev/null
+++ b/drivers/net/irda/uircc.c
@@ -0,0 +1,914 @@
+/*********************************************************************
+ *
+ * Filename: uircc.c
+ * Version: 0.1
+ * Description: Driver for the Sharp Universal Infrared
+ * Communications Controller (UIRCC)
+ * Status: Experimental.
+ * Author: Dag Brattli <dagb@cs.uit.no>
+ * Created at: Sat Dec 26 10:59:03 1998
+ * Modified at: Tue Jan 19 23:54:04 1999
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ *
+ * Copyright (c) 1998 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.
+ *
+ * Applicable Models : Tecra 510CDT, 500C Series, 530CDT, 520CDT,
+ * 740CDT, Portege 300CT, 660CDT, Satellite 220C Series,
+ * Satellite Pro, 440C Series, 470CDT, 460C Series, 480C Series
+ *
+ * Notice that FIR mode is not working yet, since I don't know
+ * how to make the UIRCC drive the interrupt line, and not the
+ * UART (which is used for SIR speeds). Please mail me if you know!
+ *
+ ********************************************************************/
+
+#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/malloc.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/byteorder.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/uircc.h>
+#include <net/irda/irport.h>
+
+static char *driver_name = "uircc";
+
+#define CHIP_IO_EXTENT 16
+
+static unsigned int io[] = { 0x300, ~0, ~0, ~0 };
+static unsigned int io2[] = { 0x3e8, 0, 0, 0};
+static unsigned int irq[] = { 11, 0, 0, 0 };
+static unsigned int dma[] = { 5, 0, 0, 0 };
+
+static struct uircc_cb *dev_self[] = { NULL, NULL, NULL, NULL};
+
+/* Some prototypes */
+static int uircc_open( int i, unsigned int iobase, unsigned int board_addr,
+ unsigned int irq, unsigned int dma);
+static int uircc_close( struct irda_device *idev);
+static int uircc_probe( int iobase, int board_addr, int irq, int dma);
+static int uircc_dma_receive( struct irda_device *idev);
+static int uircc_dma_receive_complete(struct irda_device *idev, int iobase);
+static int uircc_hard_xmit( struct sk_buff *skb, struct device *dev);
+static void uircc_dma_write( struct irda_device *idev, int iobase);
+static void uircc_change_speed( struct irda_device *idev, int baud);
+static void uircc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void uircc_wait_until_sent( struct irda_device *idev);
+static int uircc_is_receiving( struct irda_device *idev);
+
+static int uircc_net_init( struct device *dev);
+static int uircc_net_open( struct device *dev);
+static int uircc_net_close( struct device *dev);
+
+/*
+ * Function uircc_init ()
+ *
+ * Initialize chip. Just try to find out how many chips we are dealing with
+ * and where they are
+ */
+__initfunc(int uircc_init(void))
+{
+ int i;
+
+ for ( i=0; (io[i] < 2000) && (i < 4); i++) {
+ int ioaddr = io[i];
+ if (check_region(ioaddr, CHIP_IO_EXTENT))
+ continue;
+ if (uircc_open( i, io[i], io2[i], irq[i], dma[i]) == 0)
+ return 0;
+ }
+ return -ENODEV;
+}
+
+/*
+ * Function uircc_cleanup ()
+ *
+ * Close all configured chips
+ *
+ */
+#ifdef MODULE
+static void uircc_cleanup(void)
+{
+ int i;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ for ( i=0; i < 4; i++) {
+ if ( dev_self[i])
+ uircc_close( &(dev_self[i]->idev));
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Function uircc_open (iobase, irq)
+ *
+ * Open driver instance
+ *
+ */
+static int uircc_open( int i, unsigned int iobase, unsigned int iobase2,
+ unsigned int irq, unsigned int dma)
+{
+ struct uircc_cb *self;
+ struct irda_device *idev;
+ int ret;
+
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ if (( uircc_probe( iobase, iobase2, irq, dma)) == -1)
+ return -1;
+
+ /*
+ * Allocate new instance of the driver
+ */
+ self = kmalloc( sizeof(struct uircc_cb), GFP_KERNEL);
+ if ( self == NULL) {
+ printk( KERN_ERR "IrDA: Can't allocate memory for "
+ "IrDA control block!\n");
+ return -ENOMEM;
+ }
+ memset( self, 0, sizeof(struct uircc_cb));
+
+ /* Need to store self somewhere */
+ dev_self[i] = self;
+
+ idev = &self->idev;
+
+ /* Initialize IO */
+ idev->io.iobase = iobase;
+ idev->io.iobase2 = iobase2; /* Used by irport */
+ idev->io.irq = irq;
+ idev->io.io_ext = CHIP_IO_EXTENT;
+ idev->io.io_ext2 = 8; /* Used by irport */
+ idev->io.dma = dma;
+ idev->io.fifo_size = 16;
+
+ /* Lock the port that we need */
+ ret = check_region( idev->io.iobase, idev->io.io_ext);
+ if ( ret < 0) {
+ DEBUG( 0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
+ idev->io.iobase);
+ /* uircc_cleanup( self->idev); */
+ return -ENODEV;
+ }
+ ret = check_region( idev->io.iobase2, idev->io.io_ext2);
+ if ( ret < 0) {
+ DEBUG( 0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
+ idev->io.iobase2);
+ /* uircc_cleanup( self->idev); */
+ return -ENODEV;
+ }
+ request_region( idev->io.iobase, idev->io.io_ext, idev->name);
+ request_region( idev->io.iobase2, idev->io.io_ext2, idev->name);
+
+ /* Initialize QoS for this device */
+ irda_init_max_qos_capabilies( &idev->qos);
+
+ /* The only value we must override it the baudrate */
+ idev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+ IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8);
+
+ idev->qos.min_turn_time.bits = 0x07;
+ irda_qos_bits_to_value( &idev->qos);
+
+ /* Specify which buffer allocation policy we need */
+ idev->rx_buff.flags = GFP_KERNEL | GFP_DMA;
+ idev->tx_buff.flags = GFP_KERNEL | GFP_DMA;
+
+ /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
+ idev->rx_buff.truesize = 4000;
+ idev->tx_buff.truesize = 4000;
+
+ /* Initialize callbacks */
+ idev->hard_xmit = uircc_hard_xmit;
+ idev->change_speed = uircc_change_speed;
+ idev->wait_until_sent = uircc_wait_until_sent;
+ idev->is_receiving = uircc_is_receiving;
+
+ /* Override the network functions we need to use */
+ idev->netdev.init = uircc_net_init;
+ idev->netdev.hard_start_xmit = uircc_hard_xmit;
+ idev->netdev.open = uircc_net_open;
+ idev->netdev.stop = uircc_net_close;
+
+ irport_open( iobase2);
+
+ /* Open the IrDA device */
+ irda_device_open( idev, driver_name, self);
+
+ return 0;
+}
+
+/*
+ * Function uircc_close (idev)
+ *
+ * Close driver instance
+ *
+ */
+static int uircc_close( struct irda_device *idev)
+{
+ int iobase;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ ASSERT( idev != NULL, return -1;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+ iobase = idev->io.iobase;
+
+ /* Disable modem */
+ outb( 0x00, iobase+UIRCC_CR10);
+
+ irport_close( idev->io.iobase2);
+
+ /* Release the PORT that this driver is using */
+ DEBUG( 4, __FUNCTION__ "(), Releasing Region %03x\n", idev->io.iobase);
+ release_region( idev->io.iobase, idev->io.io_ext);
+
+ if ( idev->io.iobase2) {
+ DEBUG( 4, __FUNCTION__ "(), Releasing Region %03x\n",
+ idev->io.iobase2);
+ release_region( idev->io.iobase2, idev->io.io_ext2);
+ }
+
+ irda_device_close( idev);
+
+ return 0;
+}
+
+/*
+ * Function uircc_probe (iobase, board_addr, irq, dma)
+ *
+ * Returns non-negative on success.
+ *
+ */
+static int uircc_probe( int iobase, int iobase2, int irq, int dma)
+{
+ int version;
+ int probe_irq=0;
+ unsigned long mask;
+ int i;
+
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ /* read the chip version, should be 0x03 */
+ version = inb( iobase+UIRCC_SR8);
+
+ if ( version != 0x03) {
+ DEBUG( 0, __FUNCTION__ "(), Wrong chip version");
+ return -1;
+ }
+ DEBUG( 0, "UIRCC driver loaded. Version: 0x%02x\n", version);
+
+ /* Reset chip */
+ outb( UIRCC_CR0_SYS_RST, iobase+UIRCC_CR0);
+
+ /* Initialize some registers */
+ outb( 0, iobase+UIRCC_CR11);
+ outb( 0, iobase+UIRCC_CR9);
+
+ /* Enable DMA single mode */
+ outb( UIRCC_CR1_RX_DMA|UIRCC_CR1_TX_DMA|UIRCC_CR1_MUST_SET,
+ iobase+UIRCC_CR1);
+
+ /* Disable interrupts */
+ outb( 0xff, iobase+UIRCC_CR2);
+
+#if 0
+ irport_close( iobase2);
+
+ for (i=0;i<1;i++) {
+
+ /* Set appropriate speed mode */
+ outb( UIRCC_CR10_FIR, iobase+UIRCC_CR10);
+
+ /* Enable DMA single mode */
+ outb( UIRCC_CR1_RX_DMA|UIRCC_CR1_TX_DMA|UIRCC_CR1_MUST_SET,
+ iobase+UIRCC_CR1);
+
+ /* Set up timer */
+ outb( 0x01, iobase+UIRCC_CR12);
+ outb( 0x00, iobase+UIRCC_CR13);
+
+ /* Set interrupt mask */
+ outb( 0x82, iobase+UIRCC_CR2);
+
+ DEBUG( 0, __FUNCTION__ "(*), sr3=%#x, sr2=%#x, sr10=%#x, sr12=%#x\n",
+ inb( iobase+UIRCC_SR3), inb( iobase+UIRCC_SR2),
+ inb( iobase+UIRCC_SR10), inb( iobase+UIRCC_SR12));
+
+ mask = probe_irq_on();
+
+ /* Enable timer */
+ outb( 0x08, iobase+UIRCC_CR11);
+
+ udelay( 10000); /* Wait for interrupt! */
+
+ probe_irq = probe_irq_off( mask);
+
+ DEBUG( 0, "Found irq=%d\n", probe_irq);
+
+ DEBUG( 0, __FUNCTION__ "(), sr3=%#x, sr2=%#x, sr10=%#x, sr12=%#x\n",
+ inb( iobase+UIRCC_SR3), inb( iobase+UIRCC_SR2),
+ inb( iobase+UIRCC_SR10), inb( iobase+UIRCC_SR12));
+
+
+ /* Diable timer */
+ outb( 0x00, iobase+UIRCC_CR11);
+ }
+#endif
+
+ /* Set self poll address */
+
+ return 0;
+}
+
+/*
+ * Function uircc_change_speed (idev, baud)
+ *
+ * Change the speed of the device
+ *
+ */
+static void uircc_change_speed( struct irda_device *idev, int speed)
+{
+ struct uircc_cb *self;
+ int iobase;
+ int modem = UIRCC_CR10_SIR;
+
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ self = idev->priv;
+ iobase = idev->io.iobase;
+
+ /* Update accounting for new speed */
+ idev->io.baudrate = speed;
+
+ /* Disable interrupts */
+ outb( 0xff, iobase+UIRCC_CR2);
+
+ switch ( speed) {
+ case 9600:
+ case 19200:
+ case 37600:
+ case 57600:
+ case 115200:
+/* irport_open( idev->io.iobase2); */
+ irport_change_speed( idev->io.iobase2, speed);
+ modem = UIRCC_CR10_SIR;
+ break;
+ case 576000:
+
+ DEBUG(0, __FUNCTION__ "(), handling baud of 576000\n");
+ break;
+ case 1152000:
+
+ DEBUG(0, __FUNCTION__ "(), handling baud of 1152000\n");
+ break;
+ case 4000000:
+ irport_close( idev->io.iobase2);
+ modem = UIRCC_CR10_FIR;
+ DEBUG(0, __FUNCTION__ "(), handling baud of 4000000\n");
+ break;
+ default:
+ DEBUG( 0, __FUNCTION__ "(), unknown baud rate of %d\n", speed);
+ break;
+ }
+
+ /* Set appropriate speed mode */
+ outb( modem, iobase+UIRCC_CR10);
+
+ idev->netdev.tbusy = 0;
+
+ /* Enable some interrupts so we can receive frames */
+ if ( speed > 115200) {
+ /* Enable DMA single mode */
+ outb( UIRCC_CR1_RX_DMA|UIRCC_CR1_TX_DMA|UIRCC_CR1_MUST_SET,
+ iobase+UIRCC_CR1);
+
+ /* outb( UIRCC_CR2_RECV_MASK, iobase+UIRCC_CR2); */
+ outb( 0, iobase+UIRCC_CR2);
+ uircc_dma_receive( idev);
+ }
+}
+
+/*
+ * Function uircc_hard_xmit (skb, dev)
+ *
+ * Transmit the frame!
+ *
+ */
+static int uircc_hard_xmit( struct sk_buff *skb, struct device *dev)
+{
+ struct irda_device *idev;
+ int iobase;
+ int mtt;
+
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT( idev != NULL, return 0;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+ iobase = idev->io.iobase;
+
+ DEBUG(0, __FUNCTION__ "(%ld), skb->len=%d\n", jiffies, (int) skb->len);
+
+ /* Use irport for SIR speeds */
+ if ( idev->io.baudrate <= 115200) {
+ return irport_hard_xmit( skb, dev);
+ }
+
+ if ( dev->tbusy) {
+ __u8 sr3;
+
+ DEBUG( 4, __FUNCTION__ "(), tbusy==TRUE\n");
+
+ return -EBUSY;
+ }
+
+ /* Lock transmit buffer */
+ if ( irda_lock( (void *) &dev->tbusy) == FALSE)
+ return -EBUSY;
+
+ memcpy( idev->tx_buff.data, skb->data, skb->len);
+
+ /* Make sure that the length is a multiple of 16 bits */
+ if ( skb->len & 0x01)
+ skb->len++;
+
+ idev->tx_buff.len = skb->len;
+ idev->tx_buff.head = idev->tx_buff.data;
+ idev->tx_buff.offset = 0;
+
+ mtt = irda_get_mtt( skb);
+
+ /* Use udelay for delays less than 50 us. */
+ if (mtt)
+ udelay( mtt);
+
+ /* Enable transmit interrupts */
+ /* outb( UIRCC_CR2_XMIT_MASK, iobase+UIRCC_CR2); */
+ outb( 0, iobase+UIRCC_CR2);
+
+ uircc_dma_write( idev, iobase);
+
+ dev_kfree_skb( skb);
+
+ return 0;
+}
+
+/*
+ * Function uircc_dma_xmit (idev, iobase)
+ *
+ * Transmit data using DMA
+ *
+ */
+static void uircc_dma_write( struct irda_device *idev, int iobase)
+{
+ struct uircc_cb *self;
+ int i;
+
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ self = idev->priv;
+
+ /* Receiving disable */
+ self->cr3 &= ~UIRCC_CR3_RECV_EN;
+ outb( self->cr3, iobase+UIRCC_CR3);
+
+ setup_dma( idev->io.dma, idev->tx_buff.data, idev->tx_buff.len,
+ DMA_MODE_WRITE);
+
+ DEBUG( 0, __FUNCTION__ "residue=%d\n",
+ get_dma_residue( idev->io.dma));
+
+ idev->io.direction = IO_XMIT;
+
+ /* Set frame length */
+ outb( idev->tx_buff.len & 0xff, iobase+UIRCC_CR4); /* Low byte */
+ outb( idev->tx_buff.len >> 8, iobase+UIRCC_CR5); /* High byte */
+
+ /* Enable transmit and transmit CRC */
+ self->cr3 |= (UIRCC_CR3_XMIT_EN|UIRCC_CR3_TX_CRC_EN);
+ outb( self->cr3, iobase+UIRCC_CR3);
+}
+
+/*
+ * Function uircc_dma_xmit_complete (idev)
+ *
+ * The transfer of a frame in finished. This function will only be called
+ * by the interrupt handler
+ *
+ */
+static void uircc_dma_xmit_complete( struct irda_device *idev, int underrun)
+{
+ struct uircc_cb *self;
+ int iobase;
+ int len;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ self = idev->priv;
+
+ iobase = idev->io.iobase;
+
+ /* Select TX counter */
+ outb( UIRCC_CR0_CNT_SWT, iobase+UIRCC_CR0);
+
+ /* Read TX length counter */
+ len = inb( iobase+UIRCC_SR4); /* Low byte */
+ len |= inb( iobase+UIRCC_SR5) << 8; /* High byte */
+
+ /* Disable transmit */
+ self->cr3 &= ~UIRCC_CR3_XMIT_EN;
+ outb( self->cr3, iobase+UIRCC_CR3);
+
+ /* Check for underrrun! */
+ if ( underrun) {
+ idev->stats.tx_errors++;
+ idev->stats.tx_fifo_errors++;
+ } else {
+ idev->stats.tx_packets++;
+ idev->stats.tx_bytes += idev->tx_buff.len;
+ }
+
+ /* Unlock tx_buff and request another frame */
+ idev->netdev.tbusy = 0; /* Unlock */
+ idev->media_busy = FALSE;
+
+ /* Tell the network layer, that we can accept more frames */
+ mark_bh( NET_BH);
+}
+
+/*
+ * Function uircc_dma_receive (idev)
+ *
+ * Get ready for receiving a frame. The device will initiate a DMA
+ * if it starts to receive a frame.
+ *
+ */
+static int uircc_dma_receive( struct irda_device *idev)
+{
+ struct uircc_cb *self;
+ int iobase;
+
+ ASSERT( idev != NULL, return -1;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+ DEBUG( 0, __FUNCTION__ "\n");
+
+ self = idev->priv;
+ iobase= idev->io.iobase;
+
+ /* Disable DMA */
+
+ setup_dma( idev->io.dma, idev->rx_buff.data, idev->rx_buff.truesize,
+ DMA_MODE_READ);
+
+ /* driver->media_busy = FALSE; */
+ idev->io.direction = IO_RECV;
+ idev->rx_buff.head = idev->rx_buff.data;
+ idev->rx_buff.offset = 0;
+
+ /* Enable receiving with CRC */
+ self->cr3 |= (UIRCC_CR3_RECV_EN|UIRCC_CR3_RX_CRC_EN);
+ outb( self->cr3, iobase+UIRCC_CR3);
+
+ /* Address check? */
+
+ DEBUG( 4, __FUNCTION__ "(), done!\n");
+
+ return 0;
+}
+
+/*
+ * Function uircc_dma_receive_complete (idev)
+ *
+ * Finished with receiving frames
+ *
+ *
+ */
+static int uircc_dma_receive_complete( struct irda_device *idev, int iobase)
+{
+ struct sk_buff *skb;
+ struct uircc_cb *self;
+ int len;
+
+ self = idev->priv;
+
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ /* Check for CRC or framing error */
+ if ( inb( iobase+UIRCC_SR0) & UIRCC_SR0_RX_CRCFRM) {
+ DEBUG( 0, __FUNCTION__ "(), crc or frm error\n");
+ return -1;
+ }
+
+ /* Select receive length counter */
+ outb( 0x00, iobase+UIRCC_CR0);
+
+ /* Read frame length */
+ len = inb( iobase+UIRCC_SR4); /* Low byte */
+ len |= inb( iobase+UIRCC_SR5) << 8; /* High byte */
+
+ DEBUG( 0, __FUNCTION__ "(), len=%d\n", len);
+
+ /* Receiving disable */
+ self->cr3 &= ~UIRCC_CR3_RECV_EN;
+ outb( self->cr3, iobase+UIRCC_CR3);
+
+ skb = dev_alloc_skb( len+1);
+ if (skb == NULL) {
+ printk( KERN_INFO __FUNCTION__
+ "(), memory squeeze, dropping frame.\n");
+ /* Restore bank register */
+ return FALSE;
+ }
+
+ /* Make sure IP header gets aligned */
+ skb_reserve( skb, 1);
+
+ /* Copy frame without CRC */
+ /* if ( idev->io.baudrate < 4000000) { */
+/* skb_put( skb, len-2); */
+/* memcpy( skb->data, idev->rx_buff.head, len-2); */
+/* } else { */
+/* skb_put( skb, len-4); */
+/* memcpy( skb->data, idev->rx_buff.head, len-4); */
+/* } */
+
+ skb_put( skb, len);
+ memcpy( skb->data, idev->rx_buff.head, len);
+ idev->stats.rx_packets++;
+
+ skb->dev = &idev->netdev;
+ skb->mac.raw = skb->data;
+ skb->protocol = htons(ETH_P_IRDA);
+ netif_rx( skb);
+
+ return TRUE;
+}
+
+/*
+ * Function uircc_interrupt (irq, dev_id, regs)
+ *
+ * An interrupt from the chip has arrived. Time to do some work
+ *
+ */
+static void uircc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ __u8 sr3;
+ int iobase;
+
+ struct irda_device *idev = (struct irda_device *) dev_id;
+
+ if (idev == NULL) {
+ printk( KERN_WARNING "%s: irq %d for unknown device.\n",
+ driver_name, irq);
+ return;
+ }
+
+ if (idev->io.baudrate <= 115200)
+ return irport_interrupt( irq, dev_id, regs);
+
+ iobase = idev->io.iobase;
+
+ /* Read interrupt status */
+ sr3 = inb( iobase+UIRCC_SR3);
+ if (!sr3) {
+ return;
+ }
+
+ idev->netdev.interrupt = 1;
+
+ DEBUG( 4, __FUNCTION__ "(), sr3=%#x, sr2=%#x, sr10=%#x\n",
+ inb( iobase+UIRCC_SR3), inb( iobase+UIRCC_SR2),
+ inb( iobase+UIRCC_SR10));
+
+ /*
+ * Check what interrupt this is. The UIRCC will not report two
+ * different interrupts at the same time!
+ */
+ switch( sr3) {
+ case UIRCC_SR3_RX_EOF: /* Check of end of frame */
+ uircc_dma_receive_complete( idev, iobase);
+ break;
+ case UIRCC_SR3_TXUR: /* Check for transmit underrun */
+ uircc_dma_xmit_complete( idev, TRUE);
+ break;
+ case UIRCC_SR3_TX_DONE:
+ uircc_dma_xmit_complete( idev, FALSE);
+ break;
+ case UIRCC_SR3_TMR_OUT:
+ /* Disable timer */
+ outb( inb( iobase+UIRCC_CR11) & ~UIRCC_CR11_TMR_EN,
+ iobase+UIRCC_CR11);
+ break;
+ default:
+ DEBUG( 0, __FUNCTION__ "(), unknown interrupt status=%#x\n",
+ sr3);
+ break;
+ }
+
+ idev->netdev.interrupt = 0;
+}
+
+/*
+ * Function uircc_wait_until_sent (idev)
+ *
+ * This function should put the current thread to sleep until all data
+ * have been sent, so it is safe to change the speed.
+ */
+static void uircc_wait_until_sent( struct irda_device *idev)
+{
+ /* Just delay 60 ms */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(6);
+}
+
+/*
+ * Function uircc_is_receiving (idev)
+ *
+ * Return TRUE is we are currently receiving a frame
+ *
+ */
+static int uircc_is_receiving( struct irda_device *idev)
+{
+ int status = FALSE;
+ /* int iobase; */
+
+ ASSERT( idev != NULL, return FALSE;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return FALSE;);
+
+ if ( idev->io.baudrate > 115200) {
+
+ } else
+ status = ( idev->rx_buff.state != OUTSIDE_FRAME);
+
+ return status;
+}
+
+/*
+ * Function uircc_net_init (dev)
+ *
+ * Initialize network device
+ *
+ */
+static int uircc_net_init( struct device *dev)
+{
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ /* Setup to be a normal IrDA network device driver */
+ irda_device_setup( dev);
+
+ /* Insert overrides below this line! */
+
+ return 0;
+}
+
+
+/*
+ * Function uircc_net_open (dev)
+ *
+ * Start the device
+ *
+ */
+static int uircc_net_open( struct device *dev)
+{
+ struct irda_device *idev;
+ int iobase;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ ASSERT( dev != NULL, return -1;);
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT( idev != NULL, return 0;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+ iobase = idev->io.iobase;
+
+ if (request_irq( idev->io.irq, uircc_interrupt, 0, idev->name,
+ (void *) idev)) {
+ return -EAGAIN;
+ }
+ /*
+ * Always allocate the DMA channel after the IRQ,
+ * and clean up on failure.
+ */
+ if (request_dma(idev->io.dma, idev->name)) {
+ free_irq( idev->io.irq, idev);
+ return -EAGAIN;
+ }
+
+ /* Ready to play! */
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ /* turn on interrupts */
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+/*
+ * Function uircc_net_close (dev)
+ *
+ * Stop the device
+ *
+ */
+static int uircc_net_close(struct device *dev)
+{
+ struct irda_device *idev;
+ int iobase;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ /* Stop device */
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ ASSERT( dev != NULL, return -1;);
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT( idev != NULL, return 0;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+ iobase = idev->io.iobase;
+
+ disable_dma( idev->io.dma);
+
+ /* Disable interrupts */
+
+ free_irq( idev->io.irq, idev);
+ free_dma( idev->io.dma);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+#ifdef MODULE
+
+/*
+ * Function init_module (void)
+ *
+ *
+ *
+ */
+int init_module(void)
+{
+ uircc_init();
+
+ return(0);
+}
+
+/*
+ * Function cleanup_module (void)
+ *
+ *
+ *
+ */
+void cleanup_module(void)
+{
+ uircc_cleanup();
+}
+
+#endif
+
diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c
new file mode 100644
index 000000000..5a7995ef7
--- /dev/null
+++ b/drivers/net/irda/w83977af_ir.c
@@ -0,0 +1,1273 @@
+/*********************************************************************
+ *
+ * Filename: w83977af_ir.c
+ * Version: 0.8
+ * Description: FIR/MIR 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: Mon Dec 14 21:51:53 1998
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ *
+ * Copyright (c) 1998 Corel Computer Corp.
+ * Copyright (c) 1998 Dag Brattli <dagb@cs.uit.no>
+ *
+ * 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 Corel Computer Corp. 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 w83977af_ir.c since the implementations is 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/kernel.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/byteorder.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/irda_device.h>
+#include <net/irda/w83977af.h>
+#include <net/irda/w83977af_ir.h>
+
+#define NETWINDER
+
+static char *driver_name = "w83977af_ir";
+
+#define CHIP_IO_EXTENT 8
+
+static unsigned int io[] = { 0x400, ~0, ~0, ~0 };
+static unsigned int irq[] = { 22, 0, 0, 0 };
+static unsigned int dma[] = { 0, 0, 0, 0 };
+
+static struct irda_device *dev_self[] = { NULL, NULL, NULL, NULL};
+
+/* For storing entries in the status FIFO */
+struct st_fifo_entry {
+ int status;
+ int len;
+};
+
+static struct st_fifo_entry prev;
+
+/* Some prototypes */
+static int w83977af_open( int i, unsigned int iobase, unsigned int irq,
+ unsigned int dma);
+static int w83977af_close( struct irda_device *idev);
+static int w83977af_probe( int iobase, int irq, int dma);
+static int w83977af_dma_receive(struct irda_device *idev);
+static int w83977af_dma_receive_complete(struct irda_device *idev);
+static int w83977af_hard_xmit( struct sk_buff *skb, struct device *dev);
+static int w83977af_pio_write( int iobase, __u8 *buf, int len, int fifo_size);
+static void w83977af_dma_write( struct irda_device *idev, int iobase);
+static void w83977af_change_speed( struct irda_device *idev, int baud);
+static void w83977af_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void w83977af_wait_until_sent( struct irda_device *idev);
+static int w83977af_is_receiving( struct irda_device *idev);
+
+static int w83977af_net_init( struct device *dev);
+static int w83977af_net_open( struct device *dev);
+static int w83977af_net_close( struct device *dev);
+
+/*
+ * Function w83977af_init ()
+ *
+ * Initialize chip. Just try to find out how many chips we are dealing with
+ * and where they are
+ */
+__initfunc(int w83977af_init(void))
+{
+ int i;
+
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ prev.status = 0;
+
+ for ( i=0; (io[i] < 2000) && (i < 4); i++) {
+ int ioaddr = io[i];
+ if (check_region(ioaddr, CHIP_IO_EXTENT))
+ continue;
+ if (w83977af_open( i, io[i], irq[i], dma[i]) == 0)
+ return 0;
+ }
+ return -ENODEV;
+}
+
+/*
+ * Function w83977af_cleanup ()
+ *
+ * Close all configured chips
+ *
+ */
+#ifdef MODULE
+void w83977af_cleanup(void)
+{
+ int i;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ for ( i=0; i < 4; i++) {
+ if ( dev_self[i])
+ w83977af_close( dev_self[i]);
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Function w83977af_open (iobase, irq)
+ *
+ * Open driver instance
+ *
+ */
+int w83977af_open( int i, unsigned int iobase, unsigned int irq,
+ unsigned int dma)
+{
+ struct irda_device *idev;
+ int ret;
+
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ if ( w83977af_probe( iobase, irq, dma) == -1)
+ return -1;
+
+ /*
+ * Allocate new instance of the driver
+ */
+ idev = kmalloc( sizeof(struct irda_device), GFP_KERNEL);
+ if ( idev == NULL) {
+ printk( KERN_ERR "IrDA: Can't allocate memory for "
+ "IrDA control block!\n");
+ return -ENOMEM;
+ }
+ memset( idev, 0, sizeof(struct irda_device));
+
+ /* Need to store self somewhere */
+ dev_self[i] = idev;
+
+ /* Initialize IO */
+ idev->io.iobase = iobase;
+ idev->io.irq = irq;
+ idev->io.io_ext = CHIP_IO_EXTENT;
+ idev->io.dma = dma;
+ idev->io.fifo_size = 32;
+
+ /* Lock the port that we need */
+ ret = check_region( idev->io.iobase, idev->io.io_ext);
+ if ( ret < 0) {
+ DEBUG( 0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
+ idev->io.iobase);
+ /* w83977af_cleanup( self->idev); */
+ return -ENODEV;
+ }
+ request_region( idev->io.iobase, idev->io.io_ext, idev->name);
+
+ /* Initialize QoS for this device */
+ irda_init_max_qos_capabilies( &idev->qos);
+
+ /* The only value we must override it the baudrate */
+
+ /* FIXME: The HP HDLS-1100 does not support 1152000! */
+ idev->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 */
+ idev->qos.min_turn_time.bits = 0x03; /* 1ms and more */
+ irda_qos_bits_to_value( &idev->qos);
+
+ /* Specify which buffer allocation policy we need */
+ idev->rx_buff.flags = GFP_KERNEL | GFP_DMA;
+ idev->tx_buff.flags = GFP_KERNEL | GFP_DMA;
+
+ /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
+ idev->rx_buff.truesize = 14384;
+ idev->tx_buff.truesize = 4000;
+
+ /* Initialize callbacks */
+ idev->hard_xmit = w83977af_hard_xmit;
+ idev->change_speed = w83977af_change_speed;
+ idev->wait_until_sent = w83977af_wait_until_sent;
+ idev->is_receiving = w83977af_is_receiving;
+
+ /* Override the network functions we need to use */
+ idev->netdev.init = w83977af_net_init;
+ idev->netdev.hard_start_xmit = w83977af_hard_xmit;
+ idev->netdev.open = w83977af_net_open;
+ idev->netdev.stop = w83977af_net_close;
+
+ /* Open the IrDA device */
+ irda_device_open( idev, driver_name, NULL);
+
+ return 0;
+}
+
+/*
+ * Function w83977af_close (idev)
+ *
+ * Close driver instance
+ *
+ */
+static int w83977af_close( struct irda_device *idev)
+{
+ int iobase;
+
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ ASSERT( idev != NULL, return -1;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+ iobase = idev->io.iobase;
+
+ /* enter PnP configuration mode */
+ w977_efm_enter();
+
+ w977_select_device(W977_DEVICE_IR);
+
+ /* Deactivate device */
+ w977_write_reg(0x30, 0x00);
+
+ w977_efm_exit();
+
+ /* Release the PORT that this driver is using */
+ DEBUG(0 , __FUNCTION__ "(), Releasing Region %03x\n",
+ idev->io.iobase);
+ release_region( idev->io.iobase, idev->io.io_ext);
+
+ irda_device_close( idev);
+
+ return 0;
+}
+
+/*
+ * Function w83977af_probe (iobase, irq, dma)
+ *
+ * Returns non-negative on success.
+ *
+ */
+int w83977af_probe( int iobase, int irq, int dma)
+{
+ int version;
+
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ /* Enter PnP configuration mode */
+ w977_efm_enter();
+
+ w977_select_device(W977_DEVICE_IR);
+
+ /* Configure PnP port, IRQ, and DMA channel */
+ w977_write_reg(0x60, (iobase >> 8) & 0xff);
+ w977_write_reg(0x61, (iobase) & 0xff);
+ w977_write_reg(0x70, 0x06);
+#ifdef NETWINDER
+ w977_write_reg(0x74, dma+1); /* Netwinder uses 1 higher than Linux */
+#else
+ w977_write_reg(0x74, dma);
+#endif
+ w977_write_reg(0x75, dma); /* Disable Tx DMA */
+
+ /* Set append hardware CRC, enable IR bank selection */
+ w977_write_reg(0xf0, APEDCRC|ENBNKSEL);
+
+ /* Activate device */
+ w977_write_reg(0x30, 0x01);
+
+ w977_efm_exit();
+
+ /* 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)) {
+ DEBUG( 0, __FUNCTION__ "(), Wrong chip version");
+ return -1;
+ }
+
+ /* 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);
+/* outb( 0xa7, 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);
+
+ DEBUG(0, "W83977AF (IR) driver loaded. Version: 0x%02x\n", version);
+
+ return 0;
+}
+
+/*
+ * Function w83977af_change_speed (idev, baud)
+ *
+ * Change the speed of the device
+ *
+ */
+void w83977af_change_speed( struct irda_device *idev, int speed)
+{
+ int ir_mode = HCR_SIR;
+ int iobase;
+ __u8 set;
+
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ iobase = idev->io.iobase;
+
+ /* Update accounting for new speed */
+ idev->io.baudrate = 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 37600: 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;
+ DEBUG(0, __FUNCTION__ "(), handling baud of 576000\n");
+ break;
+ case 1152000:
+ ir_mode = HCR_MIR_1152;
+ DEBUG(0, __FUNCTION__ "(), handling baud of 1152000\n");
+ break;
+ case 4000000:
+ ir_mode = HCR_FIR;
+ DEBUG(0, __FUNCTION__ "(), handling baud of 4000000\n");
+ break;
+ default:
+ ir_mode = HCR_FIR;
+ DEBUG( 0, __FUNCTION__ "(), unknown baud rate of %d\n", 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( UFR_RXTL|UFR_TXTL|UFR_TXF_RST|UFR_RXF_RST|UFR_EN_FIFO, iobase+UFR);
+
+ idev->netdev.tbusy = 0;
+
+ /* Enable some interrupts so we can receive frames */
+ switch_bank(iobase, SET0);
+ if ( speed > 115200) {
+ outb( ICR_EFSFI, iobase+ICR);
+ w83977af_dma_receive( idev);
+ } 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 device *dev)
+{
+ struct irda_device *idev;
+ int iobase;
+ __u8 set;
+ int mtt;
+
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT( idev != NULL, return 0;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+ iobase = idev->io.iobase;
+
+ DEBUG(4, __FUNCTION__ "(%ld), skb->len=%d\n", jiffies, (int) skb->len);
+
+ if ( dev->tbusy) {
+ DEBUG( 4, __FUNCTION__ "(), tbusy==TRUE\n");
+
+ return -EBUSY;
+ }
+
+ /* Lock transmit buffer */
+ if ( irda_lock( (void *) &dev->tbusy) == FALSE)
+ return -EBUSY;
+
+ /* Save current set */
+ set = inb( iobase+SSR);
+
+ /* Decide if we should use PIO or DMA transfer */
+ if ( idev->io.baudrate > 115200) {
+ memcpy( idev->tx_buff.data, skb->data, skb->len);
+ idev->tx_buff.len = skb->len;
+ idev->tx_buff.head = idev->tx_buff.data;
+ idev->tx_buff.offset = 0;
+
+ mtt = irda_get_mtt( skb);
+ 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);
+ idev->io.direction = IO_XMIT;
+
+ /* Enable timer interrupt */
+ switch_bank( iobase, SET0);
+ outb( ICR_ETMRI, iobase+ICR);
+ } else {
+ if ( mtt)
+ udelay( mtt);
+
+ /* Enable DMA interrupt */
+ switch_bank( iobase, SET0);
+ outb( ICR_EDMAI, iobase+ICR);
+ w83977af_dma_write( idev, iobase);
+ }
+ } else {
+ idev->tx_buff.len = async_wrap_skb( skb, idev->tx_buff.data,
+ idev->tx_buff.truesize);
+
+ idev->tx_buff.offset = 0;
+ idev->tx_buff.head = idev->tx_buff.data;
+
+ /* Add interrupt on tx low level (will fire immediately) */
+ switch_bank( iobase, SET0);
+ outb( ICR_ETXTHI, iobase+ICR);
+ }
+ dev_kfree_skb( skb);
+
+ /* Restore set register */
+ outb( set, iobase+SSR);
+
+ return 0;
+}
+
+
+/*
+ * Function w83977af_dma_write (idev, iobase)
+ *
+ *
+ *
+ */
+static void w83977af_dma_write( struct irda_device *idev, int iobase)
+{
+ __u8 set;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ /* Save current set */
+ set = inb( iobase+SSR);
+
+ /* Disable DMA */
+ switch_bank(iobase, SET0);
+ outb( inb( iobase+HCR) & ~HCR_EN_DMA, iobase+HCR);
+
+ setup_dma( idev->io.dma, idev->tx_buff.data, idev->tx_buff.len,
+ DMA_MODE_WRITE);
+
+ /* idev->media_busy = TRUE; */
+ idev->io.direction = IO_XMIT;
+
+ /* Choose transmit DMA channel */
+ switch_bank(iobase, SET2);
+ outb( inb( iobase+ADCR1) | ADCR1_D_CHSW|ADCR1_DMA_F|ADCR1_ADV_SL,
+ iobase+ADCR1);
+
+ /* Enable DMA */
+ switch_bank( iobase, SET0);
+ outb( inb( iobase+HCR) | HCR_EN_DMA, iobase+HCR);
+
+ /* 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;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ /* Save current bank */
+ set = inb( iobase+SSR);
+
+ switch_bank( iobase, SET0);
+ if (!( inb_p( iobase+USR) & USR_TSRE)) {
+ DEBUG( 4, __FUNCTION__ "(), warning, FIFO not empty yet!\n");
+
+ fifo_size -= 17;
+ DEBUG( 4, __FUNCTION__ "%d bytes left in tx fifo\n", fifo_size);
+ }
+
+ /* Fill FIFO with current frame */
+ while (( fifo_size-- > 0) && (actual < len)) {
+ /* Transmit next byte */
+ outb( buf[actual++], iobase+TBR);
+ }
+
+ DEBUG( 4, __FUNCTION__ "(), fifo_size %d ; %d sent of %d\n",
+ fifo_size, actual, len);
+
+ /* Restore bank */
+ outb( set, iobase+SSR);
+
+ return actual;
+}
+
+/*
+ * Function w83977af_dma_xmit_complete (idev)
+ *
+ * The transfer of a frame in finished. So do the necessary things
+ *
+ *
+ */
+void w83977af_dma_xmit_complete( struct irda_device *idev)
+{
+ int iobase;
+ __u8 set;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ iobase = idev->io.iobase;
+
+ /* 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) {
+ DEBUG( 0, __FUNCTION__ "(), Transmit underrun!\n");
+
+ idev->stats.tx_errors++;
+ idev->stats.tx_fifo_errors++;
+
+ /* Clear bit, by writing 1 to it */
+ outb( AUDR_UNDR, iobase+AUDR);
+ } else
+ idev->stats.tx_packets++;
+
+ /* Unlock tx_buff and request another frame */
+ idev->netdev.tbusy = 0; /* Unlock */
+ idev->media_busy = FALSE;
+
+ /* Tell the network layer, that we want more frames */
+ mark_bh( NET_BH);
+
+ /* Restore set */
+ outb( set, iobase+SSR);
+}
+
+/*
+ * Function w83977af_dma_receive (idev)
+ *
+ * Get ready for receiving a frame. The device will initiate a DMA
+ * if it starts to receive a frame.
+ *
+ */
+int w83977af_dma_receive( struct irda_device *idev)
+{
+ int iobase;
+ __u8 set;
+#ifdef NETWINDER
+ unsigned long flags;
+ __u8 hcr;
+#endif
+
+ ASSERT( idev != NULL, return -1;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+ DEBUG( 0, __FUNCTION__ "\n");
+
+ iobase= idev->io.iobase;
+
+ /* Save current set */
+ set = inb( iobase+SSR);
+
+ /* Disable DMA */
+ switch_bank( iobase, SET0);
+ outb( inb( iobase+HCR) & ~HCR_EN_DMA, iobase+HCR);
+
+#ifdef NETWINDER
+ save_flags(flags);
+ cli();
+
+ disable_dma( idev->io.dma);
+ clear_dma_ff( idev->io.dma);
+ set_dma_mode( idev->io.dma, DMA_MODE_READ);
+ set_dma_addr( idev->io.dma, virt_to_bus(idev->rx_buff.data));
+ set_dma_count( idev->io.dma, idev->rx_buff.truesize);
+#else
+ setup_dma( idev->io.dma, idev->rx_buff.data, idev->rx_buff.truesize,
+ DMA_MODE_READ);
+#endif
+ /* driver->media_busy = FALSE; */
+ idev->io.direction = IO_RECV;
+ idev->rx_buff.head = idev->rx_buff.data;
+ idev->rx_buff.offset = 0;
+
+ /*
+ * 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
+ */
+ outb( UFR_RXTL|UFR_TXTL|UFR_RXF_RST|UFR_EN_FIFO, iobase+UFR);
+ prev.status = 0;
+
+ /* 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);
+
+ /* Enable DMA */
+ switch_bank(iobase, SET0);
+#ifdef NETWINDER
+ hcr = inb( iobase+HCR);
+ enable_dma( idev->io.dma);
+ outb( hcr | HCR_EN_DMA, iobase+HCR);
+ restore_flags(flags);
+#else
+ outb( inb( iobase+HCR) | HCR_EN_DMA, iobase+HCR);
+#endif
+
+ /* Restore set */
+ outb( set, iobase+SSR);
+
+ DEBUG( 4, __FUNCTION__ "(), done!\n");
+
+ return 0;
+}
+
+/*
+ * Function w83977af_receive_complete (idev)
+ *
+ * Finished with receiving a frame
+ *
+ */
+int w83977af_dma_receive_complete(struct irda_device *idev)
+{
+ struct sk_buff *skb;
+ int len;
+ int iobase;
+ __u8 set;
+ __u8 status;
+
+ DEBUG( 0, __FUNCTION__ "\n");
+
+ iobase = idev->io.iobase;
+
+ /* Save current set */
+ set = inb( iobase+SSR);
+
+ iobase = idev->io.iobase;
+
+ switch_bank(iobase, SET5);
+ if ( prev.status & FS_FO_FSFDR) {
+ status = prev.status;
+ len = prev.len;
+
+ prev.status = 0;
+ } else {
+ status = inb( iobase+FS_FO);
+ len = inb( iobase+RFLFL);
+ len |= inb( iobase+RFLFH) << 8;
+ }
+
+ while ( status & FS_FO_FSFDR) {
+ /* Check for errors */
+ if ( status & FS_FO_ERR_MSK) {
+ if ( status & FS_FO_LST_FR) {
+ /* Add number of lost frames to stats */
+ idev->stats.rx_errors += len;
+ } else {
+ /* Skip frame */
+ idev->stats.rx_errors++;
+
+ idev->rx_buff.offset += len;
+ idev->rx_buff.head += len;
+
+ if ( status & FS_FO_MX_LEX)
+ idev->stats.rx_length_errors++;
+
+ if ( status & FS_FO_PHY_ERR)
+ idev->stats.rx_frame_errors++;
+
+ if ( status & FS_FO_CRC_ERR)
+ idev->stats.rx_crc_errors++;
+ }
+ /* The errors below can be reported in both cases */
+ if ( status & FS_FO_RX_OV)
+ idev->stats.rx_fifo_errors++;
+
+ if ( status & FS_FO_FSF_OV)
+ idev->stats.rx_fifo_errors++;
+
+ } else {
+ /* Check if we have transfered all data to memory */
+ switch_bank(iobase, SET0);
+ if ( inb( iobase+USR) & USR_RDR) {
+ /* Put this entry back in fifo */
+ prev.status = status;
+ prev.len = len;
+
+ /* Restore set register */
+ outb( set, iobase+SSR);
+
+ return FALSE; /* I'll be back! */
+ }
+
+ skb = dev_alloc_skb( len+1);
+ if (skb == NULL) {
+ printk( KERN_INFO __FUNCTION__
+ "(), memory squeeze, dropping frame.\n");
+ /* Restore set register */
+ outb( set, iobase+SSR);
+
+ return FALSE;
+ }
+
+ /* Align to 20 bytes */
+ skb_reserve( skb, 1);
+
+ /* Copy frame without CRC */
+ if ( idev->io.baudrate < 4000000) {
+ skb_put( skb, len-2);
+ memcpy( skb->data, idev->rx_buff.head, len-2);
+ } else {
+ skb_put( skb, len-4);
+ memcpy( skb->data, idev->rx_buff.head, len-4);
+ }
+
+ /* Move to next frame */
+ idev->rx_buff.offset += len;
+ idev->rx_buff.head += len;
+
+ skb->dev = &idev->netdev;
+ skb->mac.raw = skb->data;
+ skb->protocol = htons(ETH_P_IRDA);
+ netif_rx( skb);
+ idev->stats.rx_packets++;
+ }
+ /* Read next entry in ST_FIFO */
+ switch_bank(iobase, SET5);
+ status = inb( iobase+FS_FO);
+ len = inb( iobase+RFLFL);
+ len |= inb( iobase+RFLFH) << 8;
+ }
+ /* Restore set register */
+ outb( set, iobase+SSR);
+
+ return TRUE;
+}
+
+/*
+ * Function pc87108_pio_receive (idev)
+ *
+ * Receive all data in receiver FIFO
+ *
+ */
+static void w83977af_pio_receive( struct irda_device *idev)
+{
+ __u8 byte = 0x00;
+ int iobase;
+
+ DEBUG( 4, __FUNCTION__ "()\n");
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ iobase = idev->io.iobase;
+
+ if ( idev->rx_buff.len == 0) {
+ idev->rx_buff.head = idev->rx_buff.data;
+ }
+
+ /* Receive all characters in Rx FIFO */
+ do {
+ byte = inb( iobase+RBR);
+ async_unwrap_char( idev, byte);
+
+ } while ( inb( iobase+USR) & USR_RDR); /* Data available */
+}
+
+/*
+ * Function w83977af_sir_interrupt (idev, eir)
+ *
+ * Handle SIR interrupt
+ *
+ */
+static __u8 w83977af_sir_interrupt( struct irda_device *idev, int isr)
+{
+ int len;
+ int actual;
+ __u8 new_icr = 0;
+
+ DEBUG( 4, __FUNCTION__ "(), isr=%#x\n", isr);
+
+ /* Transmit FIFO low on data */
+ if ( isr & ISR_TXTH_I) {
+ /* Write data left in transmit buffer */
+ len = idev->tx_buff.len - idev->tx_buff.offset;
+
+ ASSERT( len > 0, return 0;);
+ actual = w83977af_pio_write( idev->io.iobase,
+ idev->tx_buff.head,
+ len, idev->io.fifo_size);
+ idev->tx_buff.offset += actual;
+ idev->tx_buff.head += actual;
+
+ idev->io.direction = IO_XMIT;
+ ASSERT( actual <= len, return 0;);
+ /* Check if finished */
+ if ( actual == len) {
+ DEBUG( 4, __FUNCTION__ "(), finished with frame!\n");
+ idev->netdev.tbusy = 0; /* Unlock */
+ idev->stats.tx_packets++;
+
+ /* Schedule network layer */
+ mark_bh(NET_BH);
+
+ new_icr |= ICR_ETBREI;
+ } else
+ new_icr |= ICR_ETXTHI;
+ }
+ /* Check if transmission has completed */
+ if ( isr & ISR_TXEMP_I) {
+
+ /* Turn around and get ready to receive some data */
+ idev->io.direction = IO_RECV;
+ new_icr |= ICR_ERBRI;
+ }
+
+ /* Rx FIFO threshold or timeout */
+ if ( isr & ISR_RXTH_I) {
+ w83977af_pio_receive( idev);
+
+ /* Keep receiving */
+ new_icr |= ICR_ERBRI;
+ }
+ return new_icr;
+}
+
+/*
+ * Function pc87108_fir_interrupt (idev, eir)
+ *
+ * Handle MIR/FIR interrupt
+ *
+ */
+static __u8 w83977af_fir_interrupt( struct irda_device *idev, int isr)
+{
+ __u8 new_icr = 0;
+ __u8 set;
+ int iobase;
+
+ DEBUG( 4, __FUNCTION__ "(), isr=%#x\n", isr);
+
+ iobase = idev->io.iobase;
+
+ set = inb( iobase+SSR);
+
+ /* End of frame detected in FIFO */
+ if ( isr & (ISR_FEND_I|ISR_FSF_I)) {
+ if ( w83977af_dma_receive_complete( idev)) {
+
+ 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 ( idev->io.direction == IO_XMIT) {
+ w83977af_dma_write( idev, iobase);
+
+ new_icr |= ICR_EDMAI;
+ } else {
+ /* Check if DMA has now finished */
+ w83977af_dma_receive_complete( idev);
+
+ new_icr |= ICR_EFSFI;
+ }
+ }
+ /* Finished with DMA */
+ if ( isr & ISR_DMA_I) {
+ w83977af_dma_xmit_complete( idev);
+
+ /* Check if there are more frames to be transmitted */
+ if ( irda_device_txqueue_empty( idev)) {
+
+ /* Prepare for receive */
+ w83977af_dma_receive( idev);
+ new_icr = ICR_EFSFI;
+ }
+ }
+
+ /* Restore set */
+ outb( set, iobase+SSR);
+
+ return new_icr;
+}
+
+/*
+ * Function pc87108_interrupt (irq, dev_id, regs)
+ *
+ * An interrupt from the chip has arrived. Time to do some work
+ *
+ */
+static void w83977af_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ __u8 set, icr, isr;
+ int iobase;
+
+ struct irda_device *idev = (struct irda_device *) dev_id;
+
+ if ( idev == NULL) {
+ printk( KERN_WARNING "%s: irq %d for unknown device.\n",
+ driver_name, irq);
+ return;
+ }
+
+ idev->netdev.interrupt = 1;
+
+ iobase = idev->io.iobase;
+
+ /* 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 ( idev->io.baudrate > 115200)
+ icr = w83977af_fir_interrupt( idev, isr);
+ else
+ icr = w83977af_sir_interrupt( idev, isr);
+ }
+
+ outb( icr, iobase+ICR); /* Restore (new) interrupts */
+ outb( set, iobase+SSR); /* Restore bank register */
+
+ idev->netdev.interrupt = 0;
+}
+
+/*
+ * Function w83977af_wait_until_sent (idev)
+ *
+ * This function should put the current thread to sleep until all data
+ * have been sent, so it is safe to f.eks. change the speed.
+ */
+static void w83977af_wait_until_sent( struct irda_device *idev)
+{
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(6);
+}
+
+/*
+ * Function w83977af_is_receiving (idev)
+ *
+ * Return TRUE is we are currently receiving a frame
+ *
+ */
+static int w83977af_is_receiving( struct irda_device *idev)
+{
+ int status = FALSE;
+ int iobase;
+ __u8 set;
+
+ ASSERT( idev != NULL, return FALSE;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return FALSE;);
+
+ if ( idev->io.baudrate > 115200) {
+ iobase = idev->io.iobase;
+
+ /* 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 = ( idev->rx_buff.state != OUTSIDE_FRAME);
+
+ return status;
+}
+
+/*
+ * Function w83977af_net_init (dev)
+ *
+ *
+ *
+ */
+static int w83977af_net_init( struct device *dev)
+{
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ /* Set up to be a normal IrDA network device driver */
+ irda_device_setup( dev);
+
+ /* Insert overrides below this line! */
+
+ return 0;
+}
+
+
+/*
+ * Function w83977af_net_open (dev)
+ *
+ * Start the device
+ *
+ */
+static int w83977af_net_open( struct device *dev)
+{
+ struct irda_device *idev;
+ int iobase;
+ __u8 set;
+
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ ASSERT( dev != NULL, return -1;);
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT( idev != NULL, return 0;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+ iobase = idev->io.iobase;
+
+ if (request_irq( idev->io.irq, w83977af_interrupt, 0, idev->name,
+ (void *) idev)) {
+ return -EAGAIN;
+ }
+ /*
+ * Always allocate the DMA channel after the IRQ,
+ * and clean up on failure.
+ */
+ if (request_dma(idev->io.dma, idev->name)) {
+ free_irq( idev->io.irq, idev);
+ return -EAGAIN;
+ }
+
+ /* Ready to play! */
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ /* Save current set */
+ set = inb( iobase+SSR);
+
+ /* Enable some interrupts so we can receive frames again */
+ switch_bank(iobase, SET0);
+ if ( idev->io.baudrate > 115200) {
+ outb( ICR_EFSFI, iobase+ICR);
+ w83977af_dma_receive( idev);
+ } else
+ outb( ICR_ERBRI, iobase+ICR);
+
+ /* Restore bank register */
+ outb( set, iobase+SSR);
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+/*
+ * Function w83977af_net_close (dev)
+ *
+ * Stop the device
+ *
+ */
+static int w83977af_net_close(struct device *dev)
+{
+ struct irda_device *idev;
+ int iobase;
+ __u8 set;
+
+ DEBUG( 0, __FUNCTION__ "()\n");
+
+ /* Stop device */
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ ASSERT( dev != NULL, return -1;);
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT( idev != NULL, return 0;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+ iobase = idev->io.iobase;
+
+ disable_dma( idev->io.dma);
+
+ /* Save current set */
+ set = inb( iobase+SSR);
+
+ /* Disable interrupts */
+ switch_bank( iobase, SET0);
+ outb( 0, iobase+ICR);
+
+ free_irq( idev->io.irq, idev);
+ free_dma( idev->io.dma);
+
+ /* Restore bank register */
+ outb( set, iobase+SSR);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+#ifdef MODULE
+
+/*
+ * Function init_module (void)
+ *
+ *
+ *
+ */
+int init_module(void)
+{
+ w83977af_init();
+
+ return(0);
+}
+
+/*
+ * Function cleanup_module (void)
+ *
+ *
+ *
+ */
+void cleanup_module(void)
+{
+ w83977af_cleanup();
+}
+
+#endif