From: Marek Vasut <marex@denx.de>
To: linux-can@vger.kernel.org
Cc: Marek Vasut <marex@denx.de>,
Oliver Hartkopp <socketcan@hartkopp.net>,
Marc Kleine-Budde <mkl@pengutronix.de>
Subject: [PATCH 5/5][RFC] can: kline: Add KLine ldisc
Date: Thu, 9 Jun 2016 21:21:04 +0200 [thread overview]
Message-ID: <1465500064-5402-5-git-send-email-marex@denx.de> (raw)
In-Reply-To: <1465500064-5402-1-git-send-email-marex@denx.de>
Add initial TTY line discipline connected into the socketcan stack
which facilitates the communication with KLine device.
Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Oliver Hartkopp <socketcan@hartkopp.net>
Cc: Marc Kleine-Budde <mkl@pengutronix.de>
---
NOTE: There is a lot of stuff in this patch which is not fleshed out yet.
drivers/net/can/Kconfig | 19 ++
drivers/net/can/Makefile | 1 +
drivers/net/can/sl_kline.c | 683 +++++++++++++++++++++++++++++++++++++++++++++
include/uapi/linux/can.h | 33 +++
include/uapi/linux/tty.h | 1 +
net/can/af_can.c | 6 +-
net/can/raw.c | 2 +-
7 files changed, 743 insertions(+), 2 deletions(-)
create mode 100644 drivers/net/can/sl_kline.c
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 20be638..e1a8728 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -29,6 +29,25 @@ config CAN_SLCAN
can be changed by the 'maxdev=xx' module option. This driver can
also be built as a module. If so, the module will be called slcan.
+config CAN_SL_KLINE
+ tristate "Serial K-Line protocol using SocketCAN (sl_kline)"
+ depends on TTY
+ ---help---
+ SocketCAN driver for the ISO 9141-2 K-Line protocol using TTY
+ line discipline. This driver allows userland to communicate
+ using standard SocketCAN interface to a device using K-Line
+ protocol. Such K-Line device is connected to a regular serial
+ port via level-shifter or similar logic. The TTY line discipline
+ type is N_SL_KLINE.
+
+ Userspace tools to attach the SL_KLINE ldisc (sl_kline_attach,
+ sl_kline_d) can be found in the can-utils at the SocketCAN git).
+
+ The sl_kline driver supports up to 10 KLine netdevices by default
+ which can be changed by the 'maxdev=xx' module option. This driver
+ can also be built as a module. If so, the module will be called
+ sl_kline.
+
config CAN_DEV
tristate "Platform CAN drivers with Netlink support"
default y
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index e3db0c8..60648e9 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_CAN_VCAN) += vcan.o
obj-$(CONFIG_CAN_SLCAN) += slcan.o
+obj-$(CONFIG_CAN_SL_KLINE) += sl_kline.o
obj-$(CONFIG_CAN_DEV) += can-dev.o
can-dev-y := dev.o
diff --git a/drivers/net/can/sl_kline.c b/drivers/net/can/sl_kline.c
new file mode 100644
index 0000000..fc9ca24
--- /dev/null
+++ b/drivers/net/can/sl_kline.c
@@ -0,0 +1,683 @@
+/*
+ * sl_kline.c - serial line K-Line interface driver (using tty line discipline)
+ *
+ * Heavilty based on slcan.c by Oliver Hartkopp <socketcan@hartkopp.net>
+ * serial line CAN interface driver (using tty line discipline)
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include <linux/uaccess.h>
+#include <linux/bitops.h>
+#include <linux/string.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
+#include <linux/can.h>
+#include <linux/can/skb.h>
+
+#include <linux/can/dev.h>
+
+MODULE_ALIAS_LDISC(N_SL_KLINE);
+MODULE_DESCRIPTION("serial line K-Line interface");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+
+#define SL_KLINE_MAGIC 0x53CB /* FIXME */
+
+static int maxdev = 10; /* MAX number of SL_KLINE channels;
+ This can be overridden with
+ insmod sl_kline.ko maxdev=nnn */
+module_param(maxdev, int, 0);
+MODULE_PARM_DESC(maxdev, "Maximum number of sl_kline interfaces");
+
+struct sl_kline {
+ struct can_priv can; /* must be the first member */
+
+ int magic;
+
+ /* Various fields. */
+ struct tty_struct *tty;
+ struct net_device *dev;
+ spinlock_t lock;
+ struct work_struct tx_work; /* Flushes transmit buffer */
+
+ /* These are pointers to the malloc()ed frame buffers. */
+ unsigned char rbuff[KLINE_MAX_DLEN];
+ int rcount;
+
+ unsigned long flags;
+
+#define KLF_IN_USE (1 << 0)
+#define KLF_INIT (1 << 1)
+#define KLF_WAIT_FOR_55 (1 << 2)
+#define KLF_WAIT_FOR_ADDR1 (1 << 3)
+#define KLF_WAIT_FOR_ADDR2 (1 << 4)
+#define KLF_WAIT_FOR_CC (1 << 5)
+
+ struct kline_frame tx_kf;
+ struct timer_list timer;
+};
+
+static struct net_device **sl_kline_devs;
+
+/* Send one kline_frame to the network layer */
+static void sl_kline_bump(struct sl_kline *sl)
+{
+ struct sk_buff *skb;
+ struct kline_frame kf;
+
+ kf.can_id = 0;
+ kf.len = sl->rcount;
+ memcpy(kf.data, sl->rbuff, sl->rcount);
+
+ skb = dev_alloc_skb(sizeof(struct kline_frame) +
+ sizeof(struct can_skb_priv));
+ if (!skb)
+ return;
+
+ skb->dev = sl->dev;
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ can_skb_reserve(skb);
+ can_skb_prv(skb)->ifindex = sl->dev->ifindex;
+ can_skb_prv(skb)->skbcnt = 0;
+
+ memcpy(skb_put(skb, sizeof(struct kline_frame)),
+ &kf, sizeof(struct kline_frame));
+
+ sl->dev->stats.rx_packets++;
+ sl->dev->stats.rx_bytes += kf.len;
+ netif_rx_ni(skb);
+}
+
+/* Write transmit buffer. Scheduled when tty is writable */
+static void sl_kline_transmit(struct work_struct *work)
+{
+ struct sl_kline *sl = container_of(work, struct sl_kline, tx_work);
+ struct kline_frame *kf = &sl->tx_kf;
+ struct ktermios ktermios = sl->tty->termios;
+ int i;
+
+ spin_lock_bh(&sl->lock);
+ /* First make sure we're connected. */
+ if (!sl->tty || sl->magic != SL_KLINE_MAGIC || !netif_running(sl->dev)) {
+ spin_unlock_bh(&sl->lock);
+ return;
+ }
+
+ /* Push out start frame */
+ if (kf->flags & (KLINE_ISO9141_START | KLINE_KWP_SLOW_START)) {
+ /* Wait time W0=2mS */
+ mdelay(sl->can.kline_timing.w0);
+
+ /* Set speed to 5 Bd/s */
+ ktermios.c_cflag &= ~CBAUD;
+ tty_termios_encode_baud_rate(&ktermios, 5, 5);
+ tty_set_termios(sl->tty, &ktermios);
+
+ /* Transmit init */
+ tty_put_char(sl->tty, 0x33);
+ sl->dev->stats.tx_bytes++;
+
+ sl->flags |= KLF_INIT | KLF_WAIT_FOR_55;
+ spin_unlock_bh(&sl->lock);
+
+ /* Start timer to measure W1 */
+ mod_timer(&sl->timer,
+ jiffies + msecs_to_jiffies(sl->can.kline_timing.w1));
+ return;
+ }
+
+ /* We should NEVER end up here with KLF_INIT set. */
+ WARN_ON(sl->flags & KLF_INIT);
+
+ /* Just write the data and be done with it. */
+ for (i = 0; i < kf->len; i++) {
+ tty_put_char(sl->tty, kf->data[i]);
+ sl->dev->stats.tx_bytes++;
+ /* FIXME: Delay for the inter-byte interval */
+ }
+ spin_unlock_bh(&sl->lock);
+}
+
+static void sl_kline_timeout(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct sl_kline *sl = netdev_priv(dev);
+
+ /* Check if the link-partner responded */
+ if (sl->flags & KLF_WAIT_FOR_55) { /* No response */
+ sl->flags &= ~(KLF_INIT | KLF_WAIT_FOR_55);
+ sl->dev->stats.tx_errors++;
+ netif_wake_queue(sl->dev);
+ }
+}
+
+/*
+ * Called by the driver when there's room for more data.
+ * Schedule the transmit.
+ */
+static void sl_kline_write_wakeup(struct tty_struct *tty)
+{
+ struct sl_kline *sl = tty->disc_data;
+
+ schedule_work(&sl->tx_work);
+}
+
+/* Send a kline_frame to a TTY queue. */
+static netdev_tx_t sl_kline_ndev_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct sl_kline *sl = netdev_priv(dev);
+
+ if (skb->len != sizeof(struct kline_frame))
+ goto out;
+
+ spin_lock(&sl->lock);
+ if (!netif_running(dev)) {
+ spin_unlock(&sl->lock);
+ printk(KERN_WARNING "%s: xmit: iface is down\n", dev->name);
+ goto out;
+ }
+ if (sl->tty == NULL) {
+ spin_unlock(&sl->lock);
+ goto out;
+ }
+
+ netif_stop_queue(sl->dev);
+ memcpy(&sl->tx_kf, (struct kline_frame *)skb->data, sizeof(sl->tx_kf));
+ schedule_work(&sl->tx_work);
+ spin_unlock(&sl->lock);
+
+out:
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+}
+
+
+/******************************************
+ * Routines looking at netdevice side.
+ ******************************************/
+
+/* Netdevice UP -> DOWN routine */
+static int sl_kline_ndev_close(struct net_device *dev)
+{
+ struct sl_kline *sl = netdev_priv(dev);
+
+ spin_lock_bh(&sl->lock);
+ if (sl->tty) {
+ /* TTY discipline is running. */
+ clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
+ }
+ netif_stop_queue(dev);
+ sl->rcount = 0;
+ spin_unlock_bh(&sl->lock);
+
+ return 0;
+}
+
+/* Netdevice DOWN -> UP routine */
+static int sl_kline_ndev_open(struct net_device *dev)
+{
+ struct sl_kline *sl = netdev_priv(dev);
+
+ if (sl->tty == NULL)
+ return -ENODEV;
+
+ setup_timer(&sl->timer, sl_kline_timeout, (unsigned long)dev);
+
+ sl->flags &= KLF_IN_USE;
+ netif_start_queue(dev);
+ return 0;
+}
+
+/* Hook the destructor so we can free sl_kline devs at the right point in time */
+static void sl_kline_ndev_free(struct net_device *dev)
+{
+ struct sl_kline *sl = netdev_priv(dev);
+ int i = dev->base_addr;
+
+ del_timer_sync(&sl->timer);
+
+ free_netdev(dev);
+ sl_kline_devs[i] = NULL;
+}
+
+static int sl_kline_ndev_change_mtu(struct net_device *dev, int new_mtu)
+{
+ return -EINVAL;
+}
+
+static const struct net_device_ops sl_kline_ndev_ops = {
+ .ndo_open = sl_kline_ndev_open,
+ .ndo_stop = sl_kline_ndev_close,
+ .ndo_start_xmit = sl_kline_ndev_xmit,
+ .ndo_change_mtu = sl_kline_ndev_change_mtu,
+};
+
+/******************************************
+ Routines looking at TTY side.
+ ******************************************/
+static void sl_kline_receive_buf(struct tty_struct *tty,
+ const unsigned char *cp, char *fp, int count)
+{
+ struct sl_kline *sl = (struct sl_kline *) tty->disc_data;
+ struct ktermios ktermios = tty->termios;
+
+ if (!sl || sl->magic != SL_KLINE_MAGIC || !netif_running(sl->dev))
+ return;
+
+ /* FIXME: Add mutex for the flags */
+
+ /* Handle incoming data */
+ if (!(sl->flags & KLF_INIT)) { /* Normal data. */
+ sl->rcount = 0;
+ while (count--) {
+ if (fp && *fp++) {
+ /* FIXME: Log RX errors ? */
+ sl->dev->stats.rx_errors++;
+ cp++;
+ continue;
+ } else {
+ sl->rbuff[sl->rcount++] = *cp++;
+ }
+ }
+ sl_kline_bump(sl);
+ netif_wake_queue(sl->dev);
+ return;
+ }
+
+ if (sl->flags & KLF_WAIT_FOR_55) {
+ if (!count)
+ return;
+
+ sl->flags &= ~KLF_WAIT_FOR_55;
+ if (*cp == 0x55) { /* Valid response */
+ /* Set speed to 10400 Bd/s */
+ ktermios.c_cflag &= ~CBAUD;
+ tty_termios_encode_baud_rate(&ktermios, 10400, 10400);
+ tty_set_termios(tty, &ktermios);
+ }
+
+ if (*cp != 0x55) /* Invalid response */
+ goto fail;
+
+ sl->rcount = 0;
+ sl->rbuff[sl->rcount++] = *cp++;
+
+ sl->flags |= KLF_WAIT_FOR_ADDR1;
+ }
+
+ if (sl->flags & KLF_WAIT_FOR_ADDR1) {
+ if (!count)
+ return;
+
+ sl->flags &= ~KLF_WAIT_FOR_ADDR1;
+ if (*cp != 0x08 && *cp != 0x94) /* Invalid address */
+ goto fail;
+
+ sl->rbuff[sl->rcount++] = *cp++;
+
+ sl->flags |= KLF_WAIT_FOR_ADDR2;
+ }
+
+ if (sl->flags & KLF_WAIT_FOR_ADDR2) {
+ if (!count)
+ return;
+
+ sl->flags &= ~KLF_WAIT_FOR_ADDR2;
+ if (*cp != 0x08 && *cp != 0x94) /* Invalid address */
+ goto fail;
+
+ /* Send negated address */
+ tty_put_char(sl->tty, ~(*cp) & 0xff);
+ sl->dev->stats.tx_bytes++;
+
+ sl->rbuff[sl->rcount++] = *cp++;
+
+ sl->flags |= KLF_WAIT_FOR_CC;
+ }
+
+ if (sl->flags & KLF_WAIT_FOR_CC) {
+ if (!count)
+ return;
+
+ sl->flags &= ~(KLF_WAIT_FOR_CC | KLF_INIT);
+ if (*cp != 0xcc) /* Invalid response */
+ goto fail;
+
+ sl->rbuff[sl->rcount] = *cp;
+
+ sl_kline_bump(sl);
+
+ sl->rcount = 0;
+
+ /* Wakeup TX queue */
+ netif_wake_queue(sl->dev);
+ }
+ return;
+fail:
+ sl->dev->stats.rx_errors++;
+ while (count--)
+ sl->rbuff[sl->rcount++] = *cp++;
+ sl_kline_bump(sl);
+ netif_wake_queue(sl->dev);
+}
+
+/************************************
+ * sl_kline_open helper routines.
+ ************************************/
+
+/* Collect hanged up channels */
+static void sl_kline_sync(void)
+{
+ int i;
+ struct net_device *dev;
+ struct sl_kline *sl;
+
+ for (i = 0; i < maxdev; i++) {
+ dev = sl_kline_devs[i];
+ if (dev == NULL)
+ break;
+
+ sl = netdev_priv(dev);
+ if (sl->tty)
+ continue;
+ if (dev->flags & IFF_UP)
+ dev_close(dev);
+ }
+}
+
+/* Find a free SL_KLINE channel, and link in this `tty' line. */
+static struct sl_kline *sl_kline_alloc(dev_t line)
+{
+ int i;
+ struct net_device *dev = NULL;
+ struct sl_kline *sl;
+
+ for (i = 0; i < maxdev; i++) {
+ dev = sl_kline_devs[i];
+ if (dev == NULL)
+ break;
+ }
+
+ /* Sorry, too many, all slots in use */
+ if (i >= maxdev)
+ return NULL;
+
+ dev = alloc_socketdev("sl_kline%d", sizeof(*sl), 0);
+ if (!dev)
+ return NULL;
+ dev->netdev_ops = &sl_kline_ndev_ops;
+ dev->base_addr = i;
+ dev->destructor = sl_kline_ndev_free;
+ dev->mtu = KLINE_MTU;
+
+ sl = netdev_priv(dev);
+
+ /* Initialize channel control data */
+ sl->magic = SL_KLINE_MAGIC;
+ sl->dev = dev;
+ spin_lock_init(&sl->lock);
+ INIT_WORK(&sl->tx_work, sl_kline_transmit);
+ init_timer(&sl->timer);
+ sl_kline_devs[i] = dev;
+
+ return sl;
+}
+
+/*
+ * Open the high-level part of the SL_KLINE channel.
+ * This function is called by the TTY module when the
+ * SL_KLINE line discipline is called for. Because we are
+ * sure the tty line exists, we only have to link it to
+ * a free SL_KLINE channel...
+ *
+ * Called in process context serialized from other ldisc calls.
+ */
+
+static int sl_kline_open(struct tty_struct *tty)
+{
+ struct sl_kline *sl;
+ int err;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (tty->ops->write == NULL)
+ return -EOPNOTSUPP;
+
+ /* RTnetlink lock is misused here to serialize concurrent
+ opens of sl_kline channels. There are better ways, but it is
+ the simplest one.
+ */
+ rtnl_lock();
+
+ /* Collect hanged up channels. */
+ sl_kline_sync();
+
+ sl = tty->disc_data;
+
+ err = -EEXIST;
+ /* First make sure we're not already connected. */
+ if (sl && sl->magic == SL_KLINE_MAGIC)
+ goto err_exit;
+
+ /* OK. Find a free SL_KLINE channel to use. */
+ err = -ENFILE;
+ sl = sl_kline_alloc(tty_devnum(tty));
+ if (sl == NULL)
+ goto err_exit;
+
+ sl->tty = tty;
+ tty->disc_data = sl;
+
+ if (!(sl->flags & KLF_IN_USE)) {
+ /* Perform the low-level SL_KLINE initialization. */
+ sl->rcount = 0;
+
+ sl->flags |= KLF_IN_USE;
+
+ err = register_candevice(sl->dev);
+ if (err)
+ goto err_free_chan;
+ }
+
+ /* Done. We have linked the TTY line to a channel. */
+ rtnl_unlock();
+ tty->receive_room = 65536; /* We don't flow control */
+
+ /* TTY layer expects 0 on success */
+ return 0;
+
+err_free_chan:
+ sl->tty = NULL;
+ tty->disc_data = NULL;
+ sl->flags &= ~KLF_IN_USE;
+
+err_exit:
+ rtnl_unlock();
+
+ /* Count references from TTY module */
+ return err;
+}
+
+/*
+ * Close down a SL_KLINE channel.
+ * This means flushing out any pending queues, and then returning. This
+ * call is serialized against other ldisc functions.
+ *
+ * We also use this method for a hangup event.
+ */
+
+static void sl_kline_close(struct tty_struct *tty)
+{
+ struct sl_kline *sl = (struct sl_kline *) tty->disc_data;
+
+ /* First make sure we're connected. */
+ if (!sl || sl->magic != SL_KLINE_MAGIC || sl->tty != tty)
+ return;
+
+ spin_lock_bh(&sl->lock);
+ tty->disc_data = NULL;
+ sl->tty = NULL;
+ spin_unlock_bh(&sl->lock);
+
+ flush_work(&sl->tx_work);
+
+ /* Flush network side */
+ unregister_netdev(sl->dev);
+ /* This will complete via sl_free_netdev */
+}
+
+static int sl_kline_hangup(struct tty_struct *tty)
+{
+ sl_kline_close(tty);
+ return 0;
+}
+
+/* Perform I/O control on an active SL_KLINE channel. */
+static int sl_kline_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct sl_kline *sl = (struct sl_kline *) tty->disc_data;
+ unsigned int tmp;
+
+ /* First make sure we're connected. */
+ if (!sl || sl->magic != SL_KLINE_MAGIC)
+ return -EINVAL;
+
+ switch (cmd) {
+ case SIOCGIFNAME:
+ tmp = strlen(sl->dev->name) + 1;
+ if (copy_to_user((void __user *)arg, sl->dev->name, tmp))
+ return -EFAULT;
+ return 0;
+
+ case SIOCSIFHWADDR:
+ return -EINVAL;
+
+ default:
+ return tty_mode_ioctl(tty, file, cmd, arg);
+ }
+}
+
+static struct tty_ldisc_ops sl_kline_ldisc = {
+ .owner = THIS_MODULE,
+ .magic = TTY_LDISC_MAGIC,
+ .name = "sl_kline",
+ .open = sl_kline_open,
+ .close = sl_kline_close,
+ .hangup = sl_kline_hangup,
+ .ioctl = sl_kline_ioctl,
+ .receive_buf = sl_kline_receive_buf,
+ .write_wakeup = sl_kline_write_wakeup,
+};
+
+static int __init sl_kline_init(void)
+{
+ int status;
+
+ if (maxdev < 4)
+ maxdev = 4; /* Sanity */
+
+ pr_info("sl_kline: serial line K-Line interface driver\n");
+ pr_info("sl_kline: %d dynamic interface channels.\n", maxdev);
+
+ sl_kline_devs = kzalloc(sizeof(struct net_device *)*maxdev, GFP_KERNEL);
+ if (!sl_kline_devs)
+ return -ENOMEM;
+
+ /* Fill in our line protocol discipline, and register it */
+ status = tty_register_ldisc(N_SL_KLINE, &sl_kline_ldisc);
+ if (status) {
+ pr_err("sl_kline: can't register line discipline\n");
+ kfree(sl_kline_devs);
+ }
+ return status;
+}
+
+static void __exit sl_kline_exit(void)
+{
+ int i;
+ struct net_device *dev;
+ struct sl_kline *sl;
+ unsigned long timeout = jiffies + HZ;
+ int busy = 0;
+
+ if (sl_kline_devs == NULL)
+ return;
+
+ /* First of all: check for active disciplines and hangup them.
+ */
+ do {
+ if (busy)
+ msleep_interruptible(100);
+
+ busy = 0;
+ for (i = 0; i < maxdev; i++) {
+ dev = sl_kline_devs[i];
+ if (!dev)
+ continue;
+ sl = netdev_priv(dev);
+ spin_lock_bh(&sl->lock);
+ if (sl->tty) {
+ busy++;
+ tty_hangup(sl->tty);
+ }
+ spin_unlock_bh(&sl->lock);
+ }
+ } while (busy && time_before(jiffies, timeout));
+
+ /* FIXME: hangup is async so we should wait when doing this second
+ phase */
+
+ for (i = 0; i < maxdev; i++) {
+ dev = sl_kline_devs[i];
+ if (!dev)
+ continue;
+ sl_kline_devs[i] = NULL;
+
+ sl = netdev_priv(dev);
+ if (sl->tty) {
+ pr_err("%s: tty discipline still running\n",
+ dev->name);
+ /* Intentionally leak the control block. */
+ dev->destructor = NULL;
+ }
+
+ unregister_netdev(dev);
+ }
+
+ kfree(sl_kline_devs);
+ sl_kline_devs = NULL;
+
+ i = tty_unregister_ldisc(N_SL_KLINE);
+ if (i)
+ printk(KERN_ERR "sl_kline: can't unregister ldisc (err %d)\n", i);
+}
+
+module_init(sl_kline_init);
+module_exit(sl_kline_exit);
diff --git a/include/uapi/linux/can.h b/include/uapi/linux/can.h
index 9692cda..732cdc0 100644
--- a/include/uapi/linux/can.h
+++ b/include/uapi/linux/can.h
@@ -89,6 +89,10 @@ typedef __u32 can_err_mask_t;
#define CANFD_MAX_DLC 15
#define CANFD_MAX_DLEN 64
+/* KLine payload length and DLC FIXME */
+#define KLINE_MAX_DLC 15
+#define KLINE_MAX_DLEN 255
+
/**
* struct can_frame - basic CAN frame structure
* @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
@@ -146,8 +150,37 @@ struct canfd_frame {
__u8 data[CANFD_MAX_DLEN] __attribute__((aligned(8)));
};
+/*
+ * FIXME
+ * defined bits for kline_frame.flags
+ *
+ */
+#define KLINE_ISO9141_START 0x1 /* ISO slow start sequence */
+#define KLINE_KWP_SLOW_START 0x2 /* KWP slow start sequence */
+#define KLINE_KWP_FAST_START 0x4 /* KWP fast start sequence */
+
+/**
+ * FIXME
+ * struct canfd_frame - CAN flexible data rate frame structure
+ * @can_id: CAN ID of the frame and CAN_*_FLAG flags, see canid_t definition
+ * @len: frame payload length in byte (0 .. CANFD_MAX_DLEN)
+ * @flags: additional flags for CAN FD
+ * @__res0: reserved / padding
+ * @__res1: reserved / padding
+ * @data: CAN FD frame payload (up to CANFD_MAX_DLEN byte)
+ */
+struct kline_frame {
+ canid_t can_id; /* 32 bit KLine address + EFF/RTR/ERR flags (unused) */
+ __u8 len; /* frame payload length in byte */
+ __u8 flags; /* additional flags for KLine */
+ __u8 __res0; /* reserved / padding */
+ __u8 __res1; /* reserved / padding */
+ __u8 data[KLINE_MAX_DLEN] __attribute__((aligned(8)));
+};
+
#define CAN_MTU (sizeof(struct can_frame))
#define CANFD_MTU (sizeof(struct canfd_frame))
+#define KLINE_MTU (sizeof(struct kline_frame))
/* particular protocols of the protocol family PF_CAN */
#define CAN_RAW 1 /* RAW sockets */
diff --git a/include/uapi/linux/tty.h b/include/uapi/linux/tty.h
index 01c4410..80b8f1e 100644
--- a/include/uapi/linux/tty.h
+++ b/include/uapi/linux/tty.h
@@ -35,5 +35,6 @@
#define N_TRACESINK 23 /* Trace data routing for MIPI P1149.7 */
#define N_TRACEROUTER 24 /* Trace data routing for MIPI P1149.7 */
#define N_NCI 25 /* NFC NCI UART */
+#define N_SL_KLINE 26 /* ISO 9141-2 K-Line via SocketCAN */
#endif /* _UAPI_LINUX_TTY_H */
diff --git a/net/can/af_can.c b/net/can/af_can.c
index 166d436..1a8b836 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -238,6 +238,10 @@ int can_send(struct sk_buff *skb, int loop)
skb->protocol = htons(ETH_P_CANFD);
if (unlikely(cfd->len > CANFD_MAX_DLEN))
goto inval_skb;
+ } else if (skb->len == KLINE_MTU) {
+ skb->protocol = htons(ETH_P_CAN);
+ if (unlikely(cfd->len > KLINE_MAX_DLEN))
+ goto inval_skb;
} else
goto inval_skb;
@@ -715,7 +719,7 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,
goto drop;
if (WARN_ONCE(dev->type != ARPHRD_CAN ||
- skb->len != CAN_MTU ||
+ (skb->len != CAN_MTU && skb->len != KLINE_MTU) ||
cfd->len > CAN_MAX_DLEN,
"PF_CAN: dropped non conform CAN skbuf: "
"dev type %d, len %d, datalen %d\n",
diff --git a/net/can/raw.c b/net/can/raw.c
index 2e67b14..8a95404 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -734,7 +734,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
if (unlikely(size != CANFD_MTU && size != CAN_MTU))
return -EINVAL;
} else {
- if (unlikely(size != CAN_MTU))
+ if (unlikely((size != CAN_MTU) && (size != KLINE_MTU)))
return -EINVAL;
}
--
2.7.0
next prev parent reply other threads:[~2016-06-09 19:21 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-06-09 19:21 [PATCH 1/5][RFC] can: slcan: Replace sizeof struct can_frame with CAN_MTU Marek Vasut
2016-06-09 19:21 ` [PATCH 2/5][RFC] can: Factor out alloc_socketdev() Marek Vasut
2016-06-09 19:21 ` [PATCH 3/5][RFC] can: Add register_candevice() Marek Vasut
2016-06-09 19:21 ` [PATCH 4/5][RFC] can: kline: Add KLine rtnl configuration options Marek Vasut
2016-06-09 20:21 ` Menschel.P
2016-06-11 19:40 ` Marek Vasut
2016-06-12 15:36 ` Patrick Menschel
2016-06-12 17:34 ` Marek Vasut
2016-06-09 19:21 ` Marek Vasut [this message]
2016-06-17 9:24 ` [PATCH 1/5][RFC] can: slcan: Replace sizeof struct can_frame with CAN_MTU Marc Kleine-Budde
2016-06-17 9:25 ` Marek Vasut
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1465500064-5402-5-git-send-email-marex@denx.de \
--to=marex@denx.de \
--cc=linux-can@vger.kernel.org \
--cc=mkl@pengutronix.de \
--cc=socketcan@hartkopp.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).