Commit c4e54b06 authored by Dario Binacchi's avatar Dario Binacchi Committed by Marc Kleine-Budde

can: slcan: use CAN network device driver API

As suggested by commit [1], now the driver uses the functions and the
data structures provided by the CAN network device driver interface.

Currently the driver doesn't implement a way to set bitrate for SLCAN
based devices via ip tool, so you'll have to do this by slcand or
slcan_attach invocation through the -sX parameter:

- slcan_attach -f -s6 -o /dev/ttyACM0
- slcand -f -s8 -o /dev/ttyUSB0

where -s6 in will set adapter's bitrate to 500 Kbit/s and -s8 to
1Mbit/s.
See the table below for further CAN bitrates:
- s0 ->   10 Kbit/s
- s1 ->   20 Kbit/s
- s2 ->   50 Kbit/s
- s3 ->  100 Kbit/s
- s4 ->  125 Kbit/s
- s5 ->  250 Kbit/s
- s6 ->  500 Kbit/s
- s7 ->  800 Kbit/s
- s8 -> 1000 Kbit/s

In doing so, the struct can_priv::bittiming.bitrate of the driver is not
set and since the open_candev() checks that the bitrate has been set, it
must be a non-zero value, the bitrate is set to a fake value (-1U)
before it is called.

Using the rtnl_lock()/rtnl_unlock() functions has become a bit more
tricky as the register_candev() function indirectly calls rtnl_lock()
via register_netdev(). To avoid a deadlock it is therefore necessary to
call rtnl_unlock() before calling register_candev(). The same goes for
the unregister_candev() function.

[1] commit 39549eef ("can: CAN Network device driver and Netlink interface")

Link: https://lore.kernel.org/all/20220628163137.413025-6-dario.binacchi@amarulasolutions.comSigned-off-by: default avatarDario Binacchi <dario.binacchi@amarulasolutions.com>
Tested-by: default avatarJeroen Hofstee <jhofstee@victronenergy.com>
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 036bff28
...@@ -49,26 +49,6 @@ config CAN_VXCAN ...@@ -49,26 +49,6 @@ config CAN_VXCAN
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called vxcan. will be called vxcan.
config CAN_SLCAN
tristate "Serial / USB serial CAN Adaptors (slcan)"
depends on TTY
help
CAN driver for several 'low cost' CAN interfaces that are attached
via serial lines or via USB-to-serial adapters using the LAWICEL
ASCII protocol. The driver implements the tty linediscipline N_SLCAN.
As only the sending and receiving of CAN frames is implemented, this
driver should work with the (serial/USB) CAN hardware from:
www.canusb.com / www.can232.com / www.mictronics.de / www.canhack.de
Userspace tools to attach the SLCAN line discipline (slcan_attach,
slcand) can be found in the can-utils at the linux-can project, see
https://github.com/linux-can/can-utils for details.
The slcan driver supports up to 10 CAN 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 slcan.
config CAN_NETLINK config CAN_NETLINK
bool "CAN device drivers with Netlink support" bool "CAN device drivers with Netlink support"
default y default y
...@@ -172,6 +152,26 @@ config CAN_KVASER_PCIEFD ...@@ -172,6 +152,26 @@ config CAN_KVASER_PCIEFD
Kvaser Mini PCI Express HS v2 Kvaser Mini PCI Express HS v2
Kvaser Mini PCI Express 2xHS v2 Kvaser Mini PCI Express 2xHS v2
config CAN_SLCAN
tristate "Serial / USB serial CAN Adaptors (slcan)"
depends on TTY
help
CAN driver for several 'low cost' CAN interfaces that are attached
via serial lines or via USB-to-serial adapters using the LAWICEL
ASCII protocol. The driver implements the tty linediscipline N_SLCAN.
As only the sending and receiving of CAN frames is implemented, this
driver should work with the (serial/USB) CAN hardware from:
www.canusb.com / www.can232.com / www.mictronics.de / www.canhack.de
Userspace tools to attach the SLCAN line discipline (slcan_attach,
slcand) can be found in the can-utils at the linux-can project, see
https://github.com/linux-can/can-utils for details.
The slcan driver supports up to 10 CAN 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 slcan.
config CAN_SUN4I config CAN_SUN4I
tristate "Allwinner A10 CAN controller" tristate "Allwinner A10 CAN controller"
depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST
......
...@@ -56,7 +56,6 @@ ...@@ -56,7 +56,6 @@
#include <linux/can.h> #include <linux/can.h>
#include <linux/can/dev.h> #include <linux/can/dev.h>
#include <linux/can/skb.h> #include <linux/can/skb.h>
#include <linux/can/can-ml.h>
MODULE_ALIAS_LDISC(N_SLCAN); MODULE_ALIAS_LDISC(N_SLCAN);
MODULE_DESCRIPTION("serial line CAN interface"); MODULE_DESCRIPTION("serial line CAN interface");
...@@ -79,6 +78,7 @@ MODULE_PARM_DESC(maxdev, "Maximum number of slcan interfaces"); ...@@ -79,6 +78,7 @@ MODULE_PARM_DESC(maxdev, "Maximum number of slcan interfaces");
#define SLC_EFF_ID_LEN 8 #define SLC_EFF_ID_LEN 8
struct slcan { struct slcan {
struct can_priv can;
int magic; int magic;
/* Various fields. */ /* Various fields. */
...@@ -394,6 +394,8 @@ static int slc_close(struct net_device *dev) ...@@ -394,6 +394,8 @@ static int slc_close(struct net_device *dev)
clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags); clear_bit(TTY_DO_WRITE_WAKEUP, &sl->tty->flags);
} }
netif_stop_queue(dev); netif_stop_queue(dev);
close_candev(dev);
sl->can.state = CAN_STATE_STOPPED;
sl->rcount = 0; sl->rcount = 0;
sl->xleft = 0; sl->xleft = 0;
spin_unlock_bh(&sl->lock); spin_unlock_bh(&sl->lock);
...@@ -405,20 +407,34 @@ static int slc_close(struct net_device *dev) ...@@ -405,20 +407,34 @@ static int slc_close(struct net_device *dev)
static int slc_open(struct net_device *dev) static int slc_open(struct net_device *dev)
{ {
struct slcan *sl = netdev_priv(dev); struct slcan *sl = netdev_priv(dev);
int err;
if (sl->tty == NULL) if (sl->tty == NULL)
return -ENODEV; return -ENODEV;
/* The baud rate is not set with the command
* `ip link set <iface> type can bitrate <baud>' and therefore
* can.bittiming.bitrate is CAN_BITRATE_UNSET (0), causing
* open_candev() to fail. So let's set to a fake value.
*/
sl->can.bittiming.bitrate = CAN_BITRATE_UNKNOWN;
err = open_candev(dev);
if (err) {
netdev_err(dev, "failed to open can device\n");
return err;
}
sl->can.state = CAN_STATE_ERROR_ACTIVE;
sl->flags &= BIT(SLF_INUSE); sl->flags &= BIT(SLF_INUSE);
netif_start_queue(dev); netif_start_queue(dev);
return 0; return 0;
} }
/* Hook the destructor so we can free slcan devs at the right point in time */ static void slc_dealloc(struct slcan *sl)
static void slc_free_netdev(struct net_device *dev)
{ {
int i = dev->base_addr; int i = sl->dev->base_addr;
free_candev(sl->dev);
slcan_devs[i] = NULL; slcan_devs[i] = NULL;
} }
...@@ -434,24 +450,6 @@ static const struct net_device_ops slc_netdev_ops = { ...@@ -434,24 +450,6 @@ static const struct net_device_ops slc_netdev_ops = {
.ndo_change_mtu = slcan_change_mtu, .ndo_change_mtu = slcan_change_mtu,
}; };
static void slc_setup(struct net_device *dev)
{
dev->netdev_ops = &slc_netdev_ops;
dev->needs_free_netdev = true;
dev->priv_destructor = slc_free_netdev;
dev->hard_header_len = 0;
dev->addr_len = 0;
dev->tx_queue_len = 10;
dev->mtu = CAN_MTU;
dev->type = ARPHRD_CAN;
/* New-style flags. */
dev->flags = IFF_NOARP;
dev->features = NETIF_F_HW_CSUM;
}
/****************************************** /******************************************
Routines looking at TTY side. Routines looking at TTY side.
******************************************/ ******************************************/
...@@ -514,11 +512,8 @@ static void slc_sync(void) ...@@ -514,11 +512,8 @@ static void slc_sync(void)
static struct slcan *slc_alloc(void) static struct slcan *slc_alloc(void)
{ {
int i; int i;
char name[IFNAMSIZ];
struct net_device *dev = NULL; struct net_device *dev = NULL;
struct can_ml_priv *can_ml;
struct slcan *sl; struct slcan *sl;
int size;
for (i = 0; i < maxdev; i++) { for (i = 0; i < maxdev; i++) {
dev = slcan_devs[i]; dev = slcan_devs[i];
...@@ -531,16 +526,14 @@ static struct slcan *slc_alloc(void) ...@@ -531,16 +526,14 @@ static struct slcan *slc_alloc(void)
if (i >= maxdev) if (i >= maxdev)
return NULL; return NULL;
sprintf(name, "slcan%d", i); dev = alloc_candev(sizeof(*sl), 1);
size = ALIGN(sizeof(*sl), NETDEV_ALIGN) + sizeof(struct can_ml_priv);
dev = alloc_netdev(size, name, NET_NAME_UNKNOWN, slc_setup);
if (!dev) if (!dev)
return NULL; return NULL;
snprintf(dev->name, sizeof(dev->name), "slcan%d", i);
dev->netdev_ops = &slc_netdev_ops;
dev->base_addr = i; dev->base_addr = i;
sl = netdev_priv(dev); sl = netdev_priv(dev);
can_ml = (void *)sl + ALIGN(sizeof(*sl), NETDEV_ALIGN);
can_set_ml_priv(dev, can_ml);
/* Initialize channel control data */ /* Initialize channel control data */
sl->magic = SLCAN_MAGIC; sl->magic = SLCAN_MAGIC;
...@@ -605,26 +598,28 @@ static int slcan_open(struct tty_struct *tty) ...@@ -605,26 +598,28 @@ static int slcan_open(struct tty_struct *tty)
set_bit(SLF_INUSE, &sl->flags); set_bit(SLF_INUSE, &sl->flags);
err = register_netdevice(sl->dev); rtnl_unlock();
if (err) err = register_candev(sl->dev);
if (err) {
pr_err("slcan: can't register candev\n");
goto err_free_chan; goto err_free_chan;
}
} else {
rtnl_unlock();
} }
/* Done. We have linked the TTY line to a channel. */
rtnl_unlock();
tty->receive_room = 65536; /* We don't flow control */ tty->receive_room = 65536; /* We don't flow control */
/* TTY layer expects 0 on success */ /* TTY layer expects 0 on success */
return 0; return 0;
err_free_chan: err_free_chan:
rtnl_lock();
sl->tty = NULL; sl->tty = NULL;
tty->disc_data = NULL; tty->disc_data = NULL;
clear_bit(SLF_INUSE, &sl->flags); clear_bit(SLF_INUSE, &sl->flags);
slc_free_netdev(sl->dev); slc_dealloc(sl);
/* do not call free_netdev before rtnl_unlock */
rtnl_unlock(); rtnl_unlock();
free_netdev(sl->dev);
return err; return err;
err_exit: err_exit:
...@@ -658,9 +653,11 @@ static void slcan_close(struct tty_struct *tty) ...@@ -658,9 +653,11 @@ static void slcan_close(struct tty_struct *tty)
synchronize_rcu(); synchronize_rcu();
flush_work(&sl->tx_work); flush_work(&sl->tx_work);
/* Flush network side */ slc_close(sl->dev);
unregister_netdev(sl->dev); unregister_candev(sl->dev);
/* This will complete via sl_free_netdev */ rtnl_lock();
slc_dealloc(sl);
rtnl_unlock();
} }
static void slcan_hangup(struct tty_struct *tty) static void slcan_hangup(struct tty_struct *tty)
...@@ -768,14 +765,15 @@ static void __exit slcan_exit(void) ...@@ -768,14 +765,15 @@ static void __exit slcan_exit(void)
dev = slcan_devs[i]; dev = slcan_devs[i];
if (!dev) if (!dev)
continue; continue;
slcan_devs[i] = NULL;
sl = netdev_priv(dev); sl = netdev_priv(dev);
if (sl->tty) { if (sl->tty) {
netdev_err(dev, "tty discipline still running\n"); netdev_err(dev, "tty discipline still running\n");
} }
unregister_netdev(dev); slc_close(dev);
unregister_candev(dev);
slc_dealloc(sl);
} }
kfree(slcan_devs); kfree(slcan_devs);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment