patch-2.2.15 linux/drivers/net/irda/irport.c

Next file: linux/drivers/net/irda/irtty.c
Previous file: linux/drivers/net/irda/girbil.c
Back to the patch index
Back to the overall index

diff -u --new-file --recursive --exclude-from ../../exclude v2.2.14/drivers/net/irda/irport.c linux/drivers/net/irda/irport.c
@@ -6,11 +6,11 @@
  * Status:	  Experimental.
  * Author:	  Dag Brattli <dagb@cs.uit.no>
  * Created at:	  Sun Aug  3 13:49:59 1997
- * Modified at:   Tue Jun  1 10:02:42 1999
+ * Modified at:   Sat Mar 11 07:41:54 2000
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:	  serial.c by Linus Torvalds 
  * 
- *     Copyright (c) 1997, 1998, 1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1997, 1998, 1999-2000 Dag Brattli, All Rights Reserved.
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -47,11 +47,12 @@
 #include <linux/serial_reg.h>
 #include <linux/errno.h>
 #include <linux/init.h>
+#include <asm/spinlock.h>
+#include <linux/rtnetlink.h>
 
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
-#include <asm/spinlock.h>
 
 #include <net/irda/irda.h>
 #include <net/irda/irmod.h>
@@ -69,24 +70,33 @@
 
 static unsigned int qos_mtt_bits = 0x03;
 
-static struct irda_device *dev_self[] = { NULL, NULL, NULL, NULL};
+static struct irport_cb *dev_self[] = { NULL, NULL, NULL, NULL};
 static char *driver_name = "irport";
 
-static int irport_open(int i, unsigned int iobase, unsigned int irq);
-static int irport_close(struct irda_device *idev);
-
-static void irport_write_wakeup(struct irda_device *idev);
+static void irport_write_wakeup(struct irport_cb *self);
 static int  irport_write(int iobase, int fifo_size, __u8 *buf, int len);
-static void irport_receive(struct irda_device *idev);
+static void irport_receive(struct irport_cb *self);
 
 static int  irport_net_init(struct device *dev);
-static int  irport_net_open(struct device *dev);
-static int  irport_net_close(struct device *dev);
-static int  irport_is_receiving(struct irda_device *idev);
-static void irport_set_dtr_rts(struct irda_device *idev, int dtr, int rts);
-static int  irport_raw_write(struct irda_device *idev, __u8 *buf, int len);
+static int  irport_net_ioctl(struct device *dev, struct ifreq *rq, 
+			     int cmd);
+static int  irport_is_receiving(struct irport_cb *self);
+static int  irport_set_dtr_rts(struct device *dev, int dtr, int rts);
+static int  irport_raw_write(struct device *dev, __u8 *buf, int len);
+static struct net_device_stats *irport_net_get_stats(struct device *dev);
+static int irport_change_speed_complete(struct irda_task *task);
+
+EXPORT_SYMBOL(irport_open);
+EXPORT_SYMBOL(irport_close);
+EXPORT_SYMBOL(irport_start);
+EXPORT_SYMBOL(irport_stop);
+EXPORT_SYMBOL(irport_interrupt);
+EXPORT_SYMBOL(irport_hard_xmit);
+EXPORT_SYMBOL(irport_change_speed);
+EXPORT_SYMBOL(irport_net_open);
+EXPORT_SYMBOL(irport_net_close);
 
-__initfunc(int irport_init(void))
+int __init irport_init(void)
 {
  	int i;
 
@@ -94,7 +104,7 @@
  		int ioaddr = io[i];
  		if (check_region(ioaddr, IO_EXTENT))
  			continue;
- 		if (irport_open(i, io[i], irq[i]) == 0)
+ 		if (irport_open(i, io[i], irq[i]) != NULL)
  			return 0;
  	}
 	/* 
@@ -114,7 +124,7 @@
 {
  	int i;
 
-        DEBUG( 4, __FUNCTION__ "()\n");
+        IRDA_DEBUG( 4, __FUNCTION__ "()\n");
 
 	for (i=0; i < 4; i++) {
  		if (dev_self[i])
@@ -123,110 +133,170 @@
 }
 #endif /* MODULE */
 
-static int irport_open(int i, unsigned int iobase, unsigned int irq)
+struct irport_cb *
+irport_open(int i, unsigned int iobase, unsigned int irq)
 {
-	struct irda_device *idev;
+	struct device *dev;
+	struct irport_cb *self;
 	int ret;
+	int err;
 
-	DEBUG( 0, __FUNCTION__ "()\n");
-
-/* 	if (irport_probe(iobase, irq) == -1) */
-/* 		return -1; */
+	IRDA_DEBUG(0, __FUNCTION__ "()\n");
 
 	/*
 	 *  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;
+	self = kmalloc(sizeof(struct irport_cb), GFP_KERNEL);
+	if (!self) {
+		ERROR(__FUNCTION__ "(), can't allocate memory for "
+		      "control block!\n");
+		return NULL;
 	}
-	memset(idev, 0, sizeof(struct irda_device));
-   
+	memset(self, 0, sizeof(struct irport_cb));
+	spin_lock_init(&self->lock);
+
 	/* Need to store self somewhere */
-	dev_self[i] = idev;
+	dev_self[i] = self;
+	self->priv = self;
+	self->index = i;
 
 	/* Initialize IO */
-	idev->io.iobase2   = iobase;
-        idev->io.irq2      = irq;
-        idev->io.io_ext    = IO_EXTENT;
-        idev->io.fifo_size = 16;
-
-	idev->netdev.base_addr = iobase;
-	idev->netdev.irq = irq;
+	self->io.sir_base  = iobase;
+        self->io.sir_ext   = IO_EXTENT;
+        self->io.irq       = irq;
+        self->io.fifo_size = 16;
 
 	/* Lock the port that we need */
-	ret = check_region(idev->io.iobase2, idev->io.io_ext);
+	ret = check_region(self->io.sir_base, self->io.sir_ext);
 	if (ret < 0) { 
-		DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
-		      idev->io.iobase2);
-		/* irport_cleanup(self->idev);  */
-		return -ENODEV;
+		IRDA_DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
+			   self->io.sir_base);
+		return NULL;
 	}
-	request_region(idev->io.iobase2, idev->io.io_ext, idev->name);
+	request_region(self->io.sir_base, self->io.sir_ext, driver_name);
 
 	/* Initialize QoS for this device */
-	irda_init_max_qos_capabilies(&idev->qos);
+	irda_init_max_qos_capabilies(&self->qos);
 	
-	idev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+	self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
 		IR_115200;
 
-	idev->qos.min_turn_time.bits = qos_mtt_bits;
-	irda_qos_bits_to_value(&idev->qos);
+	self->qos.min_turn_time.bits = qos_mtt_bits;
+	irda_qos_bits_to_value(&self->qos);
 	
-	idev->flags = IFF_SIR|IFF_PIO;
+	self->flags = IFF_SIR|IFF_PIO;
 
-	/* Specify which buffer allocation policy we need */
-	idev->rx_buff.flags = GFP_KERNEL;
-	idev->tx_buff.flags = GFP_KERNEL;
-
-	idev->rx_buff.truesize = 4000; 
-	idev->tx_buff.truesize = 4000;
-	
-	/* Initialize callbacks */
-	idev->change_speed    = irport_change_speed;
-	idev->wait_until_sent = irport_wait_until_sent;
-        idev->is_receiving    = irport_is_receiving;
-	idev->set_dtr_rts     = irport_set_dtr_rts;
-	idev->raw_write       = irport_raw_write;
+	/* Specify how much memory we want */
+	self->rx_buff.truesize = 4000; 
+	self->tx_buff.truesize = 4000;
+	
+	/* Allocate memory if needed */
+	if (self->rx_buff.truesize > 0) {
+		self->rx_buff.head = (__u8 *) kmalloc(self->rx_buff.truesize,
+						      GFP_KERNEL);
+		if (self->rx_buff.head == NULL)
+			return NULL;
+		memset(self->rx_buff.head, 0, self->rx_buff.truesize);
+	}
+	if (self->tx_buff.truesize > 0) {
+		self->tx_buff.head = (__u8 *) kmalloc(self->tx_buff.truesize, 
+						      GFP_KERNEL);
+		if (self->tx_buff.head == NULL) {
+			kfree(self->rx_buff.head);
+			return NULL;
+		}
+		memset(self->tx_buff.head, 0, self->tx_buff.truesize);
+	}	
+	self->rx_buff.in_frame = FALSE;
+	self->rx_buff.state = OUTSIDE_FRAME;
+	self->tx_buff.data = self->tx_buff.head;
+	self->rx_buff.data = self->rx_buff.head;
+	self->mode = IRDA_IRLAP;
+
+	if (!(dev = dev_alloc("irda%d", &err))) {
+		ERROR(__FUNCTION__ "(), dev_alloc() failed!\n");
+		return NULL;
+	}
+	/* dev_alloc doesn't clear the struct */
+	memset(((__u8*)dev)+sizeof(char*),0,sizeof(struct device)-sizeof(char*));
+
+	self->netdev = dev;
+
+	/* May be overridden by piggyback drivers */
+ 	dev->priv = (void *) self;
+	self->interrupt    = irport_interrupt;
+	self->change_speed = irport_change_speed;
 
 	/* Override the network functions we need to use */
-	idev->netdev.init            = irport_net_init;
-	idev->netdev.hard_start_xmit = irport_hard_xmit;
-	idev->netdev.open            = irport_net_open;
-	idev->netdev.stop            = irport_net_close;
-
-	/* Open the IrDA device */
-	irda_device_open(idev, driver_name, NULL);
-	
-	return 0;
-}
-
-static int irport_close(struct irda_device *idev)
-{
-	ASSERT(idev != NULL, return -1;);
-	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+	dev->init            = irport_net_init;
+	dev->hard_start_xmit = irport_hard_xmit;
+	dev->open            = irport_net_open;
+	dev->stop            = irport_net_close;
+	dev->get_stats	     = irport_net_get_stats;
+	dev->do_ioctl        = irport_net_ioctl;
+
+	/* Make ifconfig display some details */
+	dev->base_addr = iobase;
+	dev->irq = irq;
+
+	rtnl_lock();
+	err = register_netdevice(dev);
+	rtnl_unlock();
+	if (err) {
+		ERROR(__FUNCTION__ "(), register_netdev() failed!\n");
+		return NULL;
+	}
+	MESSAGE("IrDA: Registered device %s\n", dev->name);
+
+	return self;
+}
+
+int irport_close(struct irport_cb *self)
+{
+	ASSERT(self != NULL, return -1;);
+
+	/* We are not using any dongle anymore! */
+	if (self->dongle)
+		irda_device_dongle_cleanup(self->dongle);
+	self->dongle = NULL;
+	
+	/* Remove netdevice */
+	if (self->netdev) {
+		rtnl_lock();
+		unregister_netdevice(self->netdev);
+		rtnl_unlock();
+		/* Must free the old-style 2.2.x device */
+		kfree(self->netdev);
+	}
 
 	/* Release the IO-port that this driver is using */
-	DEBUG(0 , __FUNCTION__ "(), Releasing Region %03x\n", 
-	      idev->io.iobase2);
-	release_region(idev->io.iobase2, idev->io.io_ext);
-
-	irda_device_close(idev);
-
-	kfree(idev);
-
+	IRDA_DEBUG(0 , __FUNCTION__ "(), Releasing Region %03x\n", 
+		   self->io.sir_base);
+	release_region(self->io.sir_base, self->io.sir_ext);
+
+	if (self->tx_buff.head)
+		kfree(self->tx_buff.head);
+	
+	if (self->rx_buff.head)
+		kfree(self->rx_buff.head);
+	
+	/* Remove ourselves */
+	dev_self[self->index] = NULL;
+	kfree(self);
+	
 	return 0;
 }
 
-void irport_start(struct irda_device *idev, int iobase)
+void irport_start(struct irport_cb *self)
 {
 	unsigned long flags;
+	int iobase;
+
+	iobase = self->io.sir_base;
 
-	spin_lock_irqsave(&idev->lock, flags);
+	spin_lock_irqsave(&self->lock, flags);
 
-	irport_stop(idev, iobase);
+	irport_stop(self);
 
 	/* Initialize UART */
 	outb(UART_LCR_WLEN8, iobase+UART_LCR);  /* Reset DLAB */
@@ -235,14 +305,17 @@
 	/* Turn on interrups */
 	outb(UART_IER_RLSI | UART_IER_RDI |UART_IER_THRI, iobase+UART_IER);
 
-	spin_unlock_irqrestore(&idev->lock, flags);
+	spin_unlock_irqrestore(&self->lock, flags);
 }
 
-void irport_stop(struct irda_device *idev, int iobase)
+void irport_stop(struct irport_cb *self)
 {
 	unsigned long flags;
+	int iobase;
+
+	iobase = self->io.sir_base;
 
-	spin_lock_irqsave(&idev->lock, flags);
+	spin_lock_irqsave(&self->lock, flags);
 
 	/* Reset UART */
 	outb(0, iobase+UART_MCR);
@@ -250,7 +323,7 @@
 	/* Turn off interrupts */
 	outb(0, iobase+UART_IER);
 
-	spin_unlock_irqrestore(&idev->lock, flags);
+	spin_unlock_irqrestore(&self->lock, flags);
 }
 
 /*
@@ -261,36 +334,36 @@
  */
 int irport_probe(int iobase)
 {
-	DEBUG(4, __FUNCTION__ "(), iobase=%#x\n", iobase);
+	IRDA_DEBUG(4, __FUNCTION__ "(), iobase=%#x\n", iobase);
 
 	return 0;
 }
 
 /*
- * Function irport_change_speed (idev, speed)
+ * Function irport_change_speed (self, speed)
  *
  *    Set speed of IrDA port to specified baudrate
  *
  */
-void irport_change_speed(struct irda_device *idev, int speed)
+void irport_change_speed(void *priv, __u32 speed)
 {
+	struct irport_cb *self = (struct irport_cb *) priv;
 	unsigned long flags;
 	int iobase; 
 	int fcr;    /* FIFO control reg */
 	int lcr;    /* Line control reg */
 	int divisor;
 
-	DEBUG(0, __FUNCTION__ "(), Setting speed to: %d\n", speed);
+	IRDA_DEBUG(2, __FUNCTION__ "(), Setting speed to: %d\n", speed);
 
-	ASSERT(idev != NULL, return;);
-	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
+	ASSERT(self != NULL, return;);
 
-	iobase = idev->io.iobase2;
+	iobase = self->io.sir_base;
 	
 	/* Update accounting for new speed */
-	idev->io.baudrate = speed;
+	self->io.speed = speed;
 
-	spin_lock_irqsave(&idev->lock, flags);
+	spin_lock_irqsave(&self->lock, flags);
 
 	/* Turn off interrupts */
 	outb(0, iobase+UART_IER); 
@@ -304,7 +377,7 @@
 	 * almost 1,7 ms at 19200 bps. At speeds above that we can just forget
 	 * about this timeout since it will always be fast enough. 
 	 */
-	if (idev->io.baudrate < 38400)
+	if (self->io.speed < 38400)
 		fcr |= UART_FCR_TRIGGER_1;
 	else 
 		fcr |= UART_FCR_TRIGGER_14;
@@ -319,9 +392,82 @@
 	outb(fcr,		  iobase+UART_FCR); /* Enable FIFO's */
 
 	/* Turn on interrups */
-	outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, iobase+UART_IER);
+	outb(/*UART_IER_RLSI|*/UART_IER_RDI/*|UART_IER_THRI*/, iobase+UART_IER);
 
-	spin_unlock_irqrestore(&idev->lock, flags);
+	spin_unlock_irqrestore(&self->lock, flags);
+}
+
+/*
+ * Function __irport_change_speed (instance, state, param)
+ *
+ *    State machine for changing speed of the device. We do it this way since
+ *    we cannot use schedule_timeout() when we are in interrupt context
+ */
+int __irport_change_speed(struct irda_task *task)
+{
+	struct irport_cb *self;
+	__u32 speed = (__u32) task->param;
+	int ret = 0;
+
+	IRDA_DEBUG(2, __FUNCTION__ "(), <%ld>\n", jiffies); 
+
+	self = (struct irport_cb *) task->instance;
+
+	ASSERT(self != NULL, return -1;);
+
+	switch (task->state) {
+	case IRDA_TASK_INIT:
+	case IRDA_TASK_WAIT:
+		/* Are we ready to change speed yet? */
+		if (self->tx_buff.len > 0) {
+			task->state = IRDA_TASK_WAIT;
+
+			/* Try again later */
+			ret = MSECS_TO_JIFFIES(20);
+			break;
+		}
+
+		if (self->dongle)
+			irda_task_next_state(task, IRDA_TASK_CHILD_INIT);
+		else
+			irda_task_next_state(task, IRDA_TASK_CHILD_DONE);
+		break;
+	case IRDA_TASK_CHILD_INIT:
+		/* Go to default speed */
+		self->change_speed(self->priv, 9600);
+
+		/* Change speed of dongle */
+		if (irda_task_execute(self->dongle,
+				      self->dongle->issue->change_speed, 
+				      NULL, task, (void *) speed))
+		{
+			/* Dongle need more time to change its speed */
+			irda_task_next_state(task, IRDA_TASK_CHILD_WAIT);
+
+			/* Give dongle 1 sec to finish */
+			ret = MSECS_TO_JIFFIES(1000);
+		} else
+			/* Child finished immediately */
+			irda_task_next_state(task, IRDA_TASK_CHILD_DONE);
+		break;
+	case IRDA_TASK_CHILD_WAIT:
+		WARNING(__FUNCTION__ 
+			"(), changing speed of dongle timed out!\n");
+		ret = -1;		
+		break;
+	case IRDA_TASK_CHILD_DONE:
+		/* Finally we are ready to change the speed */
+		self->change_speed(self->priv, speed);
+		
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		break;
+	default:
+		ERROR(__FUNCTION__ "(), unknown state %d\n", task->state);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		ret = -1;
+		break;
+	}	
+	return ret;
 }
 
 /*
@@ -331,51 +477,63 @@
  *    more packets to send, we send them here.
  *
  */
-static void irport_write_wakeup(struct irda_device *idev)
+static void irport_write_wakeup(struct irport_cb *self)
 {
 	int actual = 0;
 	int iobase;
 	int fcr;
 
-	ASSERT(idev != NULL, return;);
-	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
+	ASSERT(self != NULL, return;);
 
-	DEBUG(4, __FUNCTION__ "()\n");
+	IRDA_DEBUG(4, __FUNCTION__ "()\n");
+
+	iobase = self->io.sir_base;
 
 	/* Finished with frame?  */
-	if (idev->tx_buff.len > 0)  {
+	if (self->tx_buff.len > 0)  {
 		/* Write data left in transmit buffer */
-		actual = irport_write(idev->io.iobase2, idev->io.fifo_size, 
-				      idev->tx_buff.data, idev->tx_buff.len);
-		idev->tx_buff.data += actual;
-		idev->tx_buff.len  -= actual;
+		actual = irport_write(iobase, self->io.fifo_size, 
+				      self->tx_buff.data, self->tx_buff.len);
+		self->tx_buff.data += actual;
+		self->tx_buff.len  -= actual;
 	} else {
-		iobase = idev->io.iobase2;
+		
 		/* 
 		 *  Now serial buffer is almost free & we can start 
-		 *  transmission of another packet 
+		 *  transmission of another packet. But first we must check
+		 *  if we need to change the speed of the hardware
 		 */
-		idev->netdev.tbusy = 0; /* Unlock */
-		idev->stats.tx_packets++;
+		if (self->new_speed) {
+			IRDA_DEBUG(5, __FUNCTION__ "(), Changing speed!\n");
+			irda_task_execute(self, __irport_change_speed, 
+					  irport_change_speed_complete, 
+					  NULL, (void *) self->new_speed);
+			self->new_speed = 0;
+		} else {
+			self->netdev->tbusy = 0; /* Unlock */
+		
+			/* Tell network layer that we want more frames */
+			mark_bh(NET_BH);
+		}
+		self->stats.tx_packets++;
 
 		/* Schedule network layer, so we can get some more frames */
 		mark_bh(NET_BH);
 
+		/* 
+		 * Reset Rx FIFO to make sure that all reflected transmit data
+		 * is discarded. This is needed for half duplex operation
+		 */
 		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR;
-
-		if (idev->io.baudrate < 38400)
+		if (self->io.speed < 38400)
 			fcr |= UART_FCR_TRIGGER_1;
 		else 
 			fcr |= UART_FCR_TRIGGER_14;
 
-		/* 
-		 * Reset Rx FIFO to make sure that all reflected transmit data
-		 * will be discarded
-		 */
 		outb(fcr, iobase+UART_FCR);
 
 		/* Turn on receive interrupts */
-		outb(UART_IER_RLSI|UART_IER_RDI, iobase+UART_IER); 
+		outb(UART_IER_RDI, iobase+UART_IER);
 	}
 }
 
@@ -391,8 +549,8 @@
 
 	/* Tx FIFO should be empty! */
 	if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) {
-		DEBUG(0, __FUNCTION__ "(), failed, fifo not empty!\n");
-		return -1;
+		IRDA_DEBUG(0, __FUNCTION__ "(), failed, fifo not empty!\n");
+		return 0;
 	}
         
 	/* Fill FIFO with current frame */
@@ -407,6 +565,32 @@
 }
 
 /*
+ * Function irport_change_speed_complete (task)
+ *
+ *    Called when the change speed operation completes
+ *
+ */
+static int irport_change_speed_complete(struct irda_task *task)
+{
+	struct irport_cb *self;
+
+	IRDA_DEBUG(0, __FUNCTION__ "()\n");
+
+	self = (struct irport_cb *) task->instance;
+
+	ASSERT(self != NULL, return -1;);
+	ASSERT(self->netdev != NULL, return -1;);
+
+	/* Finished changing speed, so we are not busy any longer */
+	self->netdev->tbusy = 0;
+
+	/* Signal network layer so it can try to send the frame */
+	mark_bh(NET_BH);
+
+	return 0;
+}
+
+/*
  * Function irport_xmit (void)
  *
  *    Transmits the current frame until FIFO is full, then
@@ -415,49 +599,50 @@
  */
 int irport_hard_xmit(struct sk_buff *skb, struct device *dev)
 {
-	struct irda_device *idev;
+	struct irport_cb *self;
 	unsigned long flags;
-	int actual = 0;
 	int iobase;
+	__u32 speed;
 
 	ASSERT(dev != NULL, return 0;);
 	
-	idev = (struct irda_device *) dev->priv;
-
-	ASSERT(idev != NULL, return 0;);
-	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+	self = (struct irport_cb *) dev->priv;
+	ASSERT(self != NULL, return 0;);
 
-	iobase = idev->io.iobase2;
+	iobase = self->io.sir_base;
 
 	/* Lock transmit buffer */
 	if (irda_lock((void *) &dev->tbusy) == FALSE) {
 		int tickssofar = jiffies - dev->trans_start;
-		if (tickssofar < 5)
+		if ((tickssofar < 5) || !dev->start)
 			return -EBUSY;
 
 		WARNING("%s: transmit timed out\n", dev->name);
-		irport_start(idev, iobase);
-		irport_change_speed(idev, idev->io.baudrate);
+		irport_start(self);
+		self->change_speed(self->priv, self->io.speed);
 
 		dev->trans_start = jiffies;
 	}
 
-	spin_lock_irqsave(&idev->lock, flags);
-	
+	/* Check if we need to change the speed */
+	if ((speed = irda_get_speed(skb)) != self->io.speed)
+		self->new_speed = speed;
+
+	spin_lock_irqsave(&self->lock, flags);
+
 	/* Init tx buffer */
-	idev->tx_buff.data = idev->tx_buff.head;
+	self->tx_buff.data = self->tx_buff.head;
 
         /* Copy 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->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, 
+					   self->tx_buff.truesize);
 	
-	idev->tx_buff.data += actual;
-	idev->tx_buff.len  -= actual;
+	self->stats.tx_bytes += self->tx_buff.len;
 
 	/* Turn on transmit finished interrupt. Will fire immediately!  */
 	outb(UART_IER_THRI, iobase+UART_IER); 
 
-	spin_unlock_irqrestore(&idev->lock, flags);
+	spin_unlock_irqrestore(&self->lock, flags);
 
 	dev_kfree_skb(skb);
 	
@@ -465,30 +650,31 @@
 }
         
 /*
- * Function irport_receive (void)
+ * Function irport_receive (self)
  *
  *    Receive one frame from the infrared port
  *
  */
-static void irport_receive(struct irda_device *idev) 
+static void irport_receive(struct irport_cb *self) 
 {
-	int iobase;
 	int boguscount = 0;
+	int iobase;
 
-	ASSERT(idev != NULL, return;);
+	ASSERT(self != NULL, return;);
 
-	iobase = idev->io.iobase2;
+	iobase = self->io.sir_base;
 
 	/*  
 	 * Receive all characters in Rx FIFO, unwrap and unstuff them. 
          * async_unwrap_char will deliver all found frames  
 	 */
 	do {
-		async_unwrap_char(idev, inb(iobase+UART_RX));
+		async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, 
+				  inb(iobase+UART_RX));
 
 		/* Make sure we don't stay here to long */
 		if (boguscount++ > 32) {
-			DEBUG(0,__FUNCTION__ "(), breaking!\n");
+			IRDA_DEBUG(2,__FUNCTION__ "(), breaking!\n");
 			break;
 		}
 	} while (inb(iobase+UART_LSR) & UART_LSR_DR);	
@@ -501,58 +687,60 @@
  */
 void irport_interrupt(int irq, void *dev_id, struct pt_regs *regs) 
 {
-	struct irda_device *idev = (struct irda_device *) dev_id;
+	struct device *dev = (struct device *) dev_id;
+	struct irport_cb *self;
+	int boguscount = 0;
 	int iobase;
 	int iir, lsr;
-	int boguscount = 0;
 
-	if (!idev) {
+	if (!dev) {
 		WARNING(__FUNCTION__ "() irq %d for unknown device.\n", irq);
 		return;
 	}
+	self = (struct irport_cb *) dev->priv;
 
-	spin_lock(&idev->lock);
+	spin_lock(&self->lock);
 
-	idev->netdev.interrupt = 1;
+	dev->interrupt = 1;
 
-	iobase = idev->io.iobase2;
+	iobase = self->io.sir_base;
 
 	iir = inb(iobase+UART_IIR) & UART_IIR_ID;
 	while (iir) {
 		/* Clear interrupt */
 		lsr = inb(iobase+UART_LSR);
 
-		DEBUG(4, __FUNCTION__ "(), iir=%02x, lsr=%02x, iobase=%#x\n", 
-		      iir, lsr, iobase);
+		IRDA_DEBUG(4, __FUNCTION__ 
+			   "(), iir=%02x, lsr=%02x, iobase=%#x\n", 
+			   iir, lsr, iobase);
 
 		switch (iir) {
 		case UART_IIR_RLSI:
-			DEBUG(0, __FUNCTION__ "(), RLSI\n");
+			IRDA_DEBUG(2, __FUNCTION__ "(), RLSI\n");
 			break;
 		case UART_IIR_RDI:
-			if (lsr & UART_LSR_DR)
-				/* Receive interrupt */
-				irport_receive(idev);
+			/* Receive interrupt */
+			irport_receive(self);
 			break;
 		case UART_IIR_THRI:
 			if (lsr & UART_LSR_THRE)
 				/* Transmitter ready for data */
-				irport_write_wakeup(idev);
+				irport_write_wakeup(self);
 			break;
 		default:
-			DEBUG(0, __FUNCTION__ "(), unhandled IIR=%#x\n", iir);
+			IRDA_DEBUG(0, __FUNCTION__ "(), unhandled IIR=%#x\n", iir);
 			break;
 		} 
 		
 		/* Make sure we don't stay here to long */
-		if (boguscount++ > 32)
+		if (boguscount++ > 100)
 			break;
 
  	        iir = inb(iobase + UART_IIR) & UART_IIR_ID;
 	}
-	idev->netdev.interrupt = 0;
+	dev->interrupt = 0;
 
-	spin_unlock(&idev->lock);
+	spin_unlock(&self->lock);
 }
 
 static int irport_net_init(struct device *dev)
@@ -568,64 +756,75 @@
 /*
  * Function irport_net_open (dev)
  *
- *    
- *
+ *    Network device is taken up. Usually this is done by "ifconfig irda0 up" 
+ *   
  */
-static int irport_net_open(struct device *dev)
+int irport_net_open(struct device *dev)
 {
-	struct irda_device *idev;
+	struct irport_cb *self;
 	int iobase;
 
 	ASSERT(dev != NULL, return -1;);
-	idev = (struct irda_device *) dev->priv;
+	self = (struct irport_cb *) dev->priv;
 
-	iobase = idev->io.iobase2;
+	iobase = self->io.sir_base;
 
-	if (request_irq(idev->io.irq2, irport_interrupt, 0, idev->name, 
-			(void *) idev))
+	if (request_irq(self->io.irq, self->interrupt, 0, dev->name, 
+			(void *) dev))
 		return -EAGAIN;
 
-	irport_start(idev, iobase);
-
-	MOD_INC_USE_COUNT;
+	irport_start(self);
 
 	/* Ready to play! */
 	dev->tbusy = 0;
 	dev->interrupt = 0;
 	dev->start = 1;
 
-	/* Change speed to make sure dongles follow us again */
-	if (idev->change_speed)
-		idev->change_speed(idev, 9600);
+	/* 
+	 * Open new IrLAP layer instance, now that everything should be
+	 * initialized properly 
+	 */
+	self->irlap = irlap_open(dev, &self->qos);
+
+	/* FIXME: change speed of dongle */
+
+	MOD_INC_USE_COUNT;
 
 	return 0;
 }
 
 /*
- * Function irport_net_close (idev)
- *
- *    
+ * Function irport_net_close (self)
  *
+ *    Network device is taken down. Usually this is done by 
+ *    "ifconfig irda0 down" 
  */
-static int irport_net_close(struct device *dev)
+int irport_net_close(struct device *dev)
 {
-	struct irda_device *idev;
+	struct irport_cb *self;
 	int iobase;
 
+	IRDA_DEBUG(4, __FUNCTION__ "()\n");
+
 	ASSERT(dev != NULL, return -1;);
-	idev = (struct irda_device *) dev->priv;
+	self = (struct irport_cb *) dev->priv;
 
-	DEBUG(4, __FUNCTION__ "()\n");
+	ASSERT(self != NULL, return -1;);
 
-	iobase = idev->io.iobase2;
+	iobase = self->io.sir_base;
 
 	/* Stop device */
 	dev->tbusy = 1;
 	dev->start = 0;
 
-	irport_stop(idev, iobase);
+	/* Stop and remove instance of IrLAP */
+	if (self->irlap)
+		irlap_close(self->irlap);
+	self->irlap = NULL;
+
+	irport_stop(self);
 
-	free_irq(idev->io.irq2, idev);
+	free_irq(self->io.irq, dev);
 
 	MOD_DEC_USE_COUNT;
 
@@ -633,50 +832,52 @@
 }
 
 /*
- * Function irport_wait_until_sent (idev)
+ * Function irport_wait_until_sent (self)
  *
  *    Delay exectution until finished transmitting
  *
  */
-void irport_wait_until_sent(struct irda_device *idev)
+#if 0
+void irport_wait_until_sent(struct irport_cb *self)
 {
 	int iobase;
 
-	iobase = idev->io.iobase2;
+	iobase = self->io.sir_base;
 
 	/* Wait until Tx FIFO is empty */
 	while (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) {
-		DEBUG(2, __FUNCTION__ "(), waiting!\n");
+		IRDA_DEBUG(2, __FUNCTION__ "(), waiting!\n");
 		current->state = TASK_INTERRUPTIBLE;
 		schedule_timeout(MSECS_TO_JIFFIES(60));
 	}
 }
+#endif
 
 /*
- * Function irport_is_receiving (idev)
+ * Function irport_is_receiving (self)
  *
  *    Returns true is we are currently receiving data
  *
  */
-static int irport_is_receiving(struct irda_device *idev)
+static int irport_is_receiving(struct irport_cb *self)
 {
-	return (idev->rx_buff.state != OUTSIDE_FRAME);
+	return (self->rx_buff.state != OUTSIDE_FRAME);
 }
 
 /*
- * Function irtty_set_dtr_rts (tty, dtr, rts)
+ * Function irport_set_dtr_rts (tty, dtr, rts)
  *
  *    This function can be used by dongles etc. to set or reset the status
  *    of the dtr and rts lines
  */
-static void irport_set_dtr_rts(struct irda_device *idev, int dtr, int rts)
+static int irport_set_dtr_rts(struct device *dev, int dtr, int rts)
 {
+	struct irport_cb *self = dev->priv;
 	int iobase;
 
-	ASSERT(idev != NULL, return;);
-	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
+	ASSERT(self != NULL, return -1;);
 
-	iobase = idev->io.iobase2;
+	iobase = self->io.sir_base;
 
 	if (dtr)
 		dtr = UART_MCR_DTR;
@@ -684,21 +885,23 @@
 		rts = UART_MCR_RTS;
 
 	outb(dtr|rts|UART_MCR_OUT2, iobase+UART_MCR);
+
+	return 0;
 }
 
-static int irport_raw_write(struct irda_device *idev, __u8 *buf, int len)
+static int irport_raw_write(struct device *dev, __u8 *buf, int len)
 {
-	int iobase;
+	struct irport_cb *self = (struct irport_cb *) dev->priv;
 	int actual = 0;
+	int iobase;
 
-	ASSERT(idev != NULL, return -1;);
-	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+	ASSERT(self != NULL, return -1;);
 
-	iobase = idev->io.iobase2;
+	iobase = self->io.sir_base;
 
 	/* Tx FIFO should be empty! */
 	if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) {
-		DEBUG( 0, __FUNCTION__ "(), failed, fifo not empty!\n");
+		IRDA_DEBUG( 0, __FUNCTION__ "(), failed, fifo not empty!\n");
 		return -1;
 	}
         
@@ -712,34 +915,105 @@
 	return actual;
 }
 
-#ifdef MODULE
+/*
+ * Function irport_net_ioctl (dev, rq, cmd)
+ *
+ *    Process IOCTL commands for this device
+ *
+ */
+static int irport_net_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+	struct if_irda_req *irq = (struct if_irda_req *) rq;
+	struct irport_cb *self;
+	dongle_t *dongle;
+	unsigned long flags;
+	int ret = 0;
+
+	ASSERT(dev != NULL, return -1;);
+
+	self = dev->priv;
+
+	ASSERT(self != NULL, return -1;);
+
+	IRDA_DEBUG(2, __FUNCTION__ "(), %s, (cmd=0x%X)\n", dev->name, cmd);
+	
+	/* Disable interrupts & save flags */
+	save_flags(flags);
+	cli();
+	
+	switch (cmd) {
+	case SIOCSBANDWIDTH: /* Set bandwidth */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		irda_task_execute(self, __irport_change_speed, NULL, NULL, 
+				  (void *) irq->ifr_baudrate);
+		break;
+	case SIOCSDONGLE: /* Set dongle */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		/* Initialize dongle */
+		dongle = irda_device_dongle_init(dev, irq->ifr_dongle);
+		if (!dongle)
+			break;
+		
+		dongle->set_mode    = NULL;
+		dongle->read        = NULL;
+		dongle->write       = irport_raw_write;
+		dongle->set_dtr_rts = irport_set_dtr_rts;
+		
+		self->dongle = dongle;
+
+		/* Now initialize the dongle!  */
+		dongle->issue->open(dongle, &self->qos);
+		
+		/* Reset dongle */
+		irda_task_execute(dongle, dongle->issue->reset, NULL, NULL, 
+				  NULL);	
+		break;
+	case SIOCSMEDIABUSY: /* Set media busy */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		irda_device_set_media_busy(self->netdev, TRUE);
+		break;
+	case SIOCGRECEIVING: /* Check if we are receiving right now */
+		irq->ifr_receiving = irport_is_receiving(self);
+		break;
+	case SIOCSDTRRTS:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		irport_set_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+	}
+	
+	restore_flags(flags);
+	
+	return ret;
+}
+
+static struct net_device_stats *irport_net_get_stats(struct device *dev)
+{
+	struct irport_cb *self = (struct irport_cb *) dev->priv;
+	
+	return &self->stats;
+}
 
+#ifdef MODULE
 MODULE_PARM(io, "1-4i");
 MODULE_PARM(irq, "1-4i");
 
 MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
 MODULE_DESCRIPTION("Half duplex serial driver for IrDA SIR mode");
 
-/*
- * Function cleanup_module (void)
- *
- *    
- *
- */
 void cleanup_module(void)
 {
 	irport_cleanup();
 }
 
-/*
- * Function init_module (void)
- *
- *    
- */
 int init_module(void)
 {
 	return irport_init();
 }
-
 #endif /* MODULE */
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)