patch-2.2.15 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 --new-file --recursive --exclude-from ../../exclude v2.2.14/drivers/net/irda/irtty.c linux/drivers/net/irda/irtty.c
@@ -6,12 +6,12 @@
  * Status:        Experimental.
  * Author:        Dag Brattli <dagb@cs.uit.no>
  * Created at:    Tue Dec  9 21:18:38 1997
- * Modified at:   Mon May 10 15:45:50 1999
+ * Modified at:   Sat Mar 11 07:43:30 2000
  * 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-1999 Dag Brattli, All Rights Reserved.
+ *     Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved.
  *      
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -25,46 +25,56 @@
  ********************************************************************/    
 
 #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 <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/rtnetlink.h>
+
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <asm/termios.h>
 
 #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>
 
 static hashbin_t *irtty = NULL;
-
 static struct tty_ldisc irda_ldisc;
 
 static int qos_mtt_bits = 0x03;      /* 5 ms or more */
 
+/* Network device fuction prototypes */
 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 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_net_init(struct device *dev);
 static int  irtty_net_open(struct device *dev);
 static int  irtty_net_close(struct device *dev);
+static int  irtty_net_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+static struct net_device_stats *irtty_net_get_stats(struct 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, int baud);
 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 device *dev, int dtr, int rts);
+static int  irtty_raw_write(struct device *dev, __u8 *buf, int len);
+static int  irtty_raw_read(struct device *dev, __u8 *buf, int len);
+static int  irtty_set_mode(struct device *dev, int mode);
+static int  irtty_change_speed(struct irda_task *task);
+
 char *driver_name = "irtty";
 
-__initfunc(int irtty_init(void))
+int __init irtty_init(void)
 {
 	int status;
 	
@@ -91,10 +101,9 @@
 	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);
+	if ((status = tty_register_ldisc(N_IRDA, &irda_ldisc)) != 0) {
+		ERROR("IrDA: can't register line discipline (err = %d)\n", 
+		      status);
 	}
 	
 	return status;
@@ -111,9 +120,7 @@
 {
 	int ret;
 	
-	/*
-	 *  Unregister tty line-discipline
-	 */
+	/* Unregister tty line-discipline */
 	if ((ret = tty_register_ldisc(N_IRDA, NULL))) {
 		ERROR(__FUNCTION__ 
 		      "(), can't unregister line discipline (err = %d)\n",
@@ -138,13 +145,16 @@
  */
 static int irtty_open(struct tty_struct *tty) 
 {
+	struct 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;
 	
@@ -166,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);
@@ -177,11 +186,7 @@
 		tty->ldisc.flush_buffer(tty);
 	
 	self->magic = IRTTY_MAGIC;
-
-	/*
-	 *  Initialize driver
-	 */
-	self->idev.rx_buff.state = OUTSIDE_FRAME;
+	self->mode = IRDA_IRLAP;
 
 	/* 
 	 *  Initialize QoS capabilities, we fill in all the stuff that
@@ -189,38 +194,69 @@
 	 *  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->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 device)-sizeof(char*));
 
-	/* Initialize callbacks */
-	self->idev.change_speed    = irtty_change_speed;
- 	self->idev.is_receiving    = irtty_is_receiving;
-	self->idev.set_dtr_rts     = irtty_set_dtr_rts;
-	self->idev.raw_write       = irtty_raw_write;
-	self->idev.wait_until_sent = irtty_wait_until_sent;
+	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;
 
@@ -241,39 +277,56 @@
 	/* 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;
+	
+	/* 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);
+	}
+	
+	/* We are not using any dongle anymore! */
+	if (self->dongle)
+		irda_device_dongle_cleanup(self->dongle);
+	self->dongle = NULL;
+
+	/* Remove speed changing task if any */
+	if (self->task)
+		irda_task_delete(self->task);
 
 	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;
 	
@@ -287,25 +340,15 @@
 }
 
 /* 
- *  Function irtty_change_speed (self, baud)
+ *  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, int baud) 
+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;
-
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == IRTTY_MAGIC, return;);
 
@@ -314,9 +357,9 @@
 
 	cflag &= ~CBAUD;
 
-	DEBUG(4, __FUNCTION__ "(), Setting speed to %d\n", baud);
+	IRDA_DEBUG(2, __FUNCTION__ "(), Setting speed to %d\n", speed);
 
-	switch (baud) {
+	switch (speed) {
 	case 1200:
 		cflag |= B1200;
 		break;
@@ -346,6 +389,96 @@
 
 	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;);
+
+	/* Check if busy */
+	if (self->task && self->task != task) {
+		IRDA_DEBUG(0, __FUNCTION__ "(), busy!\n");
+		return MSECS_TO_JIFFIES(10);
+	} else
+		self->task = task;
+
+	switch (task->state) {
+	case IRDA_TASK_INIT:
+		/* 
+		 * Make sure all data is sent before changing the speed of the
+		 * serial port.
+		 */
+		if (self->tty->driver.chars_in_buffer(self->tty)) {
+			/* Keep state, and try again later */
+			ret = MSECS_TO_JIFFIES(10);
+			break;
+		} else {
+			/* Transmit buffer is now empty, but it may still
+			 * take over 13 ms for the FIFO to become empty, so
+			 * wait some more to be sure all data is sent
+			 */
+			irda_task_next_state(task, IRDA_TASK_WAIT);
+			ret = MSECS_TO_JIFFIES(13);
+		}
+	case IRDA_TASK_WAIT:
+		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);
+		self->task = NULL;
+		break;
+	default:
+		ERROR(__FUNCTION__ "(), unknown state %d\n", task->state);
+		irda_task_next_state(task, IRDA_TASK_DONE);
+		self->task = NULL;
+		ret = -1;
+		break;
+	}	
+	return ret;
 }
 
 /*
@@ -356,9 +489,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;
 
@@ -366,13 +501,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, 
@@ -380,7 +515,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;
@@ -401,27 +563,72 @@
 {
 	struct irtty_cb *self = (struct irtty_cb *) tty->disc_data;
 
-	ASSERT(self != NULL, return;);
-	ASSERT(self->magic == IRTTY_MAGIC, return;);
-	
+	IRDA_DEBUG(2, __FUNCTION__ "(%ld)\n", jiffies);
+
+	if (!self || !self->netdev) {
+		IRDA_DEBUG(0, __FUNCTION__ "(), not ready yet!\n");
+		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);
-
+			IRDA_DEBUG(0, "Framing or parity error!\n");
+			irda_device_set_media_busy(self->netdev, TRUE);
+			
  			cp++;
  			continue;
  		}
-		/* Unwrap and destuff one byte */
-		async_unwrap_char(&self->idev, *cp++);
+		
+		switch (self->mode) {
+		case IRDA_IRLAP:
+			/* Unwrap and destuff one byte */
+			async_unwrap_char(self->netdev, &self->stats, 
+					  &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;
+		}
 	}
 }
 
 /*
+ * 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;
+
+	IRDA_DEBUG(2, __FUNCTION__ "()\n");
+
+	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;
+}
+
+/*
  * Function irtty_hard_xmit (skb, dev)
  *
  *    Transmit frame
@@ -430,56 +637,40 @@
 static int irtty_hard_xmit(struct sk_buff *skb, struct 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)
+		self->new_speed = speed;
+
 	/* 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);
 
 	dev->trans_start = jiffies;
+	self->stats.tx_bytes += self->tx_buff.len;
 
 	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;
-#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");
- 		self->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
- 		irda_unlock( &self->tbusy);
- 	}
-#endif
 	dev_kfree_skb(skb);
 
 	return 0;
@@ -493,7 +684,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. :-) */
 }
 
@@ -507,7 +698,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;
 	
 	/* 
@@ -516,61 +706,48 @@
 	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 */
-			
-		/* Tell network layer that we want more frames */
-		mark_bh(NET_BH);
+		if (self->new_speed) {
+			IRDA_DEBUG(5, __FUNCTION__ "(), Changing speed!\n");
+			irda_task_execute(self, irtty_change_speed, 
+					  irtty_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++;
 	}
 }
 
 /*
- * 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)
-{
-	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)
+static int irtty_is_receiving(struct irtty_cb *self)
 {
-	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);
 }
 
 /*
@@ -579,15 +756,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 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 */
@@ -610,20 +786,93 @@
 	set_fs(get_ds());
 	
 	if (tty->driver.ioctl(tty, NULL, TIOCMSET, (unsigned long) &arg)) { 
-		ERROR(__FUNCTION__ "(), error doing ioctl!\n");
+		IRDA_DEBUG(2, __FUNCTION__ "(), error doing ioctl!\n");
 	}
 	set_fs(fs);
+
+	return 0;
 }
 
-static int irtty_raw_write(struct irda_device *idev, __u8 *buf, int len)
+/*
+ * 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.
+ */
+int irtty_set_mode(struct device *dev, int mode)
 {
 	struct irtty_cb *self;
-	int actual = 0;
 
-	ASSERT(idev != NULL, return 0;);
-	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+	self = (struct irtty_cb *) dev->priv;
+
+	ASSERT(self != NULL, return -1;);
+
+	IRDA_DEBUG(2, __FUNCTION__ "(), mode=%s\n", infrared_mode[mode]);
+	
+	/* save status for driver */
+	self->mode = mode;
 	
-	self = (struct irtty_cb *) idev->priv;
+	/* reset the buffer state */
+	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 (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 device *dev, __u8 *buf, int len)
+{
+	struct irtty_cb *self;
+	int count;
+
+	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 < self->rx_buff.len) {
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(MSECS_TO_JIFFIES(10));
+
+		if (!timeout--)
+			break;
+	}
+	
+	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 ;-)
+	 */
+	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 device *dev, __u8 *buf, int len)
+{
+	struct irtty_cb *self;
+	int actual = 0;
+
+	self = (struct irtty_cb *) dev->priv;
 
 	ASSERT(self != NULL, return 0;);
 	ASSERT(self->magic == IRTTY_MAGIC, return 0;);
@@ -646,13 +895,27 @@
 
 static int irtty_net_open(struct device *dev)
 {
-	ASSERT(dev != NULL, return -1;);
+	struct irtty_cb *self = (struct irtty_cb *) dev->priv;
+
+	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(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;
 
 	return 0;
@@ -660,17 +923,118 @@
 
 static int irtty_net_close(struct device *dev)
 {
-	ASSERT(dev != NULL, return -1;);
-	
+	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(self, TRUE);
+
 	/* 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 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 */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		irda_task_execute(self, irtty_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    = 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 */
+		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 = irtty_is_receiving(self);
+		break;
+	case SIOCSDTRRTS:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		irtty_set_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts);
+		break;
+	case SIOCSMODE:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		irtty_set_mode(dev, irq->ifr_mode);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+	}
+	
+	restore_flags(flags);
+	
+	return ret;
+}
+
+static struct net_device_stats *irtty_net_get_stats(struct device *dev)
+{
+	struct irtty_cb *self = (struct irtty_cb *) dev->priv;
+
+	return &self->stats;
+}
+
 #ifdef MODULE
 
 MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
@@ -701,10 +1065,3 @@
 }
 
 #endif /* MODULE */
-
-
-
-
-
-
-

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