Linux SocketCAN Driver
The Can bus has been supported in the Linux kernel since some years, and there are a lot of drivers for CAN bus controllers, traditional CAN drivers for Linux are based on the model of character deivces. Typically they only allow sending to and receiving from the CAN controller, a set of open source CAN drivers and a networking stack is contributed by Volkswagen Research, which known as SocketCAN. The following graph showed the typical CAN communication layers, with SocketCAN in left and conventional in right.
2 A Virtual SocketCAN Driver
As above graph showed, the CAN bus device exist as a kind of network device in the Linux kernel, so writing a CAN bus controller driver is very samilar to write a network card driver, the following is a virtual can driver.
#include #include #include #include #include #include #include #include #include #define VCAN_FIFO_DEPTH 4 struct vcan_priv < struct can_priv can; struct net_device *ndev; >; struct platform_device *vcan_dev; static bool echo; /* echo testing. Default: 0 (Off) */ module_param(echo, bool, S_IRUGO); MODULE_PARM_DESC(echo, "Echo sent frames (for testing). Default: 0 (Off)"); static void vcan_rx(struct sk_buff *skb, struct net_device *ndev) < struct canfd_frame *cfd = (struct canfd_frame *)skb->data; struct net_device_stats *stats = &ndev->stats; stats->rx_packets++; stats->rx_bytes += cfd->len; skb->pkt_type = PACKET_BROADCAST; skb->dev = ndev; skb->ip_summed = CHECKSUM_UNNECESSARY; netif_rx_ni(skb); > static netdev_tx_t vcan_start_xmit(struct sk_buff *skb, struct net_device *ndev) < struct canfd_frame *cfd = (struct canfd_frame *)skb->data; struct net_device_stats *stats = &ndev->stats; int loop; if (can_dropped_invalid_skb(ndev, skb)) return NETDEV_TX_OK; stats->tx_packets++; stats->tx_bytes += cfd->len; /* set flag whether this packet has to be looped back */ loop = skb->pkt_type == PACKET_LOOPBACK; if (!echo) < /* no echo handling available inside this driver */ if (loop) < /* * only count the packets here, because the * CAN core already did the echo for us */ stats->rx_packets++; stats->rx_bytes += cfd->len; > consume_skb(skb); return NETDEV_TX_OK; > /* perform standard echo handling for CAN network interfaces */ if (loop) < skb = can_create_echo_skb(skb); if (!skb) return NETDEV_TX_OK; /* receive with packet counting */ vcan_rx(skb, ndev); >else < /* no looped packets =>no counting */ consume_skb(skb); > return NETDEV_TX_OK; > static int vcan_change_mtu(struct net_device *ndev, int new_mtu) < /* Do not allow changing the MTU while running */ if (ndev->flags & IFF_UP) return -EBUSY; if (new_mtu != CAN_MTU && new_mtu != CANFD_MTU) return -EINVAL; ndev->mtu = new_mtu; return 0; > static const struct net_device_ops vcan_netdev_ops = < .ndo_start_xmit = vcan_start_xmit, .ndo_change_mtu = vcan_change_mtu, >; static int vcan_probe(struct platform_device *pdev) < struct net_device *ndev; struct vcan_priv *priv; int err = -ENODEV; ndev = alloc_candev(sizeof(struct vcan_priv), VCAN_FIFO_DEPTH); if (!ndev) < dev_err(&pdev->dev, "alloc_candev() failed\n"); err = -ENOMEM; goto fail; > priv = netdev_priv(ndev); ndev->netdev_ops = &vcan_netdev_ops; ndev->flags |= IFF_ECHO; priv->ndev = ndev; platform_set_drvdata(pdev, ndev); SET_NETDEV_DEV(ndev, &pdev->dev); err = register_candev(ndev); if (err) < dev_err(&pdev->dev, "register_candev() failed, error %d\n", err); goto fail_candev; > dev_info(&pdev->dev, "device registered\n"); return 0; fail_candev: free_candev(ndev); fail: return err; > static int vcan_remove(struct platform_device *pdev) < struct net_device *ndev = platform_get_drvdata(pdev); unregister_candev(ndev); free_candev(ndev); dev_info(&pdev->dev, "device removed\n"); return 0; > static struct platform_driver vcan_driver = < .driver = < .name = "vcan", .owner = THIS_MODULE, >, .probe = vcan_probe, .remove = vcan_remove, >; static int __init vcan_init(void) < int retval; vcan_dev = platform_device_alloc("vcan", -1); if (!vcan_dev) return -ENOMEM; retval = platform_device_add(vcan_dev); if (retval < 0) < platform_device_put(vcan_dev); return retval; >retval = platform_driver_register(&vcan_driver); if (retval < 0) platform_device_unregister(vcan_dev); return retval; >static void __exit vcan_exit(void) < platform_driver_unregister(&vcan_driver); platform_device_unregister(vcan_dev); >module_init(vcan_init); module_exit(vcan_exit); MODULE_AUTHOR("Yannik Li"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Virtual CAN");
The Makefile as following:
ifneq ($(KERNELRELEASE),) obj-m := vcan.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: @rm -rf .tmp* .vcan* Module* modules* vcan.*o vcan.mod* endif
To install and test the virtual driver as following steps:
- modprobe can
- modprobe can-dev
- modprobe can-raw
- insmod vcan.ko
- ifconfig can0 up
- candump can0
- cansend can0 123#112233
Author: Yanqing Li(Yannik Li)
Created: 2016-04-19 Tue 22:59
CAN bus Linux driver
The current CAN bus driver uses SocketCAN API which is now the official CAN API for Linux. SocketCAN is based on the Linux socket. Further details can be found on the links at the bottom of this page.
Warning: Please ensure that you use a recent version of armadeus BSP (kernel version more than 2.6.38 is required) before trying the instructions described on this page! With older kernel versions you may not be able to change the bitrate. |
Contents
Driver installation
Networking support ---> CAN bus subsystem support ---> --- CAN bus subsystem support Raw CAN Protocol (raw access with CAN-ID filtering) Broadcast Manager CAN Protocol (with content filtering) CAN Device Drivers ---> Virtual Local CAN Interface (vcan) Platform CAN drivers with Netlink support [*] CAN bit-timing calculation Microchip 251x series SPI CAN Controller
Usage
# modprobe can # modprobe can-dev # modprobe can-raw
Example: Set the bitrate of the can0 interface to 125kbps:
# ip link set can0 up type can bitrate 125000
Note: An error occurs when you try to set the bitrate with an old Linux kernel. In that case, echo 125000 > /sys/devices/platform/FlexCAN.0/bitrate was reported to be a good alternative. |
ip: either "dev" is duplicate, or "type" is garbage
If the binary is installed in /bin instead of /sbin, the executable file is a link to busybox and the command to set the bitrate doesn’t work on busybox, so try the following instructions:
$ make busybox-clean $ make busybox-dirclean $ make menuconfig
Package Selection for the target ---> Networking applications ---> [*] iproute2
Quick test
- Once the driver is installed and the bitrate is set, the CAN interface has to be started like a standard net interface:
Userspace tools
Several tools are provided by socketCAN:
The following command shows the received message from the CAN bus
exemple : The following command sends 3 bytes on the bus (0x1E, 0x10, 0x10) with the identifier 500.
You can send a remote request message
The information with the identifier 500 will be available on the bus when the device receive the remote request message
- cangen: CAN frames generator for testing purpose
- canplayer: send CAN frames from a file to a CAN interface
These tools can be compiled and installed on the target by means of the Buildroot menuconfig:
Package Selection for the target ---> Networking ---> [*] Socket CAN
Automatically launch CAN interface at startup
auto can0 iface can0 inet manual pre-up /sbin/ip link set $IFACE type can bitrate 125000 on up /sbin/ifconfig $IFACE up down /sbin/ifconfig $IFACE down
Links
- https://www.ridgerun.com/developer/wiki/index.php/How_to_configure_and_use_CAN_bus (How to configure and use CAN bus)
- http://developer.berlios.de/projects/socketcan/ (CAN Linux Driver)
- http://ww1.microchip.com/downloads/en/DeviceDoc/21801e.pdf (MCP2515 datasheet)
- http://www.kvaser.com/can/protocol/index.htm (CAN introduction)
- Communication with a car’s CAN bus