patch-2.3.24 linux/drivers/net/irda/irtty.c

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

diff -u --recursive --new-file v2.3.23/linux/drivers/net/irda/irtty.c linux/drivers/net/irda/irtty.c
@@ -6,7 +6,7 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Dec  9 21:18:38 1997
- * Modified at:   Tue Sep 28 08:39:29 1999
+ * Modified at:   Wed Oct 20 00:05:43 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>
@@ -29,6 +29,8 @@
 #include <linux/tty.h>
 #include <linux/init.h>
 #include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/rtnetlink.h>
 
 #include <asm/segment.h>
 #include <asm/uaccess.h>
@@ -36,7 +38,6 @@
 #include <net/irda/irda.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>
 
@@ -46,27 +47,31 @@
 
 static int qos_mtt_bits = 0x03;      /* 5 ms or more */
 
+/* Network device fuction prototypes */
 static int  irtty_hard_xmit(struct sk_buff *skb, struct net_device *dev);
-static void irtty_wait_until_sent(struct irda_device *driver);
-static int  irtty_is_receiving(struct irda_device *idev);
-static void irtty_set_dtr_rts(struct irda_device *idev, int dtr, int rts);
-static int  irtty_raw_write(struct irda_device *idev, __u8 *buf, int len);
-static int  irtty_raw_read(struct irda_device *idev, __u8 *buf, int len, 
-			   int timeout);
-static void irtty_set_raw_mode(struct irda_device *dev, int mode);
 static int  irtty_net_init(struct net_device *dev);
 static int  irtty_net_open(struct net_device *dev);
 static int  irtty_net_close(struct net_device *dev);
+static int  irtty_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static struct net_device_stats *irtty_net_get_stats(struct net_device *dev);
 
+/* Line discipline function prototypes */
 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, __u32 speed);
 static void irtty_write_wakeup(struct tty_struct *tty);
-
 static void irtty_receive_buf(struct tty_struct *, const unsigned char *, 
 			      char *, int);
+
+/* IrDA specific function protoctypes */
+static int  irtty_is_receiving(struct irtty_cb *self);
+static int  irtty_set_dtr_rts(struct net_device *dev, int dtr, int rts);
+static int  irtty_raw_write(struct net_device *dev, __u8 *buf, int len);
+static int  irtty_raw_read(struct net_device *dev, __u8 *buf, int len);
+static int  irtty_set_mode(struct net_device *dev, int mode);
+static int  irtty_change_speed(struct irda_task *task);
+
 char *driver_name = "irtty";
 
 int __init irtty_init(void)
@@ -140,13 +145,16 @@
  */
 static int irtty_open(struct tty_struct *tty) 
 {
+	struct net_device *dev;
 	struct irtty_cb *self;
 	char name[16];
+	int err;
 	
 	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;
 	
@@ -168,9 +176,8 @@
 	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);
+
+	hashbin_insert(irtty, (queue_t *) self, (int) self, NULL);
 
 	if (tty->driver.flush_buffer)
 		tty->driver.flush_buffer(tty);
@@ -179,11 +186,7 @@
 		tty->ldisc.flush_buffer(tty);
 	
 	self->magic = IRTTY_MAGIC;
-
-	/*
-	 *  Initialize driver
-	 */
-	self->idev.rx_buff.state = OUTSIDE_FRAME;
+	self->rx_buff.state = OUTSIDE_FRAME;
 
 	/* 
 	 *  Initialize QoS capabilities, we fill in all the stuff that
@@ -191,40 +194,71 @@
 	 *  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);
+	irda_init_max_qos_capabilies(&self->qos);
 
 	/* The only value we must override it the baudrate */
-	self->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;
-	self->idev.qos.min_turn_time.bits = qos_mtt_bits;
-	self->idev.flags = IFF_SIR | IFF_PIO;
-	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;
+	self->qos.min_turn_time.bits = qos_mtt_bits;
+	self->flags = IFF_SIR | IFF_PIO;
+	irda_qos_bits_to_value(&self->qos);
 
 	/* Specify how much memory we want */
-	self->idev.rx_buff.truesize = 4000; 
-	self->idev.tx_buff.truesize = 4000;
+	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 -ENOMEM;
+		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 -ENOMEM;
+		}
+		memset(self->tx_buff.head, 0, self->tx_buff.truesize);
+	}
+	
+      	self->magic = IRTTY_MAGIC;
+
+	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;
+	
+	if (!(dev = dev_alloc("irda%d", &err))) {
+		ERROR(__FUNCTION__ "(), dev_alloc() failed!\n");
+		return -ENOMEM;
+	}
+	/* dev_alloc doesn't clear the struct */
+	memset(((__u8*)dev)+sizeof(char*),0,sizeof(struct net_device)-sizeof(char*));
 
-	/* Initialize callbacks */
-	self->idev.change_speed    = irtty_change_speed;
- 	self->idev.is_receiving    = irtty_is_receiving;
-	self->idev.wait_until_sent = irtty_wait_until_sent;
-	self->idev.set_dtr_rts     = irtty_set_dtr_rts;
-	self->idev.set_raw_mode    = irtty_set_raw_mode;
-	self->idev.raw_write       = irtty_raw_write;
-	self->idev.raw_read        = irtty_raw_read;
+	dev->priv = (void *) self;
+	self->netdev = dev;
 
 	/* 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;
+	dev->init            = irtty_net_init;
+	dev->hard_start_xmit = irtty_hard_xmit;
+	dev->open            = irtty_net_open;
+	dev->stop            = irtty_net_close;
+	dev->get_stats	     = irtty_net_get_stats;
+	dev->do_ioctl        = irtty_net_ioctl;
+
+	rtnl_lock();
+	err = register_netdevice(dev);
+	rtnl_unlock();
+	if (err) {
+		ERROR(__FUNCTION__ "(), register_netdev() failed!\n");
+		return -1;
+	}
 
-	/* Open the IrDA device */
-	irda_device_open(&self->idev, name, self);
+	MESSAGE("IrDA: Registered device %s\n", dev->name);
 
 	MOD_INC_USE_COUNT;
 
@@ -245,39 +279,50 @@
 	/* First make sure we're connected. */
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRTTY_MAGIC, return;);
-
-	/* Remove driver */
-	irda_device_close(&self->idev);
-
+	
 	/* Stop tty */
 	tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
 	tty->disc_data = 0;
-
+	
+	/* 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();
+	}
+	
 	self->tty = NULL;
 	self->magic = 0;
 	
 	self = hashbin_remove(irtty, (int) self, NULL);
-	
-	if (self != NULL)
-		kfree(self);
 
+	if (self->tx_buff.head)
+		kfree(self->tx_buff.head);
+	
+	if (self->rx_buff.head)
+		kfree(self->rx_buff.head);
+	
+	kfree(self);
+	
  	MOD_DEC_USE_COUNT;
 }
 
 /*
- * Function irtty_stop_receiver (irda_device, stop)
+ * Function irtty_stop_receiver (self, stop)
  *
  *    
  *
  */
-static void irtty_stop_receiver(struct irda_device *idev, int stop)
+static void irtty_stop_receiver(struct irtty_cb *self, int stop)
 {
 	struct termios old_termios;
-	struct irtty_cb *self;
 	int cflag;
 
-	self = (struct irtty_cb *) idev->priv;
-
 	old_termios = *(self->tty->termios);
 	cflag = self->tty->termios->c_cflag;
 	
@@ -291,24 +336,16 @@
 }
 
 /* 
- *  Function irtty_change_speed (self, speed)
+ *  Function irtty_do_change_speed (self, speed)
  *
- *    Change the speed of the serial port. The driver layer must check that
- *    all transmission has finished using the irtty_wait_until_sent() 
- *    function.
+ *    Change the speed of the serial port.
  */
-static void irtty_change_speed(struct irda_device *idev, __u32 speed)
+static void __irtty_change_speed(struct irtty_cb *self, __u32 speed)
 {
         struct termios old_termios;
-	struct irtty_cb *self;
 	int cflag;
 
-	DEBUG(4, __FUNCTION__ "(), <%ld>\n", jiffies); 
-
-	ASSERT(idev != NULL, return;);
-	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
-
-	self = (struct irtty_cb *) idev->priv;
+	IRDA_DEBUG(0, __FUNCTION__ "(), <%ld>\n", jiffies); 
 
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRTTY_MAGIC, return;);
@@ -318,7 +355,7 @@
 
 	cflag &= ~CBAUD;
 
-	DEBUG(4, __FUNCTION__ "(), Setting speed to %d\n", speed);
+	IRDA_DEBUG(0, __FUNCTION__ "(), Setting speed to %d\n", speed);
 
 	switch (speed) {
 	case 1200:
@@ -350,6 +387,81 @@
 
 	self->tty->termios->c_cflag = cflag;
 	self->tty->driver.set_termios(self->tty, &old_termios);
+
+	self->io.speed = speed;
+}
+
+/*
+ * Function irtty_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
+ */
+static int irtty_change_speed(struct irda_task *task)
+{
+	struct irtty_cb *self;
+	__u32 speed = (__u32) task->param;
+	int ret = 0;
+
+	IRDA_DEBUG(2, __FUNCTION__ "(), <%ld>\n", jiffies); 
+
+	self = (struct irtty_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->tty->driver.chars_in_buffer(self->tty)) {
+			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 */
+		__irtty_change_speed(self, 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 */
+		__irtty_change_speed(self, 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;
 }
 
 /*
@@ -360,9 +472,11 @@
  */
 static int irtty_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
 {
+	dongle_t *dongle;
+	struct irtty_info info;
 	struct irtty_cb *self;
-	int err = 0;
 	int size = _IOC_SIZE(cmd);
+	int err = 0;
 
 	self = (struct irtty_cb *) tty->disc_data;
 
@@ -370,13 +484,13 @@
 	ASSERT(self->magic == IRTTY_MAGIC, return -EBADR;);
 
 	if (_IOC_DIR(cmd) & _IOC_READ)
-		err = verify_area( VERIFY_WRITE, (void *) arg, size);
+		err = verify_area(VERIFY_WRITE, (void *) arg, size);
 	else if (_IOC_DIR(cmd) & _IOC_WRITE)
-		err = verify_area( VERIFY_READ, (void *) arg, size);
+		err = verify_area(VERIFY_READ, (void *) arg, size);
 	if (err)
 		return err;
 	
-	switch(cmd) {
+	switch (cmd) {
 	case TCGETS:
 	case TCGETA:
 		return n_tty_ioctl(tty, (struct file *) file, cmd, 
@@ -384,7 +498,34 @@
 		break;
 	case IRTTY_IOCTDONGLE:
 		/* Initialize dongle */
-		irda_device_init_dongle(&self->idev, (int) arg);
+		dongle = irda_device_dongle_init(self->netdev, (int) arg);
+		if (!dongle)
+			break;
+		
+		/* Initialize callbacks */
+		dongle->set_mode    = irtty_set_mode;
+		dongle->read        = irtty_raw_read;
+		dongle->write       = irtty_raw_write;
+		dongle->set_dtr_rts = irtty_set_dtr_rts;
+		
+		/* Bind dongle */
+		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 IRTTY_IOCGET:
+		ASSERT(self->netdev != NULL, return -1;);
+
+		memset(&info, 0, sizeof(struct irtty_info)); 
+		strncpy(info.name, self->netdev->name, 5);
+
+		if (copy_to_user(arg, &info, sizeof(struct irtty_info)))
+			return -EFAULT;
 		break;
 	default:
 		return -ENOIOCTLCMD;
@@ -405,34 +546,61 @@
 {
 	struct irtty_cb *self = (struct irtty_cb *) tty->disc_data;
 
-	DEBUG(5, __FUNCTION__ "(,,,count=%d)\n", count);
 	/* 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.netdev, TRUE);
-
+			IRDA_DEBUG(0, "Framing or parity error!\n");
+			irda_device_set_media_busy(self->netdev, TRUE);
+			
  			cp++;
  			continue;
  		}
+		
+		switch (self->mode) {
+		case IRDA_IRLAP:
+			/* Unwrap and destuff one byte */
+			async_unwrap_char(self->netdev, &self->rx_buff, *cp++);
+			break;
+		case IRDA_RAW:
+			/* What should we do when the buffer is full? */
+			if (self->rx_buff.len == self->rx_buff.truesize)
+				self->rx_buff.len = 0;
+			
+			self->rx_buff.data[self->rx_buff.len++] = *cp++;
+			break;
+		default:
+			break;
+		}
+	}
+}
 
-		DEBUG(6, __FUNCTION__ " char=0x%02x\n", *cp);
- 		if (self->idev.raw_mode) {
-			struct irda_device *idev = &self->idev;
+/*
+ * Function irtty_change_speed_complete (task)
+ *
+ *    Called when the change speed operation completes
+ *
+ */
+static int irtty_change_speed_complete(struct irda_task *task)
+{
+	struct irtty_cb *self;
 
-			/* What should we do when the buffer is full? */
-			if (idev->rx_buff.len == idev->rx_buff.truesize)
-				idev->rx_buff.len = 0;
+	IRDA_DEBUG(0, __FUNCTION__ "()\n");
 
-			idev->rx_buff.data[idev->rx_buff.len++] = *cp++;
-		} else {
-			/* Unwrap and destuff one byte */
-			async_unwrap_char(&self->idev, *cp++);
- 		}
-	}
+	self = (struct irtty_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;
 }
 
 /*
@@ -444,29 +612,34 @@
 static int irtty_hard_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct irtty_cb *self;
-	struct irda_device *idev;
 	int actual = 0;
+	__u32 speed;
 
-	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;
-
+	self = (struct irtty_cb *) dev->priv;
 	ASSERT(self != NULL, return 0;);
-	ASSERT(self->magic == IRTTY_MAGIC, return 0;);
 
 	/* Lock transmit buffer */
 	if (irda_lock((void *) &dev->tbusy) == FALSE)
 		return -EBUSY;
+
+	/* Check if we need to change the speed */
+	if ((speed = irda_get_speed(skb)) != self->io.speed) {
+		if (irda_task_execute(self, irtty_change_speed, 
+				      irtty_change_speed_complete, NULL,
+				      (void *) speed))	
+			/* 
+			 * Task not finished yet, so make the netdevice 
+			 * layer requeue the frame 
+			 */
+			return -EBUSY;
+	}
 	
 	/* 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); 
 
 	self->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
 
@@ -474,22 +647,22 @@
 
 	if (self->tty->driver.write)
 		actual = self->tty->driver.write(self->tty, 0, 
-						 idev->tx_buff.data, 
-						 idev->tx_buff.len);
+						 self->tx_buff.data, 
+						 self->tx_buff.len);
 
 	/* Hide the part we just transmitted */
-	idev->tx_buff.data += actual;
-	idev->tx_buff.len -= actual;
+	self->tx_buff.data += actual;
+	self->tx_buff.len -= actual;
 
-	idev->stats.tx_packets++;
-	idev->stats.tx_bytes += idev->tx_buff.len;
+	self->stats.tx_packets++;
+	self->stats.tx_bytes += self->tx_buff.len;
 #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_buff.len) == 0) {
- 		DEBUG( 4, "irtty_xmit_buf: finished with frame!\n");
+ 	if ((self->tx_buff.len) == 0) {
+ 		IRDA_DEBUG( 4, "irtty_xmit_buf: finished with frame!\n");
  		self->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
  		irda_unlock( &self->tbusy);
  	}
@@ -507,7 +680,7 @@
 */
 static int irtty_receive_room(struct tty_struct *tty) 
 {
-	DEBUG(0, __FUNCTION__ "()\n");
+	IRDA_DEBUG(0, __FUNCTION__ "()\n");
 	return 65536;  /* We can handle an infinite amount of data. :-) */
 }
 
@@ -521,7 +694,6 @@
 static void irtty_write_wakeup(struct tty_struct *tty) 
 {
 	struct irtty_cb *self = (struct irtty_cb *) tty->disc_data;
-	struct irda_device *idev;
 	int actual = 0;
 	
 	/* 
@@ -530,26 +702,24 @@
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRTTY_MAGIC, return;);
 
-	idev = &self->idev;
-
 	/* Finished with frame?  */
-	if (idev->tx_buff.len > 0)  {
+	if (self->tx_buff.len > 0)  {
 		/* Write data left in transmit buffer */
-		actual = tty->driver.write(tty, 0, idev->tx_buff.data, 
-					   idev->tx_buff.len);
+		actual = tty->driver.write(tty, 0, self->tx_buff.data, 
+					   self->tx_buff.len);
 
-		idev->tx_buff.data += actual;
-		idev->tx_buff.len  -= actual;
+		self->tx_buff.data += actual;
+		self->tx_buff.len  -= actual;
 	} else {		
 		/* 
 		 *  Now serial buffer is almost free & we can start 
 		 *  transmission of another packet 
 		 */
-		DEBUG(5, __FUNCTION__ "(), finished with frame!\n");
+		IRDA_DEBUG(5, __FUNCTION__ "(), finished with frame!\n");
 		
 		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
 
-		idev->netdev.tbusy = 0; /* Unlock */
+		self->netdev->tbusy = 0; /* Unlock */
 			
 		/* Tell network layer that we want more frames */
 		mark_bh(NET_BH);
@@ -557,34 +727,14 @@
 }
 
 /*
- * Function irtty_is_receiving (idev)
+ * Function irtty_is_receiving (self)
  *
  *    Return TRUE is we are currently receiving a frame
  *
  */
-static int irtty_is_receiving(struct irda_device *idev)
+static int irtty_is_receiving(struct irtty_cb *self)
 {
-	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);
+	return (self->rx_buff.state != OUTSIDE_FRAME);
 }
 
 /*
@@ -593,15 +743,14 @@
  *    This function can be used by dongles etc. to set or reset the status
  *    of the dtr and rts lines
  */
-static void irtty_set_dtr_rts(struct irda_device *idev, int dtr, int rts)
+static int irtty_set_dtr_rts(struct net_device *dev, int dtr, int rts)
 {
-	struct tty_struct *tty;
 	struct irtty_cb *self;
+	struct tty_struct *tty;
 	mm_segment_t fs;
 	int arg = 0;
 
-	self = (struct irtty_cb *) idev->priv;
-
+	self = (struct irtty_cb *) dev->priv;
 	tty = self->tty;
 
 #ifdef TIOCM_OUT2 /* Not defined for ARM */
@@ -627,52 +776,62 @@
 		ERROR(__FUNCTION__ "(), error doing ioctl!\n");
 	}
 	set_fs(fs);
+
+	return 0;
 }
 
 /*
- * Function irtty_set_raw_mode (idev, status)
+ * Function irtty_set_mode (self, status)
  *
  *    For the airport dongle, we need support for reading raw characters
  *    from the IrDA device. This function switches between those modes. 
  *    FALSE is the default mode, and will then treat incoming data as IrDA 
  *    packets.
  */
-void irtty_set_raw_mode(struct irda_device *idev, int status)
+int irtty_set_mode(struct net_device *dev, int mode)
 {
 	struct irtty_cb *self;
 
-	DEBUG(2, __FUNCTION__ "(), status=%s\n", status ? "TRUE" : "FALSE");
+	self = (struct irtty_cb *) dev->priv;
 
-	ASSERT(idev != NULL, return;);
-	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
-	
-	self = (struct irtty_cb *) idev->priv;
+	ASSERT(self != NULL, return -1;);
 
+	IRDA_DEBUG(2, __FUNCTION__ "(), mode=%s\n", infrared_mode[mode]);
+	
 	/* save status for driver */
-	self->idev.raw_mode = status;
+	self->mode = mode;
 	
 	/* reset the buffer state */
-	idev->rx_buff.data = idev->rx_buff.head;
-	idev->rx_buff.len = 0;
-	idev->rx_buff.state = OUTSIDE_FRAME;
+	self->rx_buff.data = self->rx_buff.head;
+	self->rx_buff.len = 0;
+	self->rx_buff.state = OUTSIDE_FRAME;
+
+	return 0;
 }
 
 /*
- * Function irtty_raw_read (idev, buf, len)
+ * Function irtty_raw_read (self, buf, len)
  *
  *    Receive incomming data. This function sleeps, so it must only be
  *    called with a process context. Timeout is currently defined to be
  *    a multiple of 10 ms.
  */
-static int irtty_raw_read(struct irda_device *idev, __u8 *buf, int len, 
-			  int timeout)
+static int irtty_raw_read(struct net_device *dev, __u8 *buf, int len)
 {
+	struct irtty_cb *self;
 	int count;
 
-	buf = idev->rx_buff.data;
+	self = (struct irtty_cb *) dev->priv;
+
+	ASSERT(self != NULL, return 0;);
+	ASSERT(self->magic == IRTTY_MAGIC, return 0;);
+
+	return 0;
+#if 0
+	buf = self->rx_buff.data;
 
 	/* Wait for the requested amount of data to arrive */
-	while (len < idev->rx_buff.len) {
+	while (len < self->rx_buff.len) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule_timeout(MSECS_TO_JIFFIES(10));
 
@@ -680,30 +839,27 @@
 			break;
 	}
 	
-	count = idev->rx_buff.len < len ? idev->rx_buff.len : len;
+	count = self->rx_buff.len < len ? self->rx_buff.len : len;
 
 	/* 
 	 * Reset the state, this mean that a raw read is sort of a 
 	 * datagram read, and _not_ a stream style read. Be aware of the
 	 * difference. Implementing it the other way will just be painful ;-)
 	 */
-	idev->rx_buff.data = idev->rx_buff.head;
-	idev->rx_buff.len = 0;
-	idev->rx_buff.state = OUTSIDE_FRAME;
-
+	self->rx_buff.data = self->rx_buff.head;
+	self->rx_buff.len = 0;
+	self->rx_buff.state = OUTSIDE_FRAME;
+#endif
 	/* Return the amount we were able to get */
 	return count;
 }
 
-static int irtty_raw_write(struct irda_device *idev, __u8 *buf, int len)
+static int irtty_raw_write(struct net_device *dev, __u8 *buf, int len)
 {
 	struct irtty_cb *self;
 	int actual = 0;
 
-	ASSERT(idev != NULL, return 0;);
-	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return -1;);
-	
-	self = (struct irtty_cb *) idev->priv;
+	self = (struct irtty_cb *) dev->priv;
 
 	ASSERT(self != NULL, return 0;);
 	ASSERT(self->magic == IRTTY_MAGIC, return 0;);
@@ -714,8 +870,6 @@
 	return actual;
 }
 
-
-
 static int irtty_net_init(struct net_device *dev)
 {
 	/* Set up to be a normal IrDA network device driver */
@@ -728,12 +882,26 @@
 
 static int irtty_net_open(struct net_device *dev)
 {
-	struct irda_device *idev = dev->priv;
+	struct irtty_cb *self = (struct irtty_cb *) dev->priv;
 
-	irda_device_net_open(dev);
+	ASSERT(self != NULL, return -1;);
+	ASSERT(self->magic == IRTTY_MAGIC, return -1;);
+
+	IRDA_DEBUG(0, __FUNCTION__ "()\n");
+	
+	/* Ready to play! */
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
 
 	/* Make sure we can receive more data */
-	irtty_stop_receiver(idev, FALSE);
+	irtty_stop_receiver(self, FALSE);
+
+	/* 
+	 * Open new IrLAP layer instance, now that everything should be
+	 * initialized properly 
+	 */
+	self->irlap = irlap_open(dev, &self->qos);
 
 	MOD_INC_USE_COUNT;
 
@@ -742,16 +910,103 @@
 
 static int irtty_net_close(struct net_device *dev)
 {
-	struct irda_device *idev = dev->priv;
+	struct irtty_cb *self = (struct irtty_cb *) dev->priv;
+
+	ASSERT(self != NULL, return -1;);
+	ASSERT(self->magic == IRTTY_MAGIC, return -1;);
 
 	/* Make sure we don't receive more data */
-	irtty_stop_receiver(idev, TRUE);
+	irtty_stop_receiver(self, TRUE);
 
-	irda_device_net_close(dev);
+	/* Stop device */
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	/* Stop and remove instance of IrLAP */
+	if (self->irlap)
+		irlap_close(self->irlap);
+	self->irlap = NULL;
 
 	MOD_DEC_USE_COUNT;
 
 	return 0;
+}
+
+/*
+ * Function irtty_net_ioctl (dev, rq, cmd)
+ *
+ *    Process IOCTL commands for this device
+ *
+ */
+static int irtty_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct if_irda_req *irq = (struct if_irda_req *) rq;
+	struct irtty_cb *self;
+	dongle_t *dongle;
+	unsigned long flags;
+	int ret = 0;
+
+	ASSERT(dev != NULL, return -1;);
+
+	self = dev->priv;
+
+	ASSERT(self != NULL, return -1;);
+	ASSERT(self->magic == IRTTY_MAGIC, 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 */
+		irda_task_execute(self, irtty_change_speed, NULL, NULL, 
+				  (void *) irq->ifr_baudrate);
+		break;
+	case SIOCSDONGLE: /* Set dongle */
+		/* Initialize dongle */
+		dongle = irda_device_dongle_init(dev, irq->ifr_dongle);
+		if (!dongle)
+			break;
+		
+		dongle->set_mode    = irtty_set_mode;
+		dongle->read        = irtty_raw_read;
+		dongle->write       = irtty_raw_write;
+		dongle->set_dtr_rts = irtty_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 */
+		irda_device_set_media_busy(self->netdev, TRUE);
+		break;
+	case SIOCGRECEIVING: /* Check if we are receiving right now */
+		irq->ifr_receiving = irtty_is_receiving(self);
+		break;
+	case SIOCSDTRRTS:
+		irtty_set_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+	}
+	
+	restore_flags(flags);
+	
+	return ret;
+}
+
+static struct net_device_stats *irtty_net_get_stats(struct net_device *dev)
+{
+	struct irtty_cb *self = (struct irtty_cb *) dev->priv;
+
+	return &self->stats;
 }
 
 #ifdef MODULE

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