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

在此分享一下对基于AM335X的Linux 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 "\n");

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结构体变量指针pdata,d_can_platform_data结构体类型与板级文件中的平台设备使用的结构体类型是一致的 */

struct d_can_platform_data *pdata;

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

pdata = pdev->dev.platform_data;

if (!pdata) {

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

goto exit;

}

/* allocate the d_can device */

/*分配d_can设备,如can0、can1、…等*/

ndev = alloc_d_can_dev(pdata->num_of_msg_objs);

if (!ndev) {

ret = -ENOMEM;

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

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\n", 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\n", 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\n");

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\n");

ret = -EBUSY;

goto exit_free_clks;

}

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

射到虚拟地址*/

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

if (!addr) {

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

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\n");

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\n");

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)\n",

D_CAN_DRV_NAME, ret);

goto exit_free_device;

}

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

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\n");

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,

};

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

下面我们借用一个开源的socket can工具:canconfig将CAN设备打开。相应的在内核驱动层会相应调用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\n", 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\n");

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\n");

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;

}

曾经的足迹——对Linux CAN驱动的理解(1)的更多相关文章

  1. Linux网络驱动--snull

    snull是<Linux Device Drivers>中的一个网络驱动的例子.这里引用这个例子学习Linux网络驱动. 因为snull的源码,网上已经更新到适合最新内核,而我自己用的还是 ...

  2. 浅谈Android系统移植、Linux设备驱动

    一.Android系统架构 第一层:Linux内核 包括驱动程序,管理内存.进程.电源等资源的程序 第二层:C/C++代码库 包括Linux的.so文件以及嵌入到APK程序中的NDK代码 第三层:An ...

  3. Linux设备驱动模型之I2C总线

    一.I2C子系统总体架构 1.三大组成部分 (1)I2C核心(i2c-core):I2C核心提供了I2C总线驱动(适配器)和设备驱动的注册.注销方法,提供了与具体硬件无关的I2C读写函数. (2)I2 ...

  4. linux设备驱动概述,王明学learn

    linux设备驱动学习-1 本章节主要学习有操作系统的设备驱动和无操作系统设备驱动的区别,以及对操作系统和设备驱动关系的认识. 一.设备驱动的作用 对设备驱动最通俗的解释就是“驱使硬件设备行动” .设 ...

  5. Smart210学习记录------linux串口驱动

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=27025492&id=327609 一.核心数据结构 串口驱动有 ...

  6. linux网卡驱动移植

    这里重要的是物理层PHY receiver,MAC(media access control)层,这里与软件中的协议栈不同,在硬件上MAC是PHY的下一层.DM9000A将MAC和PHY做到一起,也可 ...

  7. Linux USB驱动

    linux usb 驱动详解 一 http://blog.163.com/cl2006ky@126/blog/static/87195173201131245557340/ USB设备驱动开发-USB ...

  8. Linux设备驱动工程师之路——内核链表的使用【转】

    本文转载自:http://blog.csdn.net/forever_key/article/details/6798685 Linux设备驱动工程师之路——内核链表的使用 K-Style 转载请注明 ...

  9. linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-119723.html linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟 xxxxxxxxxx ...

随机推荐

  1. Java中的线程Thread总结

    首先来看一张图,下面这张图很清晰的说明了线程的状态与Thread中的各个方法之间的关系,很经典的! 在Java中创建线程有两种方法:使用Thread类和使用Runnable接口. 要注意的是Threa ...

  2. Linux学习笔记01:Linux下的drwxr-xr-x

    1. drwxr-xr-x 第1字母:表示文件类型     d   ------- 表示文件目录(directory)     -    ------- 表示二进制文件     l    ------ ...

  3. Java基础知识强化38:StringBuffer类之StringBuffer的添加功能

    1. StringBuffer的添加功能: public  StringBuffer append(String str):可以把任意类型数据添加到字符串缓冲区里面,并返回字符串缓冲区本身. publ ...

  4. angularjs金额大写过滤器

    数字转中文 MyAppFilter.filter('rmbFilter',[function(){ function ChinaCost(input){ var numberValue=new Str ...

  5. C# typeof Gettype is as &拆箱 装箱

    有时候,我们不想用值类型的值,就是想用一个引用..Net提供了一个名为装箱(boxing)的机制,它允许根据值类型来创建一个对象,然后使用对这个新对象的一个引用. 首先,回顾两个重要的事实,1.对于引 ...

  6. css实现ie6以上文字高度未知垂直居中

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...

  7. XML中 添加或修改时 xmlns="" 怎么删除

    //创建节点时 记得加上  ---> xmldoc.DocumentElement.NamespaceURI XmlElement url = xmldoc.CreateElement(&quo ...

  8. ReportViewer导出功能筛选

    ReportViewer只能导出Excel,把导出Word和PDF功能去掉 <rsweb:ReportViewer ID="ReportViewer1" runat=&quo ...

  9. 记一次lnmp环境下无法执行php文件

    lnmp环境搭建好后却无法正常执行php文件,坑爹啊!~ [错误状况] 页面直接打印出php代码内容: php文件无法执行?: 查看nginx配置文件: server { listen 80; ser ...

  10. [Android分享] 如何解决Android 5.0中出现的警告:Service Intent must be explicit

    Android 5.0程序运行报Service Intent must be explicit错误,原因是5.0的service必须显式调用 改成 Intent intent = new Intent ...