• 曾经的足迹——对Linux CAN驱动的理解(1)


    在TiAM335X系列Cortext-A8芯片中,CAN模块采用D_CAN结构,实质即两路CAN接口。

    在此分享一下对基于AM335XLinux CAN驱动源码的理解。下面来分析它的驱动源码及其工作方式。

    Linux内核源码中,CAN设备驱动文件如下:

    drivers/net/can/d_can/d_can_platform.c

    drivers/net/can/d_can/d_can.c

    drivers/net/can/d_can/d_can.h

    首先分析d_can_platform.c文件,驱动运行时,也是先从这里开始。首先是驱动入口函数:

    module_init(d_can_plat_init);

    static int __init d_can_plat_init(void)

    {

    printk(KERN_INFO D_CAN_DRV_DESC " ");

    return platform_driver_register(&d_can_plat_driver);

    }

    在驱动入口函数d_can_plat_init()中,使用platform_driver_register(&d_can_plat_driver)将结构体变量d_can_plat_driver注册为平台驱动。

    static struct platform_driver d_can_plat_driver = {

    .driver = {

    .name = D_CAN_DRV_NAME,

    .owner = THIS_MODULE,

    },

    .probe = d_can_plat_probe,

    .remove = __devexit_p(d_can_plat_remove),

    };

    平台驱动中,最重要的是探测函数d_can_plat_probe。探测函数主要的工作是获取平台设备传递过来的资源及初始化硬件。下面来看看d_can_plat_probe() 函数都做了些什么工作。

    static int __devinit d_can_plat_probe(struct platform_device *pdev)

    {

    int ret = 0;

    void __iomem *addr;

    struct net_device *ndev;

    struct d_can_priv *priv;

    struct resource *mem;

    /* 定义d_can_platform_data结构体变量指针pdatad_can_platform_data结构体类型与板级文件中的平台设备使用的结构体类型是一致的 */

    struct d_can_platform_data *pdata;

    /*获取平台设备数据*/

    pdata = pdev->dev.platform_data;

    if (!pdata) {

    dev_err(&pdev->dev, "No platform data ");

    goto exit;

    }

    /* allocate the d_can device */

    /*分配d_can设备,如can0can1*/

    ndev = alloc_d_can_dev(pdata->num_of_msg_objs);

    if (!ndev) {

    ret = -ENOMEM;

    dev_err(&pdev->dev, "alloc_d_can_dev failed ");

    goto exit;

    }

    /*获取设备私有数据*/

    priv = netdev_priv(ndev);

    /*获取时钟并使能*/

    priv->fck = clk_get(&pdev->dev, pdata->fck_name);

    if (IS_ERR(priv->fck)) {

    dev_err(&pdev->dev, "%s is not found ", pdata->fck_name);

    ret = -ENODEV;

    goto exit_free_ndev;

    }

    clk_enable(priv->fck);

     

    /*获取时钟并使能*/

    priv->ick = clk_get(&pdev->dev, pdata->ick_name);

    if (IS_ERR(priv->ick)) {

    dev_err(&pdev->dev, "%s is not found ", pdata->ick_name);

    ret = -ENODEV;

    goto exit_free_fck;

    }

    clk_enable(priv->ick);

    /* get the platform data */

    /*获取平台内存资源*/

    mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);

    if (!mem) {

    ret = -ENODEV;

    dev_err(&pdev->dev, "No mem resource ");

    goto exit_free_clks;

    }

    /*申请I/O内存*/

    if (!request_mem_region(mem->start, resource_size(mem),

    D_CAN_DRV_NAME)) {

    dev_err(&pdev->dev, "resource unavailable ");

    ret = -EBUSY;

    goto exit_free_clks;

    }

    /*在内核中访问 I/O 内存之前,需首先使用 ioremap()函数将设备所处的物理地址映

    射到虚拟地址*/

    addr = ioremap(mem->start, resource_size(mem));

    if (!addr) {

    dev_err(&pdev->dev, "ioremap failed ");

    ret = -ENOMEM;

    goto exit_release_mem;

    }

    /* IRQ specific to Error and status & can be used for Message Object */

    ndev->irq = platform_get_irq_byname(pdev, "int0");

    if (!ndev->irq) {

    dev_err(&pdev->dev, "No irq0 resource ");

    goto exit_iounmap;

    }

    /* IRQ specific for Message Object */

    priv->irq_obj = platform_get_irq_byname(pdev, "int1");

    if (!priv->irq_obj) {

    dev_err(&pdev->dev, "No irq1 resource ");

    goto exit_iounmap;

    }

    priv->base = addr;

    priv->can.clock.freq = clk_get_rate(priv->fck);

    priv->test_mode = pdata->test_mode_enable;

    platform_set_drvdata(pdev, ndev);

    SET_NETDEV_DEV(ndev, &pdev->dev);

    /*注册CAN网络设备*/

    ret = register_d_can_dev(ndev);

    if (ret) {

    dev_err(&pdev->dev, "registering %s failed (err=%d) ",

    D_CAN_DRV_NAME, ret);

    goto exit_free_device;

    }

    dev_info(&pdev->dev, "%s device registered (irq=%d, irq_obj=%d) ",

    D_CAN_DRV_NAME, ndev->irq, priv->irq_obj);

    return 0;

    exit_free_device:

    platform_set_drvdata(pdev, NULL);

    exit_iounmap:

    iounmap(addr);

    exit_release_mem:

    release_mem_region(mem->start, resource_size(mem));

    exit_free_clks:

    clk_disable(priv->ick);

    clk_put(priv->ick);

    exit_free_fck:

    clk_disable(priv->fck);

    clk_put(priv->fck);

    exit_free_ndev:

    free_d_can_dev(ndev);

    exit:

    dev_err(&pdev->dev, "probe failed ");

    return ret;

    }

    在d_can_plat_probe()函数中调用register_d_can_dev()注册CAN为网络设备。函数register_d_can_dev()在文件drivers/net/can/d_can/d_can.c中。通过EXPORT_SYMBOL_GPL宏导出。

    int register_d_can_dev(struct net_device *dev)

    {

    /* we support local echo */

    dev->flags |= IFF_ECHO;

    dev->netdev_ops = &d_can_netdev_ops;

    return register_candev(dev);

    }

    EXPORT_SYMBOL_GPL(register_d_can_dev);

    在register_d_can_dev()函数中填充其网络设备操作函数成员dev->netdev_ops= &d_can_netdev_ops

    static const struct net_device_ops d_can_netdev_ops = {

    .ndo_open = d_can_open,

    .ndo_stop = d_can_close,

    .ndo_start_xmit = d_can_start_xmit,

    };

    由于LinuxCAN驱动是写成了socket can的架构,即将其模拟成网络设备。因此我们可以借鉴操作网络设备的方法,进行socket can的应用编程。

    下面我们借用一个开源的socket can工具:canconfigCAN设备打开。相应的在内核驱动层会相应调用d_can_open()函数。

    static int d_can_open(struct net_device *ndev)

    {

    int err;

    struct d_can_priv *priv = netdev_priv(ndev);

    /* Open common can device */

    err = open_candev(ndev);

    if (err) {

    netdev_err(ndev, "open_candev() failed %d ", err);

    return err;

    }

     

    /* register interrupt handler for Message Object (MO) and Error + status change (ES) */

    err = request_irq(ndev->irq, &d_can_isr, IRQF_SHARED, ndev->name,

    ndev);

    if (err) {

    netdev_err(ndev, "failed to request MO_ES interrupt ");

    goto exit_close_candev;

    }

    /* register interrupt handler for only Message Object */

    err = request_irq(priv->irq_obj, &d_can_isr, IRQF_SHARED, ndev->name,

    ndev);

    if (err) {

    netdev_err(ndev, "failed to request MO interrupt ");

    goto exit_free_irq;

    }

    /* start the d_can controller */

    // d_can_start(ndev);

     

    napi_enable(&priv->napi);

    netif_start_queue(ndev);

    d_can_start(ndev); //embest

    return 0;

    exit_free_irq:

    free_irq(ndev->irq, ndev);

    exit_close_candev:

    close_candev(ndev);

    return err;

    }


  • 相关阅读:
    string数组批量转换成Int数组
    TCP/IP 、 HTTP 、HTTPS
    静态布局、自适应布局、流式布局、响应式布局、弹性布局等的概念和区别
    Vue源码学习02 初始化模块init.js
    IOS8白屏
    VUE 源码学习01 源码入口
    http状态码
    vue全家桶(Vue+Vue-router+Vuex+axios)(Vue+webpack项目实战系列之二)
    Vue实战Vue-cli项目构建(Vue+webpack系列之一)
    module.exports,exports,export和export default,import与require区别与联系【原创】
  • 原文地址:https://www.cnblogs.com/pangblog/p/3341766.html
Copyright © 2020-2023  润新知