platform总线、设备、驱动模型之led驱动实例
2016-05-30 16:23
507 查看
在
Linux 2.6 的设备驱动模型中,关心总线、设备和驱动这
3 个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
注意,所谓的platform_device并不是与字符设备、块设备和网络设备并列的概念,而是
Linux系统提供的一种附加手段
platform.device结构体
platform_driver
这个结构体中包含 probe()、remove()、shutdown()、suspend()、resume()函数,通常也需要由驱动实现。
系统中为platform
总线定义了一个
bus_type 的实例
platform_bus_type。
这里要重点关注其match()成员函数,正是此成员函数确定了platform_device
和
platform_driver之间如何匹配。
匹配platform_device
和
platform_driver 主要看两者的
name字段是否相同。
匹配实例:以触摸屏驱动分析;
平台设备部分:
平台设备列表:
将设备结构s3c_device_ts
注册进内核:
平台驱动部分;
内核启动过程中,
当平台设备的名字“ s3c64xx-ts” 和平台驱动 ID 列表成员的名字匹配时, probe函数被执行,这里是函数 s3c2410ts_probe()。
对platform_device
的定义通常在
BSP 的板文件中实现,在板文件中,将
platform_device归纳为一个数组,最终通过platform_add_devices()函数统一注册。platform_add_devices()函数可以将平台设备添加到系统中,这个函数的原型为:
该函数的第一个参数为平台设备数组的指针,第二个参数为平台设备的数量,它内部调用了platform_device_register()函数用于注册单个的平台设备
platform_device结构体定义的第
5~6行,描述了
platform_device的资源,资源本身由
resource
结构体描述:
设备驱动中引入platform
的概念至少有如下两大好处:
(1)使得设备被挂接在一个总线上,因此,符合Linux
2.6 的设备模型。其结果是,配套的sysfs
结点、设备电源管理都成为可能。
(
2)隔离
BSP 和驱动。在
BSP 中定义
platform 设备和设备使用的资源、设备的具体配置信息,而在驱动中,只需要通过通用API
去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。
led platform驱动实例:
内核启动过程中, 当平台设备的名字“myled"驱动 ID 列表成员的名字匹配时, probe函数被执行,这里是函数 s3c2410ts_probe()
Linux 2.6 的设备驱动模型中,关心总线、设备和驱动这
3 个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
注意,所谓的platform_device并不是与字符设备、块设备和网络设备并列的概念,而是
Linux系统提供的一种附加手段
platform.device结构体
1 struct platform_device { 2 const char * name; / * 设备名 */ 3 u32 id; 4 struct device dev; 5 u32 num_resources; / * 设备所使用各类资源数量 */ 6 struct resource * resource; / * 资源 */ 7 };
platform_driver
这个结构体中包含 probe()、remove()、shutdown()、suspend()、resume()函数,通常也需要由驱动实现。
1 struct platform_driver { 2 int (*probe)(struct platform_device *); 3 int (*remove)(struct platform_device *); 4 void (*shutdown)(struct platform_device *); 5 int (*suspend)(struct platform_device *, pm_message_t state); 6 int (*suspend_late)(struct platform_device *, pm_message_t state); 7 int (*resume_early)(struct platform_device *); 8 int (*resume)(struct platform_device *); 9 struct pm_ext_ops *pm; 10 struct device_driver driver; 11};
系统中为platform
总线定义了一个
bus_type 的实例
platform_bus_type。
1 struct bus_type platform_bus_type = { 2 .name = "platform", 3 .dev_attrs = platform_dev_attrs, 4 .match = platform_match, 5 .uevent = platform_uevent, 6 .pm = PLATFORM_PM_OPS_PTR, 7 }; 8 EXPORT_SYMBOL_GPL(platform_bus_type);
这里要重点关注其match()成员函数,正是此成员函数确定了platform_device
和
platform_driver之间如何匹配。
1 static int platform_match(struct device *dev, struct device_driver *drv) 2 { 3 struct platform_device *pdev; 4 5 pdev = container_of(dev, struct platform_device, dev); 6 return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0); 7 }
匹配platform_device
和
platform_driver 主要看两者的
name字段是否相同。
匹配实例:以触摸屏驱动分析;
平台设备部分:
平台设备列表:
将设备结构s3c_device_ts
注册进内核:
平台驱动部分;
内核启动过程中,
当平台设备的名字“ s3c64xx-ts” 和平台驱动 ID 列表成员的名字匹配时, probe函数被执行,这里是函数 s3c2410ts_probe()。
对platform_device
的定义通常在
BSP 的板文件中实现,在板文件中,将
platform_device归纳为一个数组,最终通过platform_add_devices()函数统一注册。platform_add_devices()函数可以将平台设备添加到系统中,这个函数的原型为:
int platform_add_devices(struct platform_device **devs, int num);
该函数的第一个参数为平台设备数组的指针,第二个参数为平台设备的数量,它内部调用了platform_device_register()函数用于注册单个的平台设备
platform_device结构体定义的第
5~6行,描述了
platform_device的资源,资源本身由
resource
结构体描述:
1 struct resource { 2 resource__size_t start; 3 resource_size_t end; 4 const char *name; 5 unsigned long flags; 6 struct resource *parent, *sibling, *child; 7 };
设备驱动中引入platform
的概念至少有如下两大好处:
(1)使得设备被挂接在一个总线上,因此,符合Linux
2.6 的设备模型。其结果是,配套的sysfs
结点、设备电源管理都成为可能。
(
2)隔离
BSP 和驱动。在
BSP 中定义
platform 设备和设备使用的资源、设备的具体配置信息,而在驱动中,只需要通过通用API
去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。
led platform驱动实例:
led_dev.c:定义资源
#include <linux/module.h> #include <linux/version.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/interrupt.h> #include <linux/list.h> #include <linux/timer.h> #include <linux/init.h> #include <linux/serial_core.h> #include <linux/platform_device.h> /* 分配/设置/注册一个platform_device */ static struct resource led_resource[] = { [0] = { .start = 0xE0200C40, .end = 0xE0200C40 + 8 - 1, .flags = IORESOURCE_MEM, }, [1] = { .start = 4, .end = 4, .flags = IORESOURCE_IRQ, } }; static void led_release(struct device * dev) { } static struct platform_device led_dev = { .name = "<span style="color:#ff6666;">myled</span>", .id = -1, .num_resources = ARRAY_SIZE(led_resource), .resource = led_resource, .dev = { .release = led_release, }, }; static int led_dev_init(void) { platform_device_register(&led_dev); return 0; } static void led_dev_exit(void) { platform_device_unregister(&led_dev); } module_init(led_dev_init); module_exit(led_dev_exit); MODULE_LICENSE("GPL");
led_drv.c驱动程序:
/* 分配/设置/注册一个platform_driver */ #include <linux/module.h> #include <linux/version.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/sched.h> #include <linux/pm.h> #include <linux/sysctl.h> #include <linux/proc_fs.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/input.h> #include <linux/irq.h> #include <asm/uaccess.h> #include <asm/io.h> static int major; static struct class *cls; static volatile unsigned long *gpio_con; static volatile unsigned long *gpio_dat; static int pin; static int leds_open(struct inode *inode, struct file *file) { printk("led_open\n"); *gpio_con = 0x11111111; return 0; } static long leds_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) { printk("use ioctl\n"); if(cmd == 1){ printk("arg = = %d\n",arg); printk("cmd = = %d\n",cmd); *gpio_dat =0x1f;// ~(0<<4); printk("gpio_dat == %0x\n",*gpio_dat); } else *gpio_dat &= 0x00; } static struct file_operations led_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = leds_open, .unlocked_ioctl = leds_ioctl, }; static int led_probe(struct platform_device *pdev) { struct resource *res; res = platform_get_resource(pdev,IORESOURCE_MEM,0); gpio_con = ioremap(res->start,res->end - res->start + 1); gpio_dat = gpio_con + 1; res = platform_get_resourse(pdev,IORESOURCE_IRQ,0); pin = res -> start; printk("led probe\n"); major = register_chrdev(0 , "myled", &led_fops); cls = class_create(THIS_MODULE,"myled"); class_device_create(cls,NULL,MKDEV(major,0),NULL,"led"); return 0; } static int led_remove(struct platform_device *pdev) { printk("led remove\n"); return 0; } struct platform_driver led_drv = { .probe = led_probe, .remove = led_remove, .driver = { .name = "<span style="color:#ff6666;">myled</span>", } }; static int led_drv_init(void) { platform_driver_register(&led_drv); return 0; } static void led_drv_exit(void) { platform_driver_unregister(&led_drv); } module_init(led_drv_init); module_exit(led_drv_exit); MODULE_LICENSE("GPL");
内核启动过程中, 当平台设备的名字“myled"驱动 ID 列表成员的名字匹配时, probe函数被执行,这里是函数 s3c2410ts_probe()
相关文章推荐
- Linux socket 初步
- Linux Kernel 4.0 RC5 发布!
- linux lsof详解
- linux 文件权限
- Linux 执行数学运算
- 10 篇对初学者和专家都有用的 Linux 命令教程
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程
- Linux 下无损图片压缩小工具介绍