zoukankan      html  css  js  c++  java
  • 曾经的足迹——对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;

    }


  • 相关阅读:
    了解 NoSQL 的必读资料
    关于什么时候用assert(断言)的思考
    这次见到了一些大侠
    NetBeans 时事通讯(刊号 # 87 Jan 12, 2010)
    动态链接库dll,静态链接库lib, 导入库lib
    新女性十得 写得了代码,查得出异常
    记录系统乱谈
    新女性十得 写得了代码,查得出异常
    fullpage.js禁止滚动
    RunningMapReduceExampleTFIDF hadoopclusternet This document describes how to run the TFIDF MapReduce example against ascii books. This project is for those who wants to experiment hadoop as a skunkworks in a small cluster (110 nodes) Google Pro
  • 原文地址:https://www.cnblogs.com/pangblog/p/3341766.html
Copyright © 2011-2022 走看看