* [PATCH 2/5][RFC] can: Factor out alloc_socketdev()
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 ` Marek Vasut
2016-06-09 19:21 ` [PATCH 3/5][RFC] can: Add register_candevice() Marek Vasut
` (3 subsequent siblings)
4 siblings, 0 replies; 11+ messages in thread
From: Marek Vasut @ 2016-06-09 19:21 UTC (permalink / raw)
To: linux-can; +Cc: Marek Vasut, Oliver Hartkopp, Marc Kleine-Budde
Factor out alloc_socketdev() from alloc_candev() to make it possible
to register different socket devices -- like kline -- using the same
codepath.
Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Oliver Hartkopp <socketcan@hartkopp.net>
Cc: Marc Kleine-Budde <mkl@pengutronix.de>
---
drivers/net/can/dev.c | 11 +++++++++--
include/linux/can/dev.h | 1 +
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index 141c2a4..f8ef6b0 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -641,7 +641,8 @@ EXPORT_SYMBOL_GPL(alloc_can_err_skb);
/*
* Allocate and setup space for the CAN network device
*/
-struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
+struct net_device *alloc_socketdev(const char *name, int sizeof_priv,
+ unsigned int echo_skb_max)
{
struct net_device *dev;
struct can_priv *priv;
@@ -653,7 +654,7 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
else
size = sizeof_priv;
- dev = alloc_netdev(size, "can%d", NET_NAME_UNKNOWN, can_setup);
+ dev = alloc_netdev(size, name, NET_NAME_UNKNOWN, can_setup);
if (!dev)
return NULL;
@@ -671,6 +672,12 @@ struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
return dev;
}
+EXPORT_SYMBOL_GPL(alloc_socketdev);
+
+struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max)
+{
+ return alloc_socketdev("can%d", sizeof_priv, echo_skb_max);
+}
EXPORT_SYMBOL_GPL(alloc_candev);
/*
diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
index 735f9f8..9b0520d 100644
--- a/include/linux/can/dev.h
+++ b/include/linux/can/dev.h
@@ -114,6 +114,7 @@ u8 can_dlc2len(u8 can_dlc);
/* map the sanitized data length to an appropriate data length code */
u8 can_len2dlc(u8 len);
+struct net_device *alloc_socketdev(const char *name, int sizeof_priv, unsigned int echo_skb_max);
struct net_device *alloc_candev(int sizeof_priv, unsigned int echo_skb_max);
void free_candev(struct net_device *dev);
--
2.7.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 4/5][RFC] can: kline: Add KLine rtnl configuration options
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 ` Marek Vasut
2016-06-09 20:21 ` Menschel.P
2016-06-09 19:21 ` [PATCH 5/5][RFC] can: kline: Add KLine ldisc Marek Vasut
2016-06-17 9:24 ` [PATCH 1/5][RFC] can: slcan: Replace sizeof struct can_frame with CAN_MTU Marc Kleine-Budde
4 siblings, 1 reply; 11+ messages in thread
From: Marek Vasut @ 2016-06-09 19:21 UTC (permalink / raw)
To: linux-can; +Cc: Marek Vasut, Oliver Hartkopp, Marc Kleine-Budde
Extend the can rtnl with kline timing parameters W0...W5, P1...P4 .
Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Oliver Hartkopp <socketcan@hartkopp.net>
Cc: Marc Kleine-Budde <mkl@pengutronix.de>
---
drivers/net/can/dev.c | 16 +++++++++++++++-
include/linux/can/dev.h | 2 ++
include/uapi/linux/can/netlink.h | 18 ++++++++++++++++++
3 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index 0db16f6..e867b7a 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -787,6 +787,7 @@ static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
= { .len = sizeof(struct can_bittiming) },
[IFLA_CAN_DATA_BITTIMING_CONST]
= { .len = sizeof(struct can_bittiming_const) },
+ [IFLA_KLINE_TIMING] = { .len = sizeof(struct kline_timing) },
};
static int can_changelink(struct net_device *dev,
@@ -878,6 +879,14 @@ static int can_changelink(struct net_device *dev,
}
}
+ if (data[IFLA_KLINE_TIMING]) {
+ /* Do not allow changing bittiming while running */
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+ memcpy(&priv->kline_timing, nla_data(data[IFLA_KLINE_TIMING]),
+ sizeof(struct kline_timing));
+ }
+
return 0;
}
@@ -900,6 +909,7 @@ static size_t can_get_size(const struct net_device *dev)
size += nla_total_size(sizeof(struct can_bittiming));
if (priv->data_bittiming_const) /* IFLA_CAN_DATA_BITTIMING_CONST */
size += nla_total_size(sizeof(struct can_bittiming_const));
+ size += nla_total_size(sizeof(struct kline_timing)); /* IFLA_KLINE_TIMING */
return size;
}
@@ -938,7 +948,11 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
(priv->data_bittiming_const &&
nla_put(skb, IFLA_CAN_DATA_BITTIMING_CONST,
sizeof(*priv->data_bittiming_const),
- priv->data_bittiming_const)))
+ priv->data_bittiming_const)) ||
+
+ nla_put(skb, IFLA_KLINE_TIMING,
+ sizeof(priv->kline_timing), &priv->kline_timing)
+ )
return -EMSGSIZE;
return 0;
diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
index c67082d..f95f24d 100644
--- a/include/linux/can/dev.h
+++ b/include/linux/can/dev.h
@@ -65,6 +65,8 @@ struct can_priv {
struct led_trigger *rxtx_led_trig;
char rxtx_led_trig_name[CAN_LED_NAME_SZ];
#endif
+
+ struct kline_timing kline_timing;
};
/*
diff --git a/include/uapi/linux/can/netlink.h b/include/uapi/linux/can/netlink.h
index 94ffe0c..da5f4b3 100644
--- a/include/uapi/linux/can/netlink.h
+++ b/include/uapi/linux/can/netlink.h
@@ -113,6 +113,23 @@ struct can_device_stats {
};
/*
+ * KLine timing parameters
+ */
+struct kline_timing {
+ __u32 w0;
+ __u32 w1;
+ __u32 w2;
+ __u32 w3;
+ __u32 w4;
+ __u32 w5;
+ __u32 p1;
+ __u32 p2_94;
+ __u32 p2_08;
+ __u32 p3;
+ __u32 p4;
+};
+
+/*
* CAN netlink interface
*/
enum {
@@ -127,6 +144,7 @@ enum {
IFLA_CAN_BERR_COUNTER,
IFLA_CAN_DATA_BITTIMING,
IFLA_CAN_DATA_BITTIMING_CONST,
+ IFLA_KLINE_TIMING,
__IFLA_CAN_MAX
};
--
2.7.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 5/5][RFC] can: kline: Add KLine ldisc
2016-06-09 19:21 [PATCH 1/5][RFC] can: slcan: Replace sizeof struct can_frame with CAN_MTU Marek Vasut
` (2 preceding siblings ...)
2016-06-09 19:21 ` [PATCH 4/5][RFC] can: kline: Add KLine rtnl configuration options Marek Vasut
@ 2016-06-09 19:21 ` Marek Vasut
2016-06-17 9:24 ` [PATCH 1/5][RFC] can: slcan: Replace sizeof struct can_frame with CAN_MTU Marc Kleine-Budde
4 siblings, 0 replies; 11+ messages in thread
From: Marek Vasut @ 2016-06-09 19:21 UTC (permalink / raw)
To: linux-can; +Cc: Marek Vasut, Oliver Hartkopp, Marc Kleine-Budde
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
^ permalink raw reply related [flat|nested] 11+ messages in thread