首页 > 系统相关 >Linux CAN子系统:基于M_CAN解读

Linux CAN子系统:基于M_CAN解读

时间:2024-03-09 22:35:44浏览次数:25  
标签:00 -- dev 解读 candev Linux device net 子系统

  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数据,帧号为0x5A1,数据内容为16进制8个字节11.22.33.44.55.66.77.88:

cansend can0 5A1#11.22.33.44.55.66.77.88

关闭can0:

ifconfig can0 down

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

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

cangen - 生成一个CAN帧。

canbusload - 监控CAN总线负荷。

cangw - 管理CAN网关。

canfdtest - CAN FD测试程序。

以及canplayer、cansequence、canlogserver、cansniffer。

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

标签:00,--,dev,解读,candev,Linux,device,net,子系统
From: https://www.cnblogs.com/arnoldlu/p/18061649

相关文章

  • 10_Linux连接档概念
    Linux连接档概念1.Linux下的连接档种类一个是类似于win电脑的快捷方式,我们称为软连接,一种是通过文件系统的inode连接来产生的,类似于win电脑的复制,但是不产生新的文件,我们称为硬连接。2.什么是inode?inode是用来存放文件信息的,每个文件都会占用一个inode,并且这个inode号是......
  • 09_Linux权限管理
    Linux权限管理1.为什么要管理Linux权限?很好管理每个用户。控制每个用户对系统权限的访问初次创建的用户要比普通用户有更多的权限。Ubuntu的用户分为3类:1:root(超级用户)2:系统初次创建的用户,比如topeet3:安装完系统之后创建的用户。用户的信息全部被保存在/etc/passwd文件......
  • 08_Linux帮助手册讲解
    Linux帮助手册讲解1.Linux帮助手册使用man命令打开,使用手册一共有九页。1.可执行的程序或者shell命令2.系统调用3.库调用4.设备和特殊文件的帮助,通常在/dev下面5.配置文件的帮助6.游戏的帮助7.杂项的帮助8.超级用户可以执行的系统命令的帮助9.内核相关的。2.使用m......
  • 07_Linux常用命令第二部分
    Linux常用命令第二部分1.mkdir命令功能:创建一个新的文件夹mkdirtest创建一个名为test的文件夹注意:linux下是区分大小写的同一级目录下不能创建文件夹名字一样的文件使用-p参数可以创建多级目录2.rmdir功能:删除一个非空目录rmdirtest删除test文件夹注意:这个命......
  • 关于 Linux 中模拟鼠标
    问题的背景是我想用自动化脚本来玩StardewValley的小游戏,刷钱,但是遇到了一系列问题,这里记录我的一些历程。pyautogui/pydirectinputpyautogui是我第一个考虑的方案。虽然可以正常的移动鼠标,点击,但是游戏内却没有点击事件。搜索发现一般游戏在windows下使用的是directX,所......
  • 详细解读Gitlab Runner中SSH到远程服务器的细节
    生成特有的密钥对用windows的命令行生成即可$ssh-keygen-trsa-C"[email protected]"执行如下图:上述命令行中,密钥保存在C:\Users\d211.ssh目录下:把上述公钥拷贝到目标服务器的对应用户目录下比方我们要在目标服务器,用上述私钥,以用户runner的身份登入。那么我们在目标......
  • Linux/macOS 查看网络接口
    Linux$nmclidevicestatusDEVICETYPESTATECONNECTIONenp0s5ethernetconnectedWiredconnection1docker0bridgeconnected(externally)docker0loloopbackunmanaged......
  • Linux Centos7安装node环境
    1、下载nodeNode.js中文网https://nodejs.cn/download/2、解压tar-xvf****.tar.xz3、配置环境变量vim/etc/profile文件最后增加exportPATH=$PATH:你的目录/binsource/etc/profile让环境变量配置生效4、配置淘宝镜像npmconfigsetregistryhttps://reg......
  • WindTerm的安装及远程连接Linux系统步骤
    目录WindTerm下载WindTerm远程连接LinuxWindTerm下载官网下载链接WindTerm网盘下载链接WindTerm不需要安装,将下载的压缩包解压后直接找到WindTerm.exe执行文件,双击即可运行WindTerm远程连接Linux1.先获取你的Linux的IP地址,以下以Centos7为例系统右建-在终端中打开输入if......
  • 05_Linux相对路径和绝对路径
    Linux相对路径和绝对路径什么是绝对路径?概念:目标位置的完整路径。绝对路径都是从根目录开始,Linux的根目录是/举例:北京海淀区/地球/中国/北京市/海淀区/复兴路/100号什么是相对路径?概念:目标路径是相对于你当前所在位置为基准的路径相对路径一般都是以./开头的举例:我现......