发布时间:2014-09-05 17:41:32作者:知识屋
按照前面理解的设备模型,今天又制作了一个。平台设备版本的 "hello world".
如果选用平台设备作为总线,那么在设备模型这个三角关系中缺少的就只有 device和 driver了。那么现在的问题有两个:
1).怎么把device挂到平台设备bus的设备链表上。
2).怎么把driver添加到总线的设备链表上。
要添加一个device到总线的设备列表上,使用的通用函数是 device_register() ,但对于平台设备而言,有一个在该函数上封装的更简单易用的注册函数platform_driver_register()。总之就是使用这两个函数来完成注册,而具体的注册有两种方法:
a). 利用板级文件中的设备初始化,将自己的device添加到对应的设备数值中,那么在系统加载时就会自动加载该设备。但这有一个问题就是需要重新编译内核。
b). 利用模块加载,既然模块式内核留给用户的内核门户。那么它除了用来加载驱动之外,就一定可以用来对内核进行修改。下面的程序就是采用了第二种方法。
下面是一个简单例子
// for test driver#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/cdev.h>#include <asm/uaccess.h>#include <linux/device.h>#include <linux/platform_device.h>#define HELLO_NAM "hello_test"static struct resource hello_res[] = { [0] = { .start = 0, .end = 0x1, .flags = IORESOURCE_MEM, }, [1] = { .start = 0x2, .end = 0x3, .flags = IORESOURCE_MEM, },};static struct platform_device hello_device = { .name = HELLO_NAM, .id = 0, .num_resources = ARRAY_SIZE(hello_res), .resource = hello_res,};static int __init hello_init( void){ return platform_device_register( &hello_device);}static void __exit hello_exit(){ platform_device_unregister( &hello_device);}MODULE_LICENSE("GPL");module_init( hello_init);module_exit( hello_exit);
在上面device已经成功添加到bus上去了之后,现在就剩下将driver加载上去了。在通用的驱动中使用的信息记录结构体是device_driver,但为了使用方便平台设备又有一个封装结构体platform_driver。在通用的驱动中使用的驱动注册函数是driver_register() ,在设备平台中的封装产品是platform_driver_register() 。那么制作一个完整的例子就是
#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/cdev.h>#include <asm/uaccess.h>#include <linux/device.h>#include <linux/platform_device.h>#define HELLO_NAME "hello_test"static int hello_probe(struct platform_device *dev){ struct resource *pRes = NULL; printk("hello_probe............/n"); pRes = platform_get_resource( dev, IORESOURCE_MEM, 1); if( NULL!=pRes) { printk(" resource : %d, %d, %s/n", pRes->start, pRes->end, pRes->name); } device_register(struct device * dev); return 0; //返回0表示接受这次探测}static int hello_remove(struct platform_device *dev){ printk(" hello_remove........................./n"); return 0;}static struct platform_driver hello_drv = { .probe = hello_probe, .remove = hello_remove, .driver = { .name = HELLO_NAME, .owner = THIS_MODULE, },};static int __init hello_init( void){ driver_register(struct device_driver * drv); return platform_driver_register( &hello_drv);}static void __exit hello_exit( void){ platform_driver_unregister( &hello_drv);}MODULE_LICENSE("GPL");module_init( hello_init);module_exit( hello_exit);
从platform源码可以看出其只是设备模型的一个实例。来看一下platforn_device。它的信息结构体是
struct platform_device { const char * name; //设备名,会用来和驱动进行匹配 int id; // struct device dev; //通用设备成员 u32 num_resources; //资源数 struct resource * resource; //资源数组 struct platform_device_id *id_entry;};
从信息结构体中看到,platform_device包含了一个通用设备结构体成员dev。dev就是最终在总线上工作的信息节点。再来看注册函数
int platform_device_add(struct platform_device *pdev){ int i, ret = 0; if (!pdev) return -EINVAL; // if (!pdev->dev.parent) pdev->dev.parent = &platform_bus; pdev->dev.bus = &platform_bus_type; if (pdev->id != -1) dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); else dev_set_name(&pdev->dev, pdev->name); //将资源加入一个全局的资源链表 for (i = 0; i < pdev->num_resources; i++) { struct resource *p, *r = &pdev->resource[i]; if (r->name == NULL) r->name = dev_name(&pdev->dev); p = r->parent; if (!p) { if (resource_type(r) == IORESOURCE_MEM) //默认的资源链表 p = &iomem_resource; else if (resource_type(r) == IORESOURCE_IO) p = &ioport_resource; } if (p && insert_resource(p, r)) { printk(KERN_ERR "%s: failed to claim resource %d/n", dev_name(&pdev->dev), i); ret = -EBUSY; goto failed; } } //在这里就看到了最熟悉的身影 ret = device_add(&pdev->dev); ......}
最后也就是靠"device_add(&pdev->dev) ",完成了将设备添加到总线(platform总线)的任务。之后的操作就是一般的通用设备注册操作了。
那么现在再来看看,driver部分,其信息结构体为
struct platform_driver { int (*probe)(struct platform_device *); //作为探针函数存在,返回0表示选中 int (*remove)(struct platform_device *); void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); int (*suspend_late)(struct platform_device *, pm_message_t state); int (*resume_early)(struct platform_device *); int (*resume)(struct platform_device *); struct device_driver driver; //通用设备驱动成员 struct platform_device_id *id_table;};
这里除了包含了最核心的通用设备驱动成员外,还包含了一些回调函数接口。
int platform_driver_register(struct platform_driver *drv){ //该驱动属于平台设备总线 drv->driver.bus = &platform_bus_type; if (drv->probe) drv->driver.probe = platform_drv_probe; if (drv->remove) drv->driver.remove = platform_drv_remove; if (drv->shutdown) drv->driver.shutdown = platform_drv_shutdown; if (drv->suspend) drv->driver.suspend = platform_drv_suspend; if (drv->resume) drv->driver.resume = platform_drv_resume; //熟悉的身影 return driver_register(&drv->driver);}
通过其注册代码,很容易又找到了"driver_register(&drv->driver) "。
通过对上面的device和driver两部分的分析,很容易就能看到设备模型与平台设备的关系。
linux一键安装web环境全攻略 在linux系统中怎么一键安装web环境方法
Linux网络基本网络配置方法介绍 如何配置Linux系统的网络方法
Linux下DNS服务器搭建详解 Linux下搭建DNS服务器和配置文件
对Linux进行详细的性能监控的方法 Linux 系统性能监控命令详解
linux系统root密码忘了怎么办 linux忘记root密码后找回密码的方法
Linux基本命令有哪些 Linux系统常用操作命令有哪些
Linux必学的网络操作命令 linux网络操作相关命令汇总
linux系统从入侵到提权的详细过程 linux入侵提权服务器方法技巧
linux系统怎么用命令切换用户登录 Linux切换用户的命令是什么
在linux中添加普通新用户登录 如何在Linux中添加一个新的用户
2012-07-10
CentOS 6.3安装(详细图解教程)
Linux怎么查看网卡驱动?Linux下查看网卡的驱动程序
centos修改主机名命令
Ubuntu或UbuntuKyKin14.04Unity桌面风格与Gnome桌面风格的切换
FEDORA 17中设置TIGERVNC远程访问
StartOS 5.0相关介绍,新型的Linux系统!
解决vSphere Client登录linux版vCenter失败
LINUX最新提权 Exploits Linux Kernel <= 2.6.37
nginx在网站中的7层转发功能