/********************************************************************* * * Filename: irtty.c * Version: 1.1 * Description: IrDA line discipline implementation * Status: Experimental. * Author: Dag Brattli * Created at: Tue Dec 9 21:18:38 1997 * Modified at: Thu Apr 22 09:20:24 1999 * Modified by: Dag Brattli * Sources: slip.c by Laurence Culhane, * Fred N. van Kempen, * * Copyright (c) 1998 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 #include #include #include #include #include #include #include #include #include #include #include #include #include static hashbin_t *irtty = NULL; static hashbin_t *dongles = NULL; static struct tty_ldisc irda_ldisc; 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 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_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); char *driver_name = "irtty"; __initfunc(int 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; } dongles = hashbin_new(HB_LOCAL); if (dongles == NULL) { printk(KERN_WARNING "IrDA: Can't allocate dongles 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) { printk(KERN_ERR "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); hashbin_delete(dongles, 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 irtty_cb *self; char name[16]; 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, (QUEUE*) self, 0, self->name); */ hashbin_insert(irtty, (QUEUE*) 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; /* * Initialize driver */ self->idev.rx_buff.state = OUTSIDE_FRAME; /* * 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->idev.qos); /* The only value we must override it the baudrate */ self->idev.qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| IR_115200; self->idev.qos.min_turn_time.bits = 0x0f; 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; /* Specify how much memory we want */ self->idev.rx_buff.truesize = 4000; self->idev.tx_buff.truesize = 4000; /* Initialize callbacks */ self->idev.change_speed = irtty_change_speed; self->idev.is_receiving = irtty_is_receiving; /* self->idev.is_tbusy = irtty_is_tbusy; */ self->idev.wait_until_sent = irtty_wait_until_sent; /* 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; /* Open the IrDA device */ irda_device_open(&self->idev, name, self); 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;); /* We are not using any dongle anymore! */ if (self->dongle_q) self->dongle_q->dongle->close(&self->idev); /* Remove driver */ irda_device_close(&self->idev); /* Stop tty */ tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); tty->disc_data = 0; self->tty = NULL; self->magic = 0; self = hashbin_remove(irtty, (int) self, NULL); if (self != NULL) kfree(self); MOD_DEC_USE_COUNT; } /* * Function irtty_stop_receiver (irda_device, stop) * * * */ static void irtty_stop_receiver(struct irda_device *idev, 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; if (stop) cflag &= ~CREAD; else cflag |= CREAD; self->tty->termios->c_cflag = cflag; self->tty->driver.set_termios(self->tty, &old_termios); } /* * Function irtty_change_speed (self, baud) * * Change the speed of the serial port. The driver layer must check that * all transmission has finished using the irtty_wait_until_sent() * function. */ static void irtty_change_speed(struct irda_device *idev, int baud) { 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;); old_termios = *(self->tty->termios); cflag = self->tty->termios->c_cflag; cflag &= ~CBAUD; DEBUG(4, __FUNCTION__ "(), Setting speed to %d\n", baud); switch (baud) { 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); } /* * Function irtty_init_dongle (self, type) * * Initialize attached dongle. Warning, must be called with a process * context! */ static void irtty_init_dongle(struct irtty_cb *self, int type) { struct dongle_q *node; ASSERT(self != NULL, return;); ASSERT(self->magic == IRTTY_MAGIC, return;); #ifdef CONFIG_KMOD /* Try to load the module needed */ switch( type) { case ESI_DONGLE: MESSAGE("IrDA: Trying to initialize ESI dongle!\n"); request_module("esi"); break; case TEKRAM_DONGLE: MESSAGE("IrDA: Trying to initialize Tekram dongle!\n"); request_module("tekram"); break; case ACTISYS_DONGLE: /* FALLTHROUGH */ case ACTISYS_PLUS_DONGLE: MESSAGE("IrDA: Trying to initialize ACTiSYS dongle!\n"); request_module("actisys"); break; case GIRBIL_DONGLE: MESSAGE("IrDA: Trying to initialize GIrBIL dongle!\n"); request_module("girbil"); break; default: ERROR("Unknown dongle type!\n"); return; } #endif /* CONFIG_KMOD */ node = hashbin_find(dongles, type, NULL); if ( !node) { ERROR("Unable to find requested dongle\n"); return; } self->dongle_q = node; /* Use this change speed function instead of the default */ self->idev.change_speed = node->dongle->change_speed; /* * Now initialize the dongle! */ node->dongle->open(&self->idev, type); node->dongle->qos_init(&self->idev, &self->idev.qos); /* Reset dongle */ node->dongle->reset(&self->idev, 0); /* Set to default baudrate */ node->dongle->change_speed(&self->idev, 9600); } /* * 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) { struct irtty_cb *self; int err = 0; int size = _IOC_SIZE(cmd); 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 */ irtty_init_dongle(self, (int) arg); 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; ASSERT(self != NULL, return;); ASSERT(self->magic == IRTTY_MAGIC, 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); cp++; continue; } /* Unwrap and destuff one byte */ async_unwrap_char(&self->idev, *cp++); } } /* * Function irtty_hard_xmit (skb, dev) * * Transmit frame * */ static int irtty_hard_xmit(struct sk_buff *skb, struct device *dev) { struct irtty_cb *self; struct irda_device *idev; int actual = 0; 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; ASSERT(self != NULL, return 0;); ASSERT(self->magic == IRTTY_MAGIC, return 0;); /* Lock transmit buffer */ if (irda_lock((void *) &dev->tbusy) == FALSE) return -EBUSY; /* Init tx buffer*/ idev->tx_buff.data = idev->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->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); dev->trans_start = jiffies; if (self->tty->driver.write) actual = self->tty->driver.write(self->tty, 0, idev->tx_buff.data, idev->tx_buff.len); /* Hide the part we just transmitted */ idev->tx_buff.data += actual; idev->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; } /* * 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) { 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; struct irda_device *idev; int actual = 0; /* * First make sure we're connected. */ ASSERT(self != NULL, return;); ASSERT(self->magic == IRTTY_MAGIC, return;); idev = &self->idev; /* Finished with frame? */ if (idev->tx_buff.len > 0) { /* Write data left in transmit buffer */ actual = tty->driver.write(tty, 0, idev->tx_buff.data, idev->tx_buff.len); idev->tx_buff.data += actual; idev->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"); tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); idev->netdev.tbusy = 0; /* Unlock */ /* Tell network layer that we want more frames */ mark_bh(NET_BH); } } /* * Function irtty_is_receiving (idev) * * 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) { 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); } int irtty_register_dongle(struct dongle *dongle) { struct dongle_q *new; /* Check if this compressor has been registred before */ if ( hashbin_find ( dongles, dongle->type, NULL)) { DEBUG( 0, __FUNCTION__ "(), Dongle already registered\n"); return 0; } /* Make new IrDA dongle */ new = (struct dongle_q *) kmalloc(sizeof(struct dongle_q), GFP_KERNEL); if (new == NULL) return -1; memset(new, 0, sizeof( struct dongle_q)); new->dongle = dongle; /* Insert IrDA dongle into hashbin */ hashbin_insert(dongles, (QUEUE *) new, dongle->type, NULL); return 0; } void irtty_unregister_dongle(struct dongle *dongle) { struct dongle_q *node; node = hashbin_remove(dongles, dongle->type, NULL); if (!node) { ERROR(__FUNCTION__ "(), dongle not found!\n"); return; } kfree(node); } /* * 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 */ void irtty_set_dtr_rts(struct tty_struct *tty, int dtr, int rts) { mm_segment_t fs; int arg = 0; #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)) { ERROR(__FUNCTION__ "(), error doing ioctl!\n"); } set_fs(fs); } static int irtty_net_init(struct 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 device *dev) { ASSERT(dev != NULL, return -1;); /* Ready to play! */ dev->tbusy = 0; dev->interrupt = 0; dev->start = 1; MOD_INC_USE_COUNT; return 0; } static int irtty_net_close(struct device *dev) { ASSERT(dev != NULL, return -1;); /* Stop device */ dev->tbusy = 1; dev->start = 0; MOD_DEC_USE_COUNT; return 0; } #ifdef MODULE MODULE_AUTHOR("Dag Brattli "); MODULE_DESCRIPTION("IrDA TTY device driver"); /* * 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 */