patch-2.3.26 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 --recursive --new-file v2.3.25/linux/drivers/net/irda/irport.c linux/drivers/net/irda/irport.c
@@ -6,7 +6,7 @@
  * Status:	  Experimental.
  * Author:	  Dag Brattli <dagb@cs.uit.no>
  * Created at:	  Sun Aug  3 13:49:59 1997
- * Modified at:   Wed Oct 20 00:07:42 1999
+ * Modified at:   Sat Oct 30 20:03:42 1999
  * Modified by:   Dag Brattli <dagb@cs.uit.no>
  * Sources:	  serial.c by Linus Torvalds 
  * 
@@ -83,9 +83,12 @@
 static int  irport_net_init(struct net_device *dev);
 static int  irport_net_open(struct net_device *dev);
 static int  irport_net_close(struct net_device *dev);
+static int  irport_net_ioctl(struct net_device *dev, struct ifreq *rq, 
+			     int cmd);
 static int  irport_is_receiving(struct irport_cb *self);
-static void irport_set_dtr_rts(struct net_device *dev, int dtr, int rts);
+static int  irport_set_dtr_rts(struct net_device *dev, int dtr, int rts);
 static int  irport_raw_write(struct net_device *dev, __u8 *buf, int len);
+static struct net_device_stats *irport_net_get_stats(struct net_device *dev);
 
 int __init irport_init(void)
 {
@@ -207,8 +210,6 @@
 		ERROR(__FUNCTION__ "(), dev_alloc() failed!\n");
 		return -ENOMEM;
 	}
-	/* dev_alloc doesn't clear the struct, so lets do a little hack */
-	memset(((__u8*)dev)+sizeof(char*),0,sizeof(struct net_device)-sizeof(char*));
 
  	dev->priv = (void *) self;
 	self->netdev = dev;
@@ -218,6 +219,8 @@
 	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;
@@ -253,7 +256,7 @@
 
 	/* Release the IO-port that this driver is using */
 	IRDA_DEBUG(0 , __FUNCTION__ "(), Releasing Region %03x\n", 
-	      self->io.iobase2);
+		   self->io.iobase2);
 	release_region(self->io.iobase2, self->io.io_ext);
 
 	if (self->tx_buff.head)
@@ -319,7 +322,7 @@
  *    Set speed of IrDA port to specified baudrate
  *
  */
-void irport_change_speed(struct irport_cb *self, __u32 speed)
+void __irport_change_speed(struct irport_cb *self, __u32 speed)
 {
 	unsigned long flags;
 	int iobase; 
@@ -371,6 +374,79 @@
 }
 
 /*
+ * 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
+ */
+static 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 */
+		__irport_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 */
+		__irport_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;
+}
+
+/*
  * Function irport_write_wakeup (tty)
  *
  *    Called by the driver when there's room for more data.  If we have
@@ -452,6 +528,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
@@ -469,7 +571,6 @@
 	ASSERT(dev != NULL, return 0;);
 	
 	self = (struct irport_cb *) dev->priv;
-
 	ASSERT(self != NULL, return 0;);
 
 	iobase = self->io.iobase2;
@@ -482,15 +583,22 @@
 
 		WARNING("%s: transmit timed out\n", dev->name);
 		irport_start(self, iobase);
-		irport_change_speed(self, self->io.speed );
+		__irport_change_speed(self, self->io.speed );
 
 		dev->trans_start = jiffies;
 	}
 
 	/* Check if we need to change the speed */
-	if ((speed = irda_get_speed(skb)) != self->io.speed)
-		irport_change_speed(self, speed);
-
+	if ((speed = irda_get_speed(skb)) != self->io.speed) {
+		if (irda_task_execute(self, irport_change_speed, 
+				      irport_change_speed_complete, NULL,
+				      (void *) speed))	
+			/* 
+			 * Task not finished yet, so make the netdevice 
+			 * layer requeue the frame 
+			 */
+			return -EBUSY;
+	}
 	spin_lock_irqsave(&self->lock, flags);
 
 	/* Init tx buffer */
@@ -533,7 +641,7 @@
          * async_unwrap_char will deliver all found frames  
 	 */
 	do {
-		async_unwrap_char(self->netdev, &self->rx_buff, 
+		async_unwrap_char(self->netdev, &self->stats, &self->rx_buff, 
 				  inb(iobase+UART_RX));
 
 		/* Make sure we don't stay here to long */
@@ -619,8 +727,8 @@
 /*
  * Function irport_net_open (dev)
  *
- *    
- *
+ *    Network device is taken up. Usually this is done by "ifconfig irda0 up" 
+ *   
  */
 static int irport_net_open(struct net_device *dev)
 {
@@ -659,8 +767,8 @@
 /*
  * Function irport_net_close (self)
  *
- *    
- *
+ *    Network device is taken down. Usually this is done by 
+ *    "ifconfig irda0 down" 
  */
 static int irport_net_close(struct net_device *dev)
 {
@@ -733,12 +841,12 @@
  *    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 net_device *dev, int dtr, int rts)
+static int irport_set_dtr_rts(struct net_device *dev, int dtr, int rts)
 {
 	struct irport_cb *self = dev->priv;
 	int iobase;
 
-	ASSERT(self != NULL, return;);
+	ASSERT(self != NULL, return -1;);
 
 	iobase = self->io.iobase2;
 
@@ -748,6 +856,8 @@
 		rts = UART_MCR_RTS;
 
 	outb(dtr|rts|UART_MCR_OUT2, iobase+UART_MCR);
+
+	return 0;
 }
 
 static int irport_raw_write(struct net_device *dev, __u8 *buf, int len)
@@ -774,6 +884,82 @@
 	}
 
 	return actual;
+}
+
+/*
+ * Function irport_net_ioctl (dev, rq, cmd)
+ *
+ *    Process IOCTL commands for this device
+ *
+ */
+static int irport_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct if_irda_req *irq = (struct if_irda_req *) rq;
+	struct irport_cb *self;
+	dongle_t *dongle;
+	unsigned long flags;
+	int ret = 0;
+
+	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 */
+		irda_task_execute(self, irport_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    = 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 */
+		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:
+		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 net_device *dev)
+{
+	struct irport_cb *self = (struct irport_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)