LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

Linux CAN子系统:基于M_CAN解读

  Linux下CAN驱动属于网络设备驱动。

CAN在内核中大致框架如下:

 

 

1 CAN配置

 对CAN设备的配置,首先需要打开CAN子系统,然后打开CAN设备驱动,其他还包括调试信息等。

Networking support
  ->CAN bus subsystem support
    ->Raw CAN Protocol (raw access with CAN-ID filtering)--支持BSD socket API方位CAN总线。
    ->Broadcast Manager CAN Protocol (with content filtering)--
    ->CAN Gateway/Router (with netlink configuration)--路由CAN帧。
    ->CAN Device Drivers--CAN设备驱动。
      ->Platform CAN drivers with Netlink support--支持Netlink的CAN platform总线通用框架。
        ->CAN bit-timing calculation
        ->Bosch M_CAN support--Bosch M_CAN驱动。
          ->Bosch M_CAN support for io-mapped devices
      ->CAN devices debugging messages--CAN设备调试信息。

CAN子系统初始化包括:网络设备回调通知处理LED trigger名称;注册Rtnetlink处理函数。

can_dev_init
  ->can_led_notifier_init--注册对NETDEV_CHANGENAME消息的响应处理函数。
    ->register_netdevice_notifier
      ->can_led_notifier
  ->rtnl_link_register--Rtnetlink基于netlink,允许对内核的路由表进行读写。主要用来进行内核与用户空见得通信以及内核中子系统之间的通信。操作函数集为can_link_ops
can_dev_exit
  ->rtnl_link_unregister
  ->can_led_notifier_exit

can模块:

  • 创建can_receiver缓存。
  • 注册PF_CAN协议族。
  • 增加ETH_P_CAN和ETH_P_CANFD两种类型packet处理。
can_init
  ->kmem_cache_create--创建can_receiver缓存。
  ->register_pernet_subsys
  ->sock_register--注册PF_CAN协议族。
  ->register_netdevice_notifier
  ->dev_add_pack--增加ETH_P_CAN/ETH_P_CANFD两种packet处理。

can_gw模块,带Netlink接口的CAN帧Gateway/Router/Bridge初始化:

cgw_module_init--创建can_gw模块。
  ->register_pernet_subsys--操作函数集为cangw_pernet_ops。
  ->register_netdevice_notifier
  ->rtnl_register_module--注册CAN协议的RTM_GETROUTE/RTM_NEWROUTE/RTM_DELROUTE消息处理。

2 CAN文件

 CAN子系统文件主要包括Core实现和各驱动文件。

drivers/net/can/
├── dev.c--CAN子系统Core函数实现。 ├── m_can │   ├── m_can.c--通用M_CAN设备驱动接口。 │   ├── m_can_platform.c--基于M_CAN IP的platform驱动。 ├── rx-offload.c--通过使能NAPI对CAN接收offload。

CAN网络相关文件如下:

net/can/
├── af_can.c--CAN Address Family模块初始化。
├── bcm.c--过滤或发送CAN内容广播管理。
├── gw.c--CAN Gateway/Router/Bridge处理。
├── proc.c--can和can-bcm的proc节点。
├── raw.c--SOCK_RAW类型的CAN协议。

3 CAN子系统数据结构和API

struct can_frame表示CAN总线通信的基本单位内容。

struct can_frame {
    canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
    __u8    can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
    __u8    __pad;   /* padding */
    __u8    __res0;  /* reserved / padding */
    __u8    __res1;  /* reserved / padding */
    __u8    data[CAN_MAX_DLEN] __attribute__((aligned(8)));--表示携带的数据。
};

3.1 分配和释放CAN设备

alloc_candev/alloc_candev_mqs分配并配置CAN网络设备。free_candev释放CAN网络设备空间。

alloc_candev_mqs
  ->alloc_netdev_mqs--分配一个net_device,并创建tx/rx队列。
    ->can_setup--对设备的初始化回调函数。
  ->初始化restart_work,函数为can_restart()。
    ->can_flush_echo_skb
    ->alloc_can_err_skb
    ->netif_rx
    ->netif_carrier_on

3.2 注册和注销CAN设备

register_candev/unregister_candev用于注册或注销CAN设备。

int register_candev(struct net_device *dev);
void unregister_candev(struct net_device *dev);

CAN设备是一个网络设备,注册到网络子系统中:

register_candev
  ->初始化网络设备rtnl_link_ops为can_link_ops。
  ->netif_carrier_off
  ->register_netdev
unregister_candev
  ->unregister_netdev

 CAN作为网络设备的Rtnetlink操作函数如下:

static struct rtnl_link_ops can_link_ops __read_mostly = {
    .kind        = "can",
    .maxtype    = IFLA_CAN_MAX,
    .policy        = can_policy,
    .setup        = can_setup,
    .validate    = can_validate,
    .newlink    = can_newlink,
    .changelink    = can_changelink,
    .dellink    = can_dellink,
    .get_size    = can_get_size,
    .fill_info    = can_fill_info,
    .get_xstats_size = can_get_xstats_size,
    .fill_xstats    = can_fill_xstats,
};

3.3 CAN设备操作

 CAN Core提供如下核心函数给驱动调用:

int open_candev(struct net_device *dev);
void close_candev(struct net_device *dev);
int can_change_mtu(struct net_device *dev, int new_mtu);
int can_restart_now(struct net_device *dev);
void can_bus_off(struct net_device *dev);

4 M_CAN驱动

M_CAN是Bosch提供的支持CAN/CAN FD(Flexible Data-rate)协议控制器。官方说明参考《M_CAN》,用户手册参考《M_CAN User's Manual》。

4.1 M_CAN dts配置

    soc {
        m_can1: can@4400e000 {
            compatible = "bosch,m_can";
            reg = <0x4400e000 0x400>, <0x44011000 0x1400>;
            reg-names = "m_can", "message_ram";
            interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>,
                     <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
            interrupt-names = "int0", "int1";
            clocks = <&scmi0_clk CK_SCMI0_HSE>, <&rcc FDCAN_K>;
            clock-names = "hclk", "cclk";
            bosch,mram-cfg = <0x0 0 0 32 0 0 2 2>;
            status = "disabled";
        };

        m_can2: can@4400f000 {
            compatible = "bosch,m_can";
            reg = <0x4400f000 0x400>, <0x44011000 0x2800>;
            reg-names = "m_can", "message_ram";
            interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>,
                     <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;
            interrupt-names = "int0", "int1";
            clocks = <&scmi0_clk CK_SCMI0_HSE>, <&rcc FDCAN_K>;
            clock-names = "hclk", "cclk";
            bosch,mram-cfg = <0x1400 0 0 32 0 0 2 2>;
            status = "disabled";
        };
    };

4.2 M_CAN驱动程序

M_CAN驱动初始化主要包括:

  • 获取时钟、中断、寄存器等。
  • 使能时钟,获取版本;然后创建net_device并注册。
  • 解析共享内存配置。
  • 初始化LED。
  • 获取max-bitrate信息。
m_can_plat_driver
  ->m_can_plat_probe
    ->devm_clk_get--获取时钟hclk和cclk。
    ->platform_get_resource_byname--获取寄存器m_can,获取共享消息内存message_ram。
    ->获取"bosch,mram-cfg"配置参数。
    ->alloc_candev--
    ->netdev_priv--找到net_device私有数据,然后再初始化。
    ->m_can_clk_start--使能时钟,后续读取M_CAN版本号。
    ->m_can_dev_setup
      ->m_can_check_core_release--检查M_CAN版本号。
      ->netif_napi_add--把net_device和NAPI绑定,当需要轮询是调用m_can_poll函数。
    ->register_m_can_dev
      ->register_candev--注册CAN网络设备,设备操作函数集为m_can_netdev_ops
    ->m_can_of_parse_mram--解析从bosch,mram-cfg中获取的额参数。
    ->devm_can_led_init
    ->of_can_transceiver--获取当前CAN的max-bitrate。
  ->m_can_plat_remove
    ->unregister_m_can_dev--去注册net_device设备。
    ->free_candev--释放net_device资源。

4.2.1 M_CAN poll处理

m_can_poll是使用poll方式读取M_CAN数据:

m_can_poll--根据读取到的irqstatus进行处理。
  ->m_can_handle_state_errors--处理错误状态。
  ->m_can_handle_bus_errors--处理总线错误。
  ->m_can_do_rx_poll--轮询读取CAN数据并向上层传输。
    ->判断CAN fifo中是否有数据。
    ->m_can_read_fifo
      ->alloc_canfd_skb/alloc_can_skb--根据是否CAN或者CANFD选择不同的分配skb函数。
      ->读取寄存器填充CAN帧数据。
      ->响应rx fifo 0.
      ->netif_receive_skb--将接收到的CAN帧上传处理。
    ->m_can_read--读取RX FIFO状态,以供是否继续读取提供依据。

4.2.2 M_CAN net_device操作函数

m_can_netdev_ops是M_CAN作为net_device的操作函数集:

static const struct net_device_ops m_can_netdev_ops = {
    .ndo_open = m_can_open,--打开M_CAN设备。
    .ndo_stop = m_can_close,--关闭M_CAN设备。
    .ndo_start_xmit = m_can_start_xmit,--通过M_CAN收发送数据。
    .ndo_change_mtu = can_change_mtu,--M_CAN设备一次最大传输单元。
};

m_can_open打开CAN设备并准备好数据传输:

  • 打开时钟。
  • 打开CAN设备。
  • 注册中断处理函数。
  • 使能CAN控制器。
  • 使能NAPI以及队列。
m_can_open
  ->m_can_clk_start--使能M_CAN时钟。
  ->open_candev
  ->request_irq
    ->m_can_isr--M_CAN中断处理函数。
  ->m_can_start
  ->can_led_event
  ->napi_enable
  ->netif_start_queue

m_can_start_xmit将数据通过M_CAN发送出去:

m_can_start_xmit
  ->can_dropped_invalid_skb--放弃无效skb。
  ->m_can_tx_fifo_full--检查CAN FIFO是否满。
  ->m_can_fifo_write--填充CAN Frame。
  ->can_put_echo_skb
  ->m_can_write--触发CAN进行传输。

5 CAN调试节点

/proc/net/can调试节点如下:

/proc/net/can
|-- rcvlist_all
|-- rcvlist_eff
|-- rcvlist_err
|-- rcvlist_fil
|-- rcvlist_inv
|-- rcvlist_sff
|-- reset_stats
|-- stats
`-- version

6 CAN相关调试程序

查看CAN网络接口:ifconfig -a。

can0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          NOARP  MTU:16  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:10
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
          Interrupt:58

设置CAN接口速率:

ip link set can0 type can bitrate 1000000

打开can0网卡:

ifconfig can0 up

接收CAN数据:

candump can0--从CAN总线接口接收数据并以十六进制形式打印出来。

发送CAN数据,帧号为0x5A1,数据内容为16进制8个字节11.22.33.44.55.66.77.88:

cansend can0 5A1#11.22.33.44.55.66.77.88--往指定的CAN接口发送数据。

关闭can0:

ifconfig can0 down

其他的can相关工具还包括:

can-calc-bit-timing - 计算CAN时序参数。

cangen - 生成一个CAN帧。

canbusload - 监控CAN总线负荷。

cangw - 管理CAN网关。

canfdtest - CAN FD测试程序。

cansequence - 往指定CAN接口自动重复递增数字,也可以指定接收并校验检查接收的递增数字。

以及canplayer、canlogserver、cansniffer。

参考文档:《Linux 驱动开发——Socket CAN模型》、《Linux应用层例程7 CAN 应用编程基础》。

posted on 2024-03-09 23:59  ArnoldLu  阅读(1667)  评论(0编辑  收藏  举报

导航