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
- Lines: 1051
- Date:
Fri Apr 21 12:46:17 2000
- Orig file:
v2.2.14/drivers/net/irda/irtty.c
- Orig date:
Sat Aug 14 02:26:48 1999
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)