X-Git-Url: http://rtime.felk.cvut.cz/gitweb/linux-lin.git/blobdiff_plain/aa1a6709bcec3aa70a1197cd314470d655068d4c..b56f1ea7a09bf50fc5f115a0c2e148e17d9e4799:/sllin/sllin.c diff --git a/sllin/sllin.c b/sllin/sllin.c index 91a18c7..cdc4327 100644 --- a/sllin/sllin.c +++ b/sllin/sllin.c @@ -72,6 +72,7 @@ /* Should be in include/linux/tty.h */ #define N_SLLIN 25 +#define N_SLLIN_SLAVE 26 /* -------------------------------- */ #ifdef SLLIN_LED_TRIGGER @@ -104,11 +105,18 @@ MODULE_AUTHOR("Pavel Pisa "); static bool master = true; static int baudrate; /* Use LIN_DEFAULT_BAUDRATE when not set */ +#ifndef BREAK_BY_BAUD +static bool break_by_baud = false; +#else /*BREAK_BY_BAUD*/ +static bool break_by_baud = true; +#endif /*BREAK_BY_BAUD*/ module_param(master, bool, 0444); MODULE_PARM_DESC(master, "LIN interface is Master device"); module_param(baudrate, int, 0444); MODULE_PARM_DESC(baudrate, "Baudrate of LIN interface"); +module_param(break_by_baud, bool, 0444); +MODULE_PARM_DESC(break_by_baud, "Break is sent by temporal baudrate switching"); static int maxdev = 10; /* MAX number of SLLIN channels; This can be overridden with @@ -164,6 +172,7 @@ struct sllin { int tx_lim; /* actual limit of bytes to Tx */ int tx_cnt; /* number of already Tx bytes */ char lin_master; /* node is a master node */ + bool lin_break_by_baud; /* send break character by baudrate switching */ int lin_baud; /* LIN baudrate */ int lin_state; /* state */ char id_to_send; /* there is ID to be sent */ @@ -661,13 +670,14 @@ static void sllin_master_receive_buf(struct tty_struct *tty, } } -#ifndef BREAK_BY_BAUD - /* We didn't receive Break character -- fake it! */ - if ((sl->rx_cnt == SLLIN_BUFF_BREAK) && (*cp == 0x55)) { - netdev_dbg(sl->dev, "LIN_RX[%d]: 0x00\n", sl->rx_cnt); - sl->rx_buff[sl->rx_cnt++] = 0x00; + if (!sl->lin_break_by_baud ) { + /* We didn't receive Break character -- fake it! */ + if ((sl->rx_cnt == SLLIN_BUFF_BREAK) && (*cp == 0x55)) { + netdev_dbg(sl->dev, "LIN_RX[%d]: 0x00\n", + sl->rx_cnt); + sl->rx_buff[sl->rx_cnt++] = 0x00; + } } -#endif /* BREAK_BY_BAUD */ if (sl->rx_cnt < SLLIN_BUFF_LEN) { netdev_dbg(sl->dev, "LIN_RX[%d]: 0x%02x\n", sl->rx_cnt, *cp); @@ -1025,14 +1035,14 @@ static int sllin_send_tx_buff(struct sllin *sl) smp_mb__after_atomic(); #endif -#ifdef BREAK_BY_BAUD - if (sl->lin_state != SLSTATE_BREAK_SENT) + if (sl->lin_break_by_baud) { + if (sl->lin_state != SLSTATE_BREAK_SENT) + remains = sl->tx_lim - sl->tx_cnt; + else + remains = 1; + } else { remains = sl->tx_lim - sl->tx_cnt; - else - remains = 1; -#else - remains = sl->tx_lim - sl->tx_cnt; -#endif + } res = tty->ops->write(tty, sl->tx_buff + sl->tx_cnt, remains); if (res < 0) @@ -1077,8 +1087,7 @@ error_in_write: } -#ifdef BREAK_BY_BAUD -static int sllin_send_break(struct sllin *sl) +static int sllin_send_break_by_baud(struct sllin *sl) { struct tty_struct *tty = sl->tty; unsigned long break_baud; @@ -1087,7 +1096,7 @@ static int sllin_send_break(struct sllin *sl) break_baud = ((sl->lin_baud * 2) / 3); sltty_change_speed(tty, break_baud); - if (tty != NULL && tty->ops != NULL && tty->ops->flush_buffer != NULL) { + if (tty->ops->flush_buffer != NULL) { tty->ops->flush_buffer(tty); } else { netdev_dbg(sl->dev, "flush_buffer is not implemented.\n"); @@ -1105,9 +1114,8 @@ static int sllin_send_break(struct sllin *sl) return 0; } -#else /* BREAK_BY_BAUD */ -static int sllin_send_break(struct sllin *sl) +static int sllin_send_break_by_usleep(struct sllin *sl) { struct tty_struct *tty = sl->tty; int retval; @@ -1150,8 +1158,14 @@ static int sllin_send_break(struct sllin *sl) return 0; } -#endif /* BREAK_BY_BAUD */ +static int sllin_send_break(struct sllin *sl) +{ + if (sl->lin_break_by_baud) + return sllin_send_break_by_baud(sl); + else + return sllin_send_break_by_usleep(sl); +} static enum hrtimer_restart sllin_rx_timeout_handler(struct hrtimer *hrtimer) { @@ -1331,12 +1345,12 @@ static int sllin_kwthread(void *ptr) break; case SLSTATE_BREAK_SENT: -#ifdef BREAK_BY_BAUD - if (sl->rx_cnt <= SLLIN_BUFF_BREAK) - continue; + if (sl->lin_break_by_baud) { + if (sl->rx_cnt <= SLLIN_BUFF_BREAK) + continue; - res = sltty_change_speed(tty, sl->lin_baud); -#endif + sltty_change_speed(tty, sl->lin_baud); + } sl->lin_state = SLSTATE_ID_SENT; sllin_send_tx_buff(sl); @@ -1584,7 +1598,7 @@ static struct sllin *sll_alloc(dev_t line) * Called in process context serialized from other ldisc calls. */ -static int sllin_open(struct tty_struct *tty) +static int sllin_open_common(struct tty_struct *tty, bool setup_master) { struct sllin *sl; int err; @@ -1625,8 +1639,8 @@ static int sllin_open(struct tty_struct *tty) if (!test_bit(SLF_INUSE, &sl->flags)) { /* Perform the low-level SLLIN initialization. */ - sl->lin_master = master; - if (master) + sl->lin_master = setup_master; + if (sl->lin_master) pr_debug("sllin: Configured as MASTER\n"); else pr_debug("sllin: Configured as SLAVE\n"); @@ -1636,6 +1650,13 @@ static int sllin_open(struct tty_struct *tty) sl->lin_baud = (baudrate == 0) ? LIN_DEFAULT_BAUDRATE : baudrate; pr_debug("sllin: Baudrate set to %u\n", sl->lin_baud); + sl->lin_break_by_baud = break_by_baud; + + if (tty->ops->break_ctl == NULL && !sl->lin_break_by_baud) { + sl->lin_break_by_baud = true; + pr_notice ("sllin: break_ctl, switching to break_by_baud\n"); + } + sl->lin_state = SLSTATE_IDLE; hrtimer_init(&sl->rx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); @@ -1684,6 +1705,43 @@ err_exit: return err; } +/* + * Open the high-level part of the SLLIN channel. + * This function is called by the TTY module when the + * SLLIN line discipline is called for. Because we are + * sure the tty line exists, we only have to link it to + * a free SLLIN channel... + * + * Called in process context serialized from other ldisc calls. + * + * This call variant preserves original functionality where + * slave/master can be selected by module parameter. + * Default is the master. + */ + +static int sllin_open(struct tty_struct *tty) +{ + return sllin_open_common(tty, master); +} + +/* + * Open the high-level part of the SLLIN channel. + * This function is called by the TTY module when the + * SLLIN line discipline is called for. Because we are + * sure the tty line exists, we only have to link it to + * a free SLLIN channel... + * + * Called in process context serialized from other ldisc calls. + * + * This call variant enforces slave/LIN device functionality. + */ + +static int sllin_open_slave(struct tty_struct *tty) +{ + return sllin_open_common(tty, false); +} + + /* * Close down a SLLIN channel. * This means flushing out any pending queues, and then returning. This @@ -1755,6 +1813,18 @@ static struct tty_ldisc_ops sll_ldisc = { .write_wakeup = sllin_write_wakeup, }; +static struct tty_ldisc_ops sll_slave_ldisc = { + .owner = THIS_MODULE, + .magic = TTY_LDISC_MAGIC, + .name = "sllin-slave", + .open = sllin_open_slave, + .close = sllin_close, + .hangup = sllin_hangup, + .ioctl = sllin_ioctl, + .receive_buf = sllin_receive_buf, + .write_wakeup = sllin_write_wakeup, +}; + static int __init sllin_init(void) { int status; @@ -1782,13 +1852,20 @@ static int __init sllin_init(void) if (status) { pr_err("sllin: can't register line discipline\n"); kfree(sllin_devs); + } else { + status = tty_register_ldisc(N_SLLIN_SLAVE, &sll_slave_ldisc); + if (status) { + tty_unregister_ldisc(N_SLLIN); + pr_err("sllin: can't register slave line discipline\n"); + kfree(sllin_devs); + } } -#ifdef BREAK_BY_BAUD - pr_debug("sllin: Break is generated by baud-rate change."); -#else - pr_debug("sllin: Break is generated manually with tiny sleep."); -#endif + + if (break_by_baud) + pr_debug("sllin: Break is generated by baud-rate change."); + else + pr_debug("sllin: Break is generated manually with tiny sleep."); return status; } @@ -1855,6 +1932,11 @@ static void __exit sllin_exit(void) if (i) pr_err("sllin: can't unregister ldisc (err %d)\n", i); + i = tty_unregister_ldisc(N_SLLIN_SLAVE); + if (i) + pr_err("sllin: can't unregister slave ldisc (err %d)\n", i); + + #ifdef SLLIN_LED_TRIGGER unregister_netdevice_notifier(&sllin_netdev_notifier); #endif