曾经的足迹——对Linux CAN驱动的理解(1)
在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)的更多相关文章
- Linux网络驱动--snull
snull是<Linux Device Drivers>中的一个网络驱动的例子.这里引用这个例子学习Linux网络驱动. 因为snull的源码,网上已经更新到适合最新内核,而我自己用的还是 ...
- 浅谈Android系统移植、Linux设备驱动
一.Android系统架构 第一层:Linux内核 包括驱动程序,管理内存.进程.电源等资源的程序 第二层:C/C++代码库 包括Linux的.so文件以及嵌入到APK程序中的NDK代码 第三层:An ...
- Linux设备驱动模型之I2C总线
一.I2C子系统总体架构 1.三大组成部分 (1)I2C核心(i2c-core):I2C核心提供了I2C总线驱动(适配器)和设备驱动的注册.注销方法,提供了与具体硬件无关的I2C读写函数. (2)I2 ...
- linux设备驱动概述,王明学learn
linux设备驱动学习-1 本章节主要学习有操作系统的设备驱动和无操作系统设备驱动的区别,以及对操作系统和设备驱动关系的认识. 一.设备驱动的作用 对设备驱动最通俗的解释就是“驱使硬件设备行动” .设 ...
- Smart210学习记录------linux串口驱动
转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=27025492&id=327609 一.核心数据结构 串口驱动有 ...
- linux网卡驱动移植
这里重要的是物理层PHY receiver,MAC(media access control)层,这里与软件中的协议栈不同,在硬件上MAC是PHY的下一层.DM9000A将MAC和PHY做到一起,也可 ...
- Linux USB驱动
linux usb 驱动详解 一 http://blog.163.com/cl2006ky@126/blog/static/87195173201131245557340/ USB设备驱动开发-USB ...
- Linux设备驱动工程师之路——内核链表的使用【转】
本文转载自:http://blog.csdn.net/forever_key/article/details/6798685 Linux设备驱动工程师之路——内核链表的使用 K-Style 转载请注明 ...
- linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-119723.html linux设备驱动归纳总结(十三):1.触摸屏与ADC时钟 xxxxxxxxxx ...
随机推荐
- 设计模式(3)-对象创建型模式-Abstract Factory模式
1.对象创建型模式 1.3 Abstract Factory模式 1.3.1 需求 在下面情况能够使用Abstract Factory模式: • 一个系统要独立于它的产品的创建. ...
- LayoutInflater 原理分析 示例
LayoutInflater简介 LayoutInflater 顾名思义就是布局填充器,做过Android界面编程,相信对这个类都比较熟悉,可能有人说,我们在activity中使用set ...
- python 下的数据结构与算法---4:线形数据结构,栈,队列,双端队列,列表
目录: 前言 1:栈 1.1:栈的实现 1.2:栈的应用: 1.2.1:检验数学表达式的括号匹配 1.2.2:将十进制数转化为任意进制 1.2.3:后置表达式的生成及其计算 2:队列 2.1:队列的实 ...
- ArrayList 练习
ArrayList list = new ArrayList(); Random rd = new Random(); ; i <; i++) { , ); //是否包含当前数字 if (!li ...
- (一)HTML5 - pushState 无刷新更新地址
可以解决什么问题: 可以实现网页的ajax加载,同时又能完成URL的改变而没有网页跳转刷新的迹象,就像是改变了网页的hash(#)一样. 优于hash: 避免了改变hash的问题,避免了用户不理解UR ...
- 为CKEDITOR内容中图片加上 图片服务器路径
做网站的时候,前台和后台是分开的, 用了CKEDITOR上传图片,但是发现内容带图片的时候,前台Web浏览的时候是一个红X,一看路径不对,上传的到数据库中的是相对的虚拟路径,例如:<img al ...
- Css 应用一
Placeholder使用 CSS3里有相应的通用的对Placeholder提示信息美化的方法.你可以设置提示信息文字的颜色,透明度,背景色等. 为了最大化的兼容所有浏览器,给CSS里的placeho ...
- 网狐6603 cocos2dx 棋牌、捕鱼、休闲类游戏《李逵捕鱼》手机端完整源码分析及分享
该资源说明: cocos2d 棋牌.捕鱼.休闲类游戏<李逵捕鱼>手机端完整源码,网狐6603配套手机版源码,可以选桌子,适合新手学习参考,小编已亲测试,绝对完整可编译手机端,下载后将文件考 ...
- windows下配置lamp环境(4)---安装MySQL数据库5.6
图解安装MySQL数据库 1.获取软件就多不说了 2.双击开始安装 3.点击点击NEXT进行下一步 4.同意协议,点击NEXT进入下一步 5.选择指定安装位置安装方法,进入安装位值选择页面: 6.分别 ...
- destoon系统商城加淘宝客按钮方法
destoon系统很多喜欢运营B2B的站长都在用,其中的商城模块常常被用来做淘宝客,其中的难点是如何把购买按钮做成淘宝客地址,这个问题的修改在论坛上被叫价50元,下面小编把这个实用的方法分享下,希望对 ...