X-Git-Url: http://rtime.felk.cvut.cz/gitweb/linux-lin.git/blobdiff_plain/12611a51fc163174a95c5d1df02edf7a85a17f04..49dbbd416332c2d82f361d9197332c6f76d8b55b:/sllin/sllin.c diff --git a/sllin/sllin.c b/sllin/sllin.c index ffbdf01..ab4a1cc 100644 --- a/sllin/sllin.c +++ b/sllin/sllin.c @@ -41,7 +41,8 @@ * Funded by: Volkswagen Group Research */ -#define DEBUG 1 /* Enables pr_debug() printouts */ +//#define DEBUG 1 /* Enables pr_debug() printouts */ +//#define SLLIN_LED_TRIGGER /* Enables led triggers */ #include #include @@ -69,6 +70,23 @@ #define N_SLLIN 25 /* -------------------------------- */ +#ifdef SLLIN_LED_TRIGGER +#define SLLIN_LED_NAME_SZ (IFNAMSIZ + 6) +#include + +enum sllin_led_event { + SLLIN_LED_EVENT_OPEN, + SLLIN_LED_EVENT_STOP, + SLLIN_LED_EVENT_TX, + SLLIN_LED_EVENT_RX +}; + +static unsigned long led_delay = 50; +module_param(led_delay, ulong, 0644); +MODULE_PARM_DESC(led_delay, + "blink delay time for activity leds (msecs, default: 50)."); +#endif /* SLLIN_LED_TRIGGER */ + static __initdata const char banner[] = KERN_INFO "sllin: serial line LIN interface driver\n"; @@ -83,15 +101,15 @@ MODULE_AUTHOR("Pavel Pisa "); static bool master = true; static int baudrate; /* Use LIN_DEFAULT_BAUDRATE when not set */ -module_param(master, bool, 0); +module_param(master, bool, 0444); MODULE_PARM_DESC(master, "LIN interface is Master device"); -module_param(baudrate, int, 0); +module_param(baudrate, int, 0444); MODULE_PARM_DESC(baudrate, "Baudrate of LIN interface"); static int maxdev = 10; /* MAX number of SLLIN channels; This can be overridden with insmod sllin.ko maxdev=nnn */ -module_param(maxdev, int, 0); +module_param(maxdev, int, 0444); MODULE_PARM_DESC(maxdev, "Maximum number of sllin interfaces"); /* maximum buffer len to store whole LIN message*/ @@ -173,6 +191,15 @@ struct sllin { /* List with configurations for each of 0 to LIN_ID_MAX LIN IDs */ struct sllin_conf_entry linfr_cache[LIN_ID_MAX + 1]; spinlock_t linfr_lock; /* frame cache and buffers lock */ + +#ifdef SLLIN_LED_TRIGGER + struct led_trigger *tx_led_trig; + char tx_led_trig_name[SLLIN_LED_NAME_SZ]; + struct led_trigger *rx_led_trig; + char rx_led_trig_name[SLLIN_LED_NAME_SZ]; + struct led_trigger *rxtx_led_trig; + char rxtx_led_trig_name[SLLIN_LED_NAME_SZ]; +#endif }; static struct net_device **sllin_devs; @@ -196,6 +223,124 @@ const unsigned char sllin_id_parity_table[] = { 0x40, 0x00, 0x80, 0xc0, 0x00, 0x40, 0xc0, 0x80 }; +#ifdef SLLIN_LED_TRIGGER +static void sllin_led_event(struct net_device *netdev, enum sllin_led_event event) +{ + struct sllin *sl = netdev_priv(netdev); + + switch (event) { + case SLLIN_LED_EVENT_OPEN: + led_trigger_event(sl->tx_led_trig, LED_FULL); + led_trigger_event(sl->rx_led_trig, LED_FULL); + led_trigger_event(sl->rxtx_led_trig, LED_FULL); + break; + case SLLIN_LED_EVENT_STOP: + led_trigger_event(sl->tx_led_trig, LED_OFF); + led_trigger_event(sl->rx_led_trig, LED_OFF); + led_trigger_event(sl->rxtx_led_trig, LED_OFF); + break; + case SLLIN_LED_EVENT_TX: + if (led_delay) { + led_trigger_blink_oneshot(sl->tx_led_trig, + &led_delay, &led_delay, 1); + led_trigger_blink_oneshot(sl->rxtx_led_trig, + &led_delay, &led_delay, 1); + } + break; + case SLLIN_LED_EVENT_RX: + if (led_delay) { + led_trigger_blink_oneshot(sl->rx_led_trig, + &led_delay, &led_delay, 1); + led_trigger_blink_oneshot(sl->rxtx_led_trig, + &led_delay, &led_delay, 1); + } + break; + } +} + +static void sllin_led_release(struct device *gendev, void *res) +{ + struct sllin *sl = netdev_priv(to_net_dev(gendev)); + + led_trigger_unregister_simple(sl->tx_led_trig); + led_trigger_unregister_simple(sl->rx_led_trig); + led_trigger_unregister_simple(sl->rxtx_led_trig); +} + +static void devm_sllin_led_init(struct net_device *netdev) +{ + struct sllin *sl = netdev_priv(netdev); + void *res; + + res = devres_alloc(sllin_led_release, 0, GFP_KERNEL); + if (!res) { + netdev_err(netdev, "cannot register LED triggers\n"); + return; + } + + snprintf(sl->tx_led_trig_name, sizeof(sl->tx_led_trig_name), + "%s-tx", netdev->name); + snprintf(sl->rx_led_trig_name, sizeof(sl->rx_led_trig_name), + "%s-rx", netdev->name); + snprintf(sl->rxtx_led_trig_name, sizeof(sl->rxtx_led_trig_name), + "%s-rxtx", netdev->name); + + led_trigger_register_simple(sl->tx_led_trig_name, + &sl->tx_led_trig); + led_trigger_register_simple(sl->rx_led_trig_name, + &sl->rx_led_trig); + led_trigger_register_simple(sl->rxtx_led_trig_name, + &sl->rxtx_led_trig); + + devres_add(&netdev->dev, res); +} + +static struct sllin *netdev_priv_safe(struct net_device *dev) +{ + int i; + + if (sllin_devs == NULL) + return NULL; + + for (i = 0; i < maxdev; ++i) + if (sllin_devs[i] == dev) + return netdev_priv(dev); + + return NULL; +} + +static int sllin_netdev_notifier_call(struct notifier_block *nb, unsigned long msg, + void *ptr) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); + struct sllin *sl = netdev_priv_safe(netdev); + char name[SLLIN_LED_NAME_SZ]; + + if (!sl) + return NOTIFY_DONE; + + if (!sl->tx_led_trig || !sl->rx_led_trig || !sl->rxtx_led_trig) + return NOTIFY_DONE; + + if (msg == NETDEV_CHANGENAME) { + snprintf(name, sizeof(name), "%s-tx", netdev->name); + led_trigger_rename_static(name, sl->tx_led_trig); + + snprintf(name, sizeof(name), "%s-rx", netdev->name); + led_trigger_rename_static(name, sl->rx_led_trig); + + snprintf(name, sizeof(name), "%s-rxtx", netdev->name); + led_trigger_rename_static(name, sl->rxtx_led_trig); + } + + return NOTIFY_DONE; +} + +static struct notifier_block sllin_netdev_notifier __read_mostly = { + .notifier_call = sllin_netdev_notifier_call, +}; +#endif /* SLLIN_LED_TRIGGER */ + /** * sltty_change_speed() -- Change baudrate of Serial device belonging * to particular @tty @@ -210,7 +355,11 @@ static int sltty_change_speed(struct tty_struct *tty, unsigned speed) struct ktermios old_termios, termios; int cflag; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) mutex_lock(&tty->termios_mutex); +#else + down_write(&tty->termios_rwsem); +#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) old_termios = termios = *(tty->termios); @@ -238,7 +387,11 @@ static int sltty_change_speed(struct tty_struct *tty, unsigned speed) if (tty->ops->set_termios) tty->ops->set_termios(tty, &old_termios); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) mutex_unlock(&tty->termios_mutex); +#else + up_write(&tty->termios_rwsem); +#endif return 0; } @@ -268,6 +421,10 @@ static void sllin_send_canfr(struct sllin *sl, canid_t id, char *data, int len) sl->dev->stats.rx_packets++; sl->dev->stats.rx_bytes += cf.can_dlc; + +#ifdef SLLIN_LED_TRIGGER + sllin_led_event(sl->dev, SLLIN_LED_EVENT_RX); +#endif } /** @@ -311,7 +468,12 @@ static void sllin_write_wakeup(struct tty_struct *tty) return; /* ongoing concurrent processing */ clear_bit(SLF_TXBUFF_RQ, &sl->flags); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) smp_mb__after_clear_bit(); +#else + smp_mb__after_atomic(); +#endif if (sl->lin_state != SLSTATE_BREAK_SENT) remains = sl->tx_lim - sl->tx_cnt; @@ -325,7 +487,11 @@ static void sllin_write_wakeup(struct tty_struct *tty) remains -= actual; } clear_bit(SLF_TXBUFF_INPR, &sl->flags); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) smp_mb__after_clear_bit(); +#else + smp_mb__after_atomic(); +#endif } while (unlikely(test_bit(SLF_TXBUFF_RQ, &sl->flags))); @@ -408,6 +574,9 @@ static int sll_close(struct net_device *dev) sl->tx_lim = 0; spin_unlock_bh(&sl->lock); +#ifdef SLLIN_LED_TRIGGER + sllin_led_event(dev, SLLIN_LED_EVENT_STOP); +#endif return 0; } @@ -423,6 +592,10 @@ static int sll_open(struct net_device *dev) sl->flags &= (1 << SLF_INUSE); netif_start_queue(dev); + +#ifdef SLLIN_LED_TRIGGER + sllin_led_event(dev, SLLIN_LED_EVENT_OPEN); +#endif return 0; } @@ -517,6 +690,9 @@ static void sllin_master_receive_buf(struct tty_struct *tty, */ static void sllin_report_error(struct sllin *sl, int err) { + unsigned char *lin_buff; + int lin_id; + switch (err) { case LIN_ERR_CHECKSUM: sl->dev->stats.rx_crc_errors++; @@ -531,7 +707,9 @@ static void sllin_report_error(struct sllin *sl, int err) break; } - sllin_send_canfr(sl, 0 | CAN_EFF_FLAG | + lin_buff = (sl->lin_master) ? sl->tx_buff : sl->rx_buff; + lin_id = lin_buff[SLLIN_BUFF_ID] & LIN_ID_MASK; + sllin_send_canfr(sl, lin_id | CAN_EFF_FLAG | (err & ~LIN_ID_MASK), NULL, 0); } @@ -711,10 +889,12 @@ static void sllin_slave_receive_buf(struct tty_struct *tty, if (fp && *fp++) { /* * If we don't know the length of the current message - * we received the break of the next message. - * Evaluate the previous one before continuing + * and received at least the LIN ID, we received here + * the break of the next message. + * Evaluate the previous one before continuing. */ - if (sl->rx_len_unknown == true) + if ((sl->rx_len_unknown == true) && + (sl->rx_cnt >= SLLIN_BUFF_ID)) { hrtimer_cancel(&sl->rx_timer); sllin_slave_finish_rx_msg(sl); @@ -831,7 +1011,11 @@ static int sllin_send_tx_buff(struct sllin *sl) return 0; /* ongoing concurrent processing */ clear_bit(SLF_TXBUFF_RQ, &sl->flags); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) smp_mb__after_clear_bit(); +#else + smp_mb__after_atomic(); +#endif #ifdef BREAK_BY_BAUD if (sl->lin_state != SLSTATE_BREAK_SENT) @@ -865,10 +1049,18 @@ static int sllin_send_tx_buff(struct sllin *sl) sl->tx_cnt, remains); clear_bit(SLF_TXBUFF_INPR, &sl->flags); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) smp_mb__after_clear_bit(); +#else + smp_mb__after_atomic(); +#endif } while (unlikely(test_bit(SLF_TXBUFF_RQ, &sl->flags))); +#ifdef SLLIN_LED_TRIGGER + sllin_led_event(sl->dev, SLLIN_LED_EVENT_TX); +#endif + return 0; error_in_write: @@ -981,6 +1173,7 @@ static int sllin_kwthread(void *ptr) struct sched_param schparam = { .sched_priority = 40 }; int tx_bytes = 0; /* Used for Network statistics */ unsigned long flags; + int mode; int lin_id; struct sllin_conf_entry *sce; @@ -1051,8 +1244,12 @@ static int sllin_kwthread(void *ptr) if (!test_bit(SLF_MSGEVENT, &sl->flags)) break; + mode = 0; cf = (struct can_frame *)sl->tx_req_skb->data; + if (cf->can_id & LIN_CHECKSUM_EXTENDED) + mode |= SLLIN_STPMSG_CHCKSUM_ENH; + /* SFF RTR CAN frame -> LIN header */ if (cf->can_id & CAN_RTR_FLAG) { struct sllin_conf_entry *sce; @@ -1062,11 +1259,16 @@ static int sllin_kwthread(void *ptr) sce = &sl->linfr_cache[cf->can_id & LIN_ID_MASK]; spin_lock_irqsave(&sl->linfr_lock, flags); + if (sce->frame_fl & LIN_CHECKSUM_EXTENDED) + mode |= SLLIN_STPMSG_CHCKSUM_ENH; /* Is there Slave response in linfr_cache to be sent? */ if ((sce->frame_fl & LIN_CACHE_RESPONSE) && (sce->dlc > 0)) { + if (sce->frame_fl & LIN_SINGLE_RESPONSE) + sce->frame_fl &= ~LIN_CACHE_RESPONSE; + netdev_dbg(sl->dev, "Sending LIN response from linfr_cache\n"); lin_data = sce->data; @@ -1082,9 +1284,15 @@ static int sllin_kwthread(void *ptr) spin_unlock_irqrestore(&sl->linfr_lock, flags); } else { /* SFF NON-RTR CAN frame -> LIN header + LIN response */ + struct sllin_conf_entry *sce; + netdev_dbg(sl->dev, "%s: NON-RTR SFF CAN frame, ID = %x\n", __func__, (int)cf->can_id & LIN_ID_MASK); + sce = &sl->linfr_cache[cf->can_id & LIN_ID_MASK]; + if (sce->frame_fl & LIN_CHECKSUM_EXTENDED) + mode |= SLLIN_STPMSG_CHCKSUM_ENH; + lin_data = cf->data; lin_dlc = cf->can_dlc; if (lin_dlc > SLLIN_DATA_MAX) @@ -1092,7 +1300,7 @@ static int sllin_kwthread(void *ptr) tx_bytes = lin_dlc; } - if (sllin_setup_msg(sl, 0, cf->can_id & LIN_ID_MASK, + if (sllin_setup_msg(sl, mode, cf->can_id & LIN_ID_MASK, lin_data, lin_dlc) != -1) { sl->id_to_send = true; @@ -1210,7 +1418,9 @@ slstate_response_wait: if ((sce->frame_fl & LIN_CACHE_RESPONSE) && (sce->dlc > 0)) { - int mode; + + if (sce->frame_fl & LIN_SINGLE_RESPONSE) + sce->frame_fl &= ~LIN_CACHE_RESPONSE; netdev_dbg(sl->dev, "Sending LIN response from linfr_cache\n"); @@ -1241,8 +1451,6 @@ slstate_response_wait: sllin_send_tx_buff(sl); } - kfree_skb(sl->tx_req_skb); - netif_wake_queue(sl->dev); hrtimer_start(&sl->rx_timer, ktime_add(ktime_get(), sl->rx_timer_timeout), HRTIMER_MODE_ABS); @@ -1332,7 +1540,12 @@ static struct sllin *sll_alloc(dev_t line) char name[IFNAMSIZ]; sprintf(name, "sllin%d", i); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)) dev = alloc_netdev(sizeof(*sl), name, sll_setup); +#else + dev = alloc_netdev(sizeof(*sl), name, NET_NAME_UNKNOWN, sll_setup); +#endif + if (!dev) return NULL; dev->base_addr = i; @@ -1430,6 +1643,10 @@ static int sllin_open(struct tty_struct *tty) err = register_netdevice(sl->dev); if (err) goto err_free_chan_and_thread; + +#ifdef SLLIN_LED_TRIGGER + devm_sllin_led_init(sl->dev); +#endif } /* Done. We have linked the TTY line to a channel. */ @@ -1530,6 +1747,12 @@ static int __init sllin_init(void) { int status; +#ifdef SLLIN_LED_TRIGGER + status = register_netdevice_notifier(&sllin_netdev_notifier); + if (status) + pr_err("sllin: can't register netdevice notifier\n"); +#endif + if (maxdev < 4) maxdev = 4; /* Sanity */ @@ -1615,6 +1838,11 @@ static void __exit sllin_exit(void) i = tty_unregister_ldisc(N_SLLIN); if (i) pr_err("sllin: can't unregister ldisc (err %d)\n", i); + +#ifdef SLLIN_LED_TRIGGER + unregister_netdevice_notifier(&sllin_netdev_notifier); +#endif + } module_init(sllin_init);