| /********************************************************************* |
| * |
| * Filename: irtty.c |
| * Version: 1.1 |
| * Description: IrDA line discipline implementation |
| * Status: Experimental. |
| * Author: Dag Brattli <dagb@cs.uit.no> |
| * Created at: Tue Dec 9 21:18:38 1997 |
| * 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-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 |
| * published by the Free Software Foundation; either version 2 of |
| * the License, or (at your option) any later version. |
| * |
| * Neither Dag Brattli nor University of Tromsø admit liability nor |
| * provide warranty for any of this software. This material is |
| * provided "AS-IS" and at no charge. |
| * |
| ********************************************************************/ |
| |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/tty.h> |
| #include <linux/init.h> |
| #include <linux/skbuff.h> |
| #include <linux/if_arp.h> |
| #include <linux/rtnetlink.h> |
| |
| #include <asm/uaccess.h> |
| |
| #include <net/irda/irda.h> |
| #include <net/irda/irtty.h> |
| #include <net/irda/wrapper.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 net_device *dev); |
| 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_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) |
| { |
| int status; |
| |
| irtty = hashbin_new( HB_LOCAL); |
| if ( irtty == NULL) { |
| printk( KERN_WARNING "IrDA: Can't allocate irtty hashbin!\n"); |
| return -ENOMEM; |
| } |
| |
| /* Fill in our line protocol discipline, and register it */ |
| memset(&irda_ldisc, 0, sizeof( irda_ldisc)); |
| |
| irda_ldisc.magic = TTY_LDISC_MAGIC; |
| irda_ldisc.name = "irda"; |
| irda_ldisc.flags = 0; |
| irda_ldisc.open = irtty_open; |
| irda_ldisc.close = irtty_close; |
| irda_ldisc.read = NULL; |
| irda_ldisc.write = NULL; |
| irda_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *, |
| unsigned int, unsigned long)) irtty_ioctl; |
| irda_ldisc.poll = NULL; |
| irda_ldisc.receive_buf = irtty_receive_buf; |
| irda_ldisc.receive_room = irtty_receive_room; |
| irda_ldisc.write_wakeup = irtty_write_wakeup; |
| |
| if ((status = tty_register_ldisc(N_IRDA, &irda_ldisc)) != 0) { |
| ERROR("IrDA: can't register line discipline (err = %d)\n", |
| status); |
| } |
| |
| return status; |
| } |
| |
| /* |
| * Function irtty_cleanup ( ) |
| * |
| * Called when the irda module is removed. Here we remove all instances |
| * of the driver, and the master array. |
| */ |
| #ifdef MODULE |
| static void irtty_cleanup(void) |
| { |
| int ret; |
| |
| /* Unregister tty line-discipline */ |
| if ((ret = tty_register_ldisc(N_IRDA, NULL))) { |
| ERROR(__FUNCTION__ |
| "(), can't unregister line discipline (err = %d)\n", |
| ret); |
| } |
| |
| /* |
| * The TTY should care of deallocating the instances by using the |
| * callback to irtty_close(), therefore we do give any deallocation |
| * function to hashbin_destroy(). |
| */ |
| hashbin_delete(irtty, NULL); |
| } |
| #endif /* MODULE */ |
| |
| /* |
| * Function irtty_open(tty) |
| * |
| * This function is called by the TTY module when the IrDA line |
| * discipline is called for. Because we are sure the tty line exists, |
| * we only have to link it to a free IrDA channel. |
| */ |
| 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; |
| |
| /* |
| * Allocate new instance of the driver |
| */ |
| self = kmalloc(sizeof(struct irtty_cb), GFP_KERNEL); |
| if (self == NULL) { |
| printk(KERN_ERR "IrDA: Can't allocate memory for " |
| "IrDA control block!\n"); |
| return -ENOMEM; |
| } |
| memset(self, 0, sizeof(struct irtty_cb)); |
| |
| self->tty = tty; |
| tty->disc_data = self; |
| |
| /* Give self a name */ |
| sprintf(name, "%s%d", tty->driver.name, |
| minor(tty->device) - tty->driver.minor_start + |
| tty->driver.name_base); |
| |
| hashbin_insert(irtty, (irda_queue_t *) self, (int) self, NULL); |
| |
| if (tty->driver.flush_buffer) |
| tty->driver.flush_buffer(tty); |
| |
| if (tty->ldisc.flush_buffer) |
| tty->ldisc.flush_buffer(tty); |
| |
| self->magic = IRTTY_MAGIC; |
| self->mode = IRDA_IRLAP; |
| |
| /* |
| * Initialize QoS capabilities, we fill in all the stuff that |
| * we support. Be careful not to place any restrictions on values |
| * 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->qos); |
| |
| /* The only value we must override it the baudrate */ |
| self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| |
| IR_115200; |
| 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->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->priv = (void *) self; |
| self->netdev = dev; |
| |
| /* Override the network functions we need to use */ |
| 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; |
| } |
| |
| MESSAGE("IrDA: Registered device %s\n", dev->name); |
| |
| MOD_INC_USE_COUNT; |
| |
| return 0; |
| } |
| |
| /* |
| * Function irtty_close (tty) |
| * |
| * Close down a IrDA channel. This means flushing out any pending queues, |
| * and then restoring the TTY line discipline to what it was before it got |
| * hooked to IrDA (which usually is TTY again). |
| */ |
| static void irtty_close(struct tty_struct *tty) |
| { |
| struct irtty_cb *self = (struct irtty_cb *) tty->disc_data; |
| |
| /* First make sure we're connected. */ |
| ASSERT(self != NULL, return;); |
| ASSERT(self->magic == IRTTY_MAGIC, return;); |
| |
| /* 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(); |
| } |
| |
| /* 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->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 (self, stop) |
| * |
| * |
| * |
| */ |
| static void irtty_stop_receiver(struct irtty_cb *self, int stop) |
| { |
| struct termios old_termios; |
| int cflag; |
| |
| old_termios = *(self->tty->termios); |
| cflag = self->tty->termios->c_cflag; |
| |
| if (stop) |
| cflag &= ~CREAD; |
| else |
| cflag |= CREAD; |
| |
| self->tty->termios->c_cflag = cflag; |
| self->tty->driver.set_termios(self->tty, &old_termios); |
| } |
| |
| /* |
| * Function irtty_do_change_speed (self, speed) |
| * |
| * Change the speed of the serial port. |
| */ |
| static void __irtty_change_speed(struct irtty_cb *self, __u32 speed) |
| { |
| struct termios old_termios; |
| int cflag; |
| |
| ASSERT(self != NULL, return;); |
| ASSERT(self->magic == IRTTY_MAGIC, return;); |
| |
| old_termios = *(self->tty->termios); |
| cflag = self->tty->termios->c_cflag; |
| |
| cflag &= ~CBAUD; |
| |
| IRDA_DEBUG(2, __FUNCTION__ "(), Setting speed to %d\n", speed); |
| |
| switch (speed) { |
| case 1200: |
| cflag |= B1200; |
| break; |
| case 2400: |
| cflag |= B2400; |
| break; |
| case 4800: |
| cflag |= B4800; |
| break; |
| case 19200: |
| cflag |= B19200; |
| break; |
| case 38400: |
| cflag |= B38400; |
| break; |
| case 57600: |
| cflag |= B57600; |
| break; |
| case 115200: |
| cflag |= B115200; |
| break; |
| case 9600: |
| default: |
| cflag |= B9600; |
| break; |
| } |
| |
| 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; |
| } |
| |
| /* |
| * Function irtty_ioctl (tty, file, cmd, arg) |
| * |
| * The Swiss army knife of system calls :-) |
| * |
| */ |
| 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 size = _IOC_SIZE(cmd); |
| int err = 0; |
| |
| self = (struct irtty_cb *) tty->disc_data; |
| |
| ASSERT(self != NULL, return -ENODEV;); |
| ASSERT(self->magic == IRTTY_MAGIC, return -EBADR;); |
| |
| if (_IOC_DIR(cmd) & _IOC_READ) |
| err = verify_area(VERIFY_WRITE, (void *) arg, size); |
| else if (_IOC_DIR(cmd) & _IOC_WRITE) |
| err = verify_area(VERIFY_READ, (void *) arg, size); |
| if (err) |
| return err; |
| |
| switch (cmd) { |
| case TCGETS: |
| case TCGETA: |
| return n_tty_ioctl(tty, (struct file *) file, cmd, |
| (unsigned long) arg); |
| break; |
| case IRTTY_IOCTDONGLE: |
| /* Initialize dongle */ |
| 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; |
| } |
| return 0; |
| } |
| |
| /* |
| * Function irtty_receive_buf( tty, cp, count) |
| * |
| * Handle the 'receiver data ready' interrupt. This function is called |
| * by the 'tty_io' module in the kernel when a block of IrDA data has |
| * been received, which can now be decapsulated and delivered for |
| * further processing |
| */ |
| static void irtty_receive_buf(struct tty_struct *tty, const unsigned char *cp, |
| char *fp, int count) |
| { |
| struct irtty_cb *self = (struct irtty_cb *) tty->disc_data; |
| |
| 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++) { |
| 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->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 */ |
| /* Signal network layer so it can try to send the frame */ |
| netif_wake_queue(self->netdev); |
| |
| return 0; |
| } |
| |
| /* |
| * Function irtty_hard_xmit (skb, dev) |
| * |
| * Transmit frame |
| * |
| */ |
| static int irtty_hard_xmit(struct sk_buff *skb, struct net_device *dev) |
| { |
| struct irtty_cb *self; |
| int actual = 0; |
| __s32 speed; |
| |
| self = (struct irtty_cb *) dev->priv; |
| ASSERT(self != NULL, return 0;); |
| |
| /* Lock transmit buffer */ |
| netif_stop_queue(dev); |
| |
| /* Check if we need to change the speed */ |
| speed = irda_get_next_speed(skb); |
| if ((speed != self->io.speed) && (speed != -1)) { |
| /* Check for empty frame */ |
| if (!skb->len) { |
| irda_task_execute(self, irtty_change_speed, |
| irtty_change_speed_complete, |
| NULL, (void *) speed); |
| dev_kfree_skb(skb); |
| return 0; |
| } else |
| self->new_speed = speed; |
| } |
| |
| /* Init tx buffer*/ |
| self->tx_buff.data = self->tx_buff.head; |
| |
| /* Copy skb to tx_buff while wrapping, stuffing and making CRC */ |
| 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, |
| self->tx_buff.data, |
| self->tx_buff.len); |
| /* Hide the part we just transmitted */ |
| self->tx_buff.data += actual; |
| self->tx_buff.len -= actual; |
| |
| dev_kfree_skb(skb); |
| |
| return 0; |
| } |
| |
| /* |
| * Function irtty_receive_room (tty) |
| * |
| * Used by the TTY to find out how much data we can receive at a time |
| * |
| */ |
| static int irtty_receive_room(struct tty_struct *tty) |
| { |
| IRDA_DEBUG(0, __FUNCTION__ "()\n"); |
| return 65536; /* We can handle an infinite amount of data. :-) */ |
| } |
| |
| /* |
| * Function irtty_write_wakeup (tty) |
| * |
| * Called by the driver when there's room for more data. If we have |
| * more packets to send, we send them here. |
| * |
| */ |
| static void irtty_write_wakeup(struct tty_struct *tty) |
| { |
| struct irtty_cb *self = (struct irtty_cb *) tty->disc_data; |
| int actual = 0; |
| |
| /* |
| * First make sure we're connected. |
| */ |
| ASSERT(self != NULL, return;); |
| ASSERT(self->magic == IRTTY_MAGIC, return;); |
| |
| /* Finished with frame? */ |
| if (self->tx_buff.len > 0) { |
| /* Write data left in transmit buffer */ |
| actual = tty->driver.write(tty, 0, self->tx_buff.data, |
| self->tx_buff.len); |
| |
| self->tx_buff.data += actual; |
| self->tx_buff.len -= actual; |
| |
| self->stats.tx_packets++; |
| } else { |
| /* |
| * Now serial buffer is almost free & we can start |
| * transmission of another packet |
| */ |
| IRDA_DEBUG(5, __FUNCTION__ "(), finished with frame!\n"); |
| |
| tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); |
| |
| 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 { |
| /* Tell network layer that we want more frames */ |
| netif_wake_queue(self->netdev); |
| } |
| } |
| } |
| |
| /* |
| * Function irtty_is_receiving (self) |
| * |
| * Return TRUE is we are currently receiving a frame |
| * |
| */ |
| static int irtty_is_receiving(struct irtty_cb *self) |
| { |
| return (self->rx_buff.state != OUTSIDE_FRAME); |
| } |
| |
| /* |
| * Function irtty_set_dtr_rts (tty, dtr, rts) |
| * |
| * This function can be used by dongles etc. to set or reset the status |
| * of the dtr and rts lines |
| */ |
| static int irtty_set_dtr_rts(struct net_device *dev, int dtr, int rts) |
| { |
| struct irtty_cb *self; |
| struct tty_struct *tty; |
| mm_segment_t fs; |
| int arg = 0; |
| |
| self = (struct irtty_cb *) dev->priv; |
| tty = self->tty; |
| |
| #ifdef TIOCM_OUT2 /* Not defined for ARM */ |
| arg = TIOCM_OUT2; |
| #endif |
| if (rts) |
| arg |= TIOCM_RTS; |
| if (dtr) |
| arg |= TIOCM_DTR; |
| |
| /* |
| * The ioctl() function, or actually set_modem_info() in serial.c |
| * expects a pointer to the argument in user space. To hack us |
| * around this, we use the set_fs() function to fool the routines |
| * that check if they are called from user space. We also need |
| * to send a pointer to the argument so get_user() gets happy. DB. |
| */ |
| |
| fs = get_fs(); |
| set_fs(get_ds()); |
| |
| if (tty->driver.ioctl(tty, NULL, TIOCMSET, (unsigned long) &arg)) { |
| IRDA_DEBUG(2, __FUNCTION__ "(), error doing ioctl!\n"); |
| } |
| set_fs(fs); |
| |
| return 0; |
| } |
| |
| /* |
| * 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 net_device *dev, int mode) |
| { |
| struct irtty_cb *self; |
| |
| 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; |
| |
| /* 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 incoming 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 net_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 net_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;); |
| |
| if (self->tty->driver.write) |
| actual = self->tty->driver.write(self->tty, 0, buf, len); |
| |
| return actual; |
| } |
| |
| static int irtty_net_init(struct net_device *dev) |
| { |
| /* Set up to be a normal IrDA network device driver */ |
| irda_device_setup(dev); |
| |
| /* Insert overrides below this line! */ |
| |
| return 0; |
| } |
| |
| static int irtty_net_open(struct net_device *dev) |
| { |
| struct irtty_cb *self = (struct irtty_cb *) dev->priv; |
| struct tty_struct *tty = self->tty; |
| char hwname[16]; |
| |
| ASSERT(self != NULL, return -1;); |
| ASSERT(self->magic == IRTTY_MAGIC, return -1;); |
| |
| IRDA_DEBUG(0, __FUNCTION__ "()\n"); |
| |
| /* Ready to play! */ |
| netif_start_queue(dev); |
| |
| /* Make sure we can receive more data */ |
| irtty_stop_receiver(self, FALSE); |
| |
| /* Give self a hardware name */ |
| sprintf(hwname, "%s%d", tty->driver.name, |
| minor(tty->device) - tty->driver.minor_start + |
| tty->driver.name_base); |
| |
| /* |
| * Open new IrLAP layer instance, now that everything should be |
| * initialized properly |
| */ |
| self->irlap = irlap_open(dev, &self->qos, hwname); |
| |
| MOD_INC_USE_COUNT; |
| |
| return 0; |
| } |
| |
| static int irtty_net_close(struct net_device *dev) |
| { |
| 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 */ |
| netif_stop_queue(dev); |
| |
| /* 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(3, __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)) |
| ret = -EPERM; |
| else |
| irda_task_execute(self, irtty_change_speed, NULL, NULL, |
| (void *) irq->ifr_baudrate); |
| break; |
| case SIOCSDONGLE: /* Set dongle */ |
| if (!capable(CAP_NET_ADMIN)) { |
| ret = -EPERM; |
| break; |
| } |
| |
| /* 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)) |
| ret = -EPERM; |
| else |
| 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)) |
| ret = -EPERM; |
| else |
| irtty_set_dtr_rts(dev, irq->ifr_dtr, irq->ifr_rts); |
| break; |
| case SIOCSMODE: |
| if (!capable(CAP_NET_ADMIN)) |
| ret = -EPERM; |
| else |
| 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 net_device *dev) |
| { |
| struct irtty_cb *self = (struct irtty_cb *) dev->priv; |
| |
| return &self->stats; |
| } |
| |
| #ifdef MODULE |
| |
| MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>"); |
| MODULE_DESCRIPTION("IrDA TTY device driver"); |
| MODULE_LICENSE("GPL"); |
| |
| |
| MODULE_PARM(qos_mtt_bits, "i"); |
| MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); |
| |
| /* |
| * Function init_module (void) |
| * |
| * Initialize IrTTY module |
| * |
| */ |
| int init_module(void) |
| { |
| return irtty_init(); |
| } |
| |
| /* |
| * Function cleanup_module (void) |
| * |
| * Cleanup IrTTY module |
| * |
| */ |
| void cleanup_module(void) |
| { |
| irtty_cleanup(); |
| } |
| |
| #endif /* MODULE */ |