diff --git a/examples/tty/serial.c b/examples/tty/serial.c index ea0a5a2a08ce09b488fcf0cd5eb78fe53ba2bb62..630ac4430899f29e331452dd1c2ffa2e332991b6 100644 --- a/examples/tty/serial.c +++ b/examples/tty/serial.c @@ -29,6 +29,7 @@ #include <linux/module.h> #include <linux/err.h> +#include <linux/termios.h> #include "../../include/ecrt.h" // EtherCAT realtime interface #include "../../include/ectty.h" // EtherCAT TTY interface @@ -200,12 +201,45 @@ ec_sync_info_t el6002_syncs[] = { /****************************************************************************/ +int el6002_cflag_changed(void *data, unsigned short cflag) +{ + el6002_t *ser = (el6002_t *) data; + int cs = 0, stop, par; + + printk(KERN_INFO PFX "%s(data=%p, cflag=%x).\n", __func__, ser, cflag); + + switch (cflag & CSIZE) { + case CS7: + cs = 7; + break; + case CS8: + cs = 8; + break; + default: /* CS5 or CS6 */ + return -EINVAL; // not supported + } + + stop = (cflag & CSTOPB) ? 2 : 1; + + if (cflag & PARENB) { + par = (cflag & PARODD) ? 1 : 2; + } else { + par = 0; + } + + printk(KERN_INFO PFX "CS%u stopb=%u par=%i.\n", cs, stop, par); + + return 0; +} + +/****************************************************************************/ + int el6002_init(el6002_t *ser, ec_master_t *master, u16 position, ec_domain_t *domain) { int ret = 0; - ser->tty = ectty_create(); + ser->tty = ectty_create(el6002_cflag_changed, ser); if (IS_ERR(ser->tty)) { printk(KERN_ERR PFX "Failed to create tty.\n"); ret = PTR_ERR(ser->tty); diff --git a/include/ectty.h b/include/ectty.h index 250e5d44669aa1fc5f2ef628c7ba77f1ff935197..806eedf7435e1ad0a42678d56ee6e8be01edede3 100644 --- a/include/ectty.h +++ b/include/ectty.h @@ -54,10 +54,19 @@ typedef struct ec_tty ec_tty_t; /**< \see ec_tty */ *****************************************************************************/ /** Create a virtual TTY interface. - * + * + * \param cflag_cb This callback function is called when the serial settings + * shall be changed. The \a cb_data argument is the same as the \a cb_data + * given on device creation, while the \a cflag argument contains the new + * settings. + * \param cb_data Arbitrary data to pass to callbacks. + * * \return Pointer to the interface object, otherwise an ERR_PTR value. */ -ec_tty_t *ectty_create(void); +ec_tty_t *ectty_create( + int (*cflag_cb)(void *cb_data, unsigned short cflag), + void *cb_data + ); /****************************************************************************** * TTY interface methods diff --git a/tty/module.c b/tty/module.c index 900e4390e1220cc9eb31ed535ea400b2b3231c34..4c0fd93c1265792464059a55f83368518062f187 100644 --- a/tty/module.c +++ b/tty/module.c @@ -107,6 +107,9 @@ struct ec_tty { struct timer_list timer; struct tty_struct *tty; + + int (*cflag_cb)(void *, unsigned short); + void *cb_data; }; static const struct tty_operations ec_tty_ops; // see below @@ -242,10 +245,10 @@ void ec_tty_wakeup(unsigned long data) 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; @@ -266,7 +269,7 @@ void ec_tty_wakeup(unsigned long data) } tty_flip_buffer_push(tty->tty); } - } + } tty->timer.expires += 1; add_timer(&tty->timer); @@ -274,7 +277,8 @@ void ec_tty_wakeup(unsigned long data) /*****************************************************************************/ -int ec_tty_init(ec_tty_t *tty, int minor) +int ec_tty_init(ec_tty_t *tty, int minor, + int (*cflag_cb)(void *, unsigned short), void *cb_data) { tty->minor = minor; tty->tx_read_idx = 0; @@ -284,6 +288,8 @@ int ec_tty_init(ec_tty_t *tty, int minor) tty->rx_write_idx = 0; init_timer(&tty->timer); tty->tty = NULL; + tty->cflag_cb = cflag_cb; + tty->cb_data = cb_data; tty->dev = tty_register_device(tty_driver, tty->minor, NULL); if (IS_ERR(tty->dev)) { @@ -310,17 +316,17 @@ void ec_tty_clear(ec_tty_t *tty) int ec_tty_get_serial_info(ec_tty_t *tty, struct serial_struct *data) { - struct serial_struct tmp; - - if (!data) - return -EFAULT; + struct serial_struct tmp; - memset(&tmp, 0, sizeof(tmp)); - - if (copy_to_user(data, &tmp, sizeof(*data))) { + if (!data) + return -EFAULT; + + memset(&tmp, 0, sizeof(tmp)); + + if (copy_to_user(data, &tmp, sizeof(*data))) { return -EFAULT; } - return 0; + return 0; } /****************************************************************************** @@ -336,8 +342,8 @@ static int ec_tty_open(struct tty_struct *tty, struct file *file) 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]; @@ -474,7 +480,7 @@ static void ec_tty_flush_buffer(struct tty_struct *tty) /*****************************************************************************/ 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; @@ -487,8 +493,8 @@ static int ec_tty_ioctl(struct tty_struct *tty, struct file *file, #endif switch (cmd) { - case TIOCGSERIAL: - if (access_ok(VERIFY_WRITE, + 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 { @@ -513,9 +519,10 @@ static int ec_tty_ioctl(struct tty_struct *tty, struct file *file, /*****************************************************************************/ static void ec_tty_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) + struct ktermios *old_termios) { - //ec_tty_t *t = (ec_tty_t *) tty->driver_data; + ec_tty_t *t = (ec_tty_t *) tty->driver_data; + int ret; #if EC_TTY_DEBUG >= 2 printk(KERN_INFO PFX "%s().\n", __func__); @@ -528,6 +535,13 @@ static void ec_tty_set_termios(struct tty_struct *tty, printk(KERN_INFO "cflag changed from %x to %x.\n", old_termios->c_cflag, tty->termios->c_cflag); #endif + + ret = t->cflag_cb(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; + } } /*****************************************************************************/ @@ -598,25 +612,25 @@ 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, - .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, + .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(int (*cflag_cb)(void *, unsigned short), void *cb_data) { ec_tty_t *tty; int minor, ret; @@ -636,7 +650,7 @@ ec_tty_t *ectty_create(void) return ERR_PTR(-ENOMEM); } - ret = ec_tty_init(tty, minor); + ret = ec_tty_init(tty, minor, cflag_cb, cb_data); if (ret) { up(&tty_sem); kfree(tty);