diff -r f228415225b7 -r 5bfbb7be5400 tty/module.c --- a/tty/module.c Thu Jan 21 11:10:22 2010 +0100 +++ b/tty/module.c Sun Jan 31 14:50:37 2010 +0100 @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include "../master/globals.h" #include "../include/ectty.h" @@ -49,7 +51,7 @@ #define PFX "ec_tty: " -#define EC_TTY_MAX_DEVICES 10 +#define EC_TTY_MAX_DEVICES 32 #define EC_TTY_TX_BUFFER_SIZE 100 #define EC_TTY_RX_BUFFER_SIZE 100 @@ -64,6 +66,8 @@ ec_tty_t *ttys[EC_TTY_MAX_DEVICES]; struct semaphore tty_sem; +void ec_tty_wakeup(unsigned long); + /*****************************************************************************/ /** \cond */ @@ -78,11 +82,15 @@ /** \endcond */ +/** Standard termios for ec_tty devices. + * + * Simplest possible configuration, as you would expect. + */ static struct ktermios ec_tty_std_termios = { - .c_iflag = ICRNL | IXON, - .c_oflag = OPOST, - .c_cflag = B38400 | CS8 | CREAD | HUPCL, - .c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN, + .c_iflag = 0, + .c_oflag = 0, + .c_cflag = B9600 | CS8 | CREAD, + .c_lflag = 0, .c_cc = INIT_C_CC, }; @@ -101,6 +109,9 @@ struct timer_list timer; struct tty_struct *tty; + + ec_tty_operations_t ops; + void *cb_data; }; static const struct tty_operations ec_tty_ops; // see below @@ -139,7 +150,6 @@ tty_driver->subtype = SERIAL_TYPE_NORMAL; tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; tty_driver->init_termios = ec_tty_std_termios; - tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; tty_set_operations(tty_driver, &ec_tty_ops); ret = tty_register_driver(tty_driver); @@ -169,6 +179,65 @@ printk(KERN_INFO PFX "Module unloading.\n"); } +/****************************************************************************** + * ec_tty_t methods. + *****************************************************************************/ + +int ec_tty_init(ec_tty_t *t, int minor, + const ec_tty_operations_t *ops, void *cb_data) +{ + int ret; + tcflag_t cflag; + struct tty_struct *tty; + + t->minor = minor; + t->tx_read_idx = 0; + t->tx_write_idx = 0; + t->wakeup = 0; + t->rx_read_idx = 0; + t->rx_write_idx = 0; + init_timer(&t->timer); + t->tty = NULL; + t->ops = *ops; + t->cb_data = cb_data; + + t->dev = tty_register_device(tty_driver, t->minor, NULL); + if (IS_ERR(t->dev)) { + printk(KERN_ERR PFX "Failed to register tty device.\n"); + return PTR_ERR(t->dev); + } + + // Tell the device-specific implementation about the initial cflags + tty = tty_driver->ttys[minor]; + + if (tty && tty->termios) { // already opened before + cflag = tty->termios->c_cflag; + } else { + cflag = tty_driver->init_termios.c_cflag; + } + ret = t->ops.cflag_changed(t->cb_data, cflag); + if (ret) { + printk(KERN_ERR PFX "ERROR: Initial cflag 0x%x not accepted.\n", + cflag); + tty_unregister_device(tty_driver, t->minor); + return ret; + } + + t->timer.function = ec_tty_wakeup; + t->timer.data = (unsigned long) t; + t->timer.expires = jiffies + 10; + add_timer(&t->timer); + return 0; +} + +/*****************************************************************************/ + +void ec_tty_clear(ec_tty_t *tty) +{ + del_timer_sync(&tty->timer); + tty_unregister_device(tty_driver, tty->minor); +} + /*****************************************************************************/ unsigned int ec_tty_tx_size(ec_tty_t *tty) @@ -215,6 +284,25 @@ /*****************************************************************************/ +int ec_tty_get_serial_info(ec_tty_t *tty, struct serial_struct *data) +{ + struct serial_struct tmp; + + if (!data) + return -EFAULT; + + memset(&tmp, 0, sizeof(tmp)); + + if (copy_to_user(data, &tmp, sizeof(*data))) { + return -EFAULT; + } + return 0; +} + +/*****************************************************************************/ + +/** Timer function. + */ void ec_tty_wakeup(unsigned long data) { ec_tty_t *tty = (ec_tty_t *) data; @@ -237,10 +325,10 @@ unsigned char *cbuf; int space = tty_prepare_flip_string(tty->tty, &cbuf, to_recv); - if (space < to_recv) { - printk(KERN_WARNING PFX "Insufficient space to_recv=%d space=%d\n", + if (space < to_recv) { + printk(KERN_WARNING PFX "Insufficient space to_recv=%d space=%d\n", to_recv, space); - } + } if (space < 0) { to_recv = 0; @@ -257,50 +345,17 @@ for (i = 0; i < to_recv; i++) { cbuf[i] = tty->rx_buffer[tty->rx_read_idx]; - tty->rx_read_idx = (tty->rx_read_idx + 1) % EC_TTY_RX_BUFFER_SIZE; + tty->rx_read_idx = + (tty->rx_read_idx + 1) % EC_TTY_RX_BUFFER_SIZE; } tty_flip_buffer_push(tty->tty); } - } + } tty->timer.expires += 1; add_timer(&tty->timer); } -/*****************************************************************************/ - -int ec_tty_init(ec_tty_t *tty, int minor) -{ - tty->minor = minor; - tty->tx_read_idx = 0; - tty->tx_write_idx = 0; - tty->wakeup = 0; - tty->rx_read_idx = 0; - tty->rx_write_idx = 0; - init_timer(&tty->timer); - tty->tty = NULL; - - tty->dev = tty_register_device(tty_driver, tty->minor, NULL); - if (IS_ERR(tty->dev)) { - printk(KERN_ERR PFX "Failed to register tty device.\n"); - return PTR_ERR(tty->dev); - } - - tty->timer.function = ec_tty_wakeup; - tty->timer.data = (unsigned long) tty; - tty->timer.expires = jiffies + 10; - add_timer(&tty->timer); - return 0; -} - -/*****************************************************************************/ - -void ec_tty_clear(ec_tty_t *tty) -{ - del_timer_sync(&tty->timer); - tty_unregister_device(tty_driver, tty->minor); -} - /****************************************************************************** * Device callbacks *****************************************************************************/ @@ -314,8 +369,8 @@ printk(KERN_INFO PFX "Opening line %i.\n", line); #endif - if (line < 0 || line >= EC_TTY_MAX_DEVICES) { - return -ENXIO; + if (line < 0 || line >= EC_TTY_MAX_DEVICES) { + return -ENXIO; } t = ttys[line]; @@ -447,45 +502,75 @@ #if EC_TTY_DEBUG >= 2 printk(KERN_INFO PFX "%s().\n", __func__); #endif + + // FIXME empty ring buffer } /*****************************************************************************/ static int ec_tty_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ + unsigned int cmd, unsigned long arg) +{ + ec_tty_t *t = (ec_tty_t *) tty->driver_data; + int ret = -ENOTTY; + +#if EC_TTY_DEBUG >= 2 + printk(KERN_INFO PFX "%s(tty=%p, file=%p, cmd=%08x, arg=%08lx).\n", + __func__, tty, file, cmd, arg); + printk(KERN_INFO PFX "decoded: type=%02x nr=%u\n", + _IOC_TYPE(cmd), _IOC_NR(cmd)); +#endif + + switch (cmd) { + case TIOCGSERIAL: + if (access_ok(VERIFY_WRITE, + (void *) arg, sizeof(struct serial_struct))) { + ret = ec_tty_get_serial_info(t, (struct serial_struct *) arg); + } else { + ret = -EFAULT; + } + break; + + case TIOCSSERIAL: // TODO + break; + + default: +#if EC_TTY_DEBUG >= 2 + printk(KERN_INFO PFX "no ioctl() -> handled by tty core!\n"); +#endif + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +/*****************************************************************************/ + +static void ec_tty_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) +{ + ec_tty_t *t = (ec_tty_t *) tty->driver_data; + int ret; + #if EC_TTY_DEBUG >= 2 printk(KERN_INFO PFX "%s().\n", __func__); #endif - return -ENOTTY; -} - -/*****************************************************************************/ - -static void ec_tty_throttle(struct tty_struct *tty) -{ -#if EC_TTY_DEBUG >= 2 - printk(KERN_INFO PFX "%s().\n", __func__); -#endif -} - -/*****************************************************************************/ - -static void ec_tty_unthrottle(struct tty_struct *tty) -{ -#if EC_TTY_DEBUG >= 2 - printk(KERN_INFO PFX "%s().\n", __func__); -#endif -} - -/*****************************************************************************/ - -static void ec_tty_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ -#if EC_TTY_DEBUG >= 2 - printk(KERN_INFO PFX "%s().\n", __func__); -#endif + + if (tty->termios->c_cflag == old_termios->c_cflag) + return; + +#if EC_TTY_DEBUG >= 2 + printk(KERN_INFO "cflag changed from %x to %x.\n", + old_termios->c_cflag, tty->termios->c_cflag); +#endif + + ret = t->ops.cflag_changed(t->cb_data, tty->termios->c_cflag); + if (ret) { + printk(KERN_ERR PFX "ERROR: cflag 0x%x not accepted.\n", + tty->termios->c_cflag); + tty->termios->c_cflag = old_termios->c_cflag; + } } /*****************************************************************************/ @@ -552,54 +637,29 @@ /*****************************************************************************/ -static int ec_tty_tiocmget(struct tty_struct *tty, struct file *file) -{ -#if EC_TTY_DEBUG >= 2 - printk(KERN_INFO PFX "%s().\n", __func__); -#endif - return -EBUSY; -} - -/*****************************************************************************/ - -static int ec_tty_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ -#if EC_TTY_DEBUG >= 2 - printk(KERN_INFO PFX "%s(set=%u, clear=%u).\n", __func__, set, clear); -#endif - return -EBUSY; -} - -/*****************************************************************************/ - static const struct tty_operations ec_tty_ops = { .open = ec_tty_open, .close = ec_tty_close, .write = ec_tty_write, - .put_char = ec_tty_put_char, - .write_room = ec_tty_write_room, - .chars_in_buffer = ec_tty_chars_in_buffer, - .flush_buffer = ec_tty_flush_buffer, - .ioctl = ec_tty_ioctl, - .throttle = ec_tty_throttle, - .unthrottle = ec_tty_unthrottle, - .set_termios = ec_tty_set_termios, - .stop = ec_tty_stop, - .start = ec_tty_start, - .hangup = ec_tty_hangup, - .break_ctl = ec_tty_break, - .send_xchar = ec_tty_send_xchar, - .wait_until_sent = ec_tty_wait_until_sent, - .tiocmget = ec_tty_tiocmget, - .tiocmset = ec_tty_tiocmset, + .put_char = ec_tty_put_char, + .write_room = ec_tty_write_room, + .chars_in_buffer = ec_tty_chars_in_buffer, + .flush_buffer = ec_tty_flush_buffer, + .ioctl = ec_tty_ioctl, + .set_termios = ec_tty_set_termios, + .stop = ec_tty_stop, + .start = ec_tty_start, + .hangup = ec_tty_hangup, + .break_ctl = ec_tty_break, + .send_xchar = ec_tty_send_xchar, + .wait_until_sent = ec_tty_wait_until_sent, }; /****************************************************************************** * Public functions and methods *****************************************************************************/ -ec_tty_t *ectty_create(void) +ec_tty_t *ectty_create(const ec_tty_operations_t *ops, void *cb_data) { ec_tty_t *tty; int minor, ret; @@ -619,7 +679,7 @@ return ERR_PTR(-ENOMEM); } - ret = ec_tty_init(tty, minor); + ret = ec_tty_init(tty, minor, ops, cb_data); if (ret) { up(&tty_sem); kfree(tty); @@ -693,7 +753,8 @@ for (i = 0; i < size; i++) { tty->rx_buffer[tty->rx_write_idx] = buffer[i]; - tty->rx_write_idx = (tty->rx_write_idx + 1) % EC_TTY_RX_BUFFER_SIZE; + tty->rx_write_idx = + (tty->rx_write_idx + 1) % EC_TTY_RX_BUFFER_SIZE; } } }