Linux 设备驱动开发 —— platform设备驱动应用实例解析
前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 —— platform 设备驱动 ,下面将通过一个实例来深入我们的学习。
一、platform 驱动的工作过程
platform模型驱动编程,platform 驱动只是在字符设备驱动外套一层platform_driver 的外壳。
在一般情况下,2.6内核中已经初始化并挂载了一条platform总线在sysfs文件系统中。那么我们编写platform模型驱动时,需要完成两个工作:
a -- 实现platform驱动
架构就很简单,如下所示:
platform驱动模型三个对象:platform总线、platform设备、platform驱动。
platform总线对应的内核结构:struct bus_type-->它包含的最关键的函数:match() (要注意的是,这块由内核完成,我们不参与)
platform设备对应的内核结构:struct platform_device-->注册:platform_device_register(unregister)
platform驱动对应的内核结构:struct platform_driver-->注册:platform_driver_register(unregister)
那具体platform驱动的工作过程是什么呢:
如果先注册设备,驱动还没有注册,那么设备在被注册到总线上时,将不会匹配到与自己同名的驱动,然后在驱动注册到总线上时,因为设备已注册,那么总线会立即匹配与绑定这时的同名的设备与驱动,再调用驱动中的probe函数等;
如果是驱动先注册,同设备驱动一样先会匹配失败,匹配失败将导致它的probe函数暂不调用,而是要等到设备注册成功并与自己匹配绑定后才会调用。
二、实现platform 驱动与设备的详细过程
1、思考问题?
在分析platform 之前,可以先思考一下下面的问题:
a -- 为什么要用 platform 驱动?不用platform驱动可以吗?
b -- 设备驱动中引入platform 概念有什么好处?
现在先不回答,看完下面的分析就明白了,后面会附上总结。
2、platform_device 结构体 VS platform_driver 结构体
这两个结构体分别描述了设备和驱动,二者有什么关系呢?先看一下具体结构体对比
设备(硬件部分):中断号,寄存器,DMA等
platform_device 结构体
|
驱动(软件部分)
platform_driver 结构体
|
struct platform_device {
const char *name; 名字
int id;
bool id_auto;
struct device dev; 硬件模块必须包含该结构体
u32 num_resources; 资源个数
struct resource *resource; 资源 人脉
const struct platform_device_id *id_entry;
/* arch specific additions */
struct pdev_archdata archdata;
};
|
struct platform_driver {
int (*probe)(struct platform_device *);
硬件和软件匹配成功之后调用该函数
int (*remove)(struct platform_device *);
硬件卸载了调用该函数
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;内核里所有的驱动程序必须包含该结构体
const struct platform_device_id *id_table; 八字
};
|
设备实例:
static struct platform_device hello_device=
{
.name = "bigbang",
.id = -1,
.dev.release = hello_release,
};
|
驱动实例:
static struct platform_driver hello_driver=
{
.driver.name = "bigbang",
.probe = hello_probe,
.remove = hello_remove,
};
|
前面提到,实现platform模型的过程就是总线对设备和驱动的匹配过程 。打个比方,就好比相亲,总线是红娘,设备是男方,驱动是女方:
a -- 红娘(总线)负责男方(设备)和女方(驱动)的撮合;
b -- 男方(女方)找到红娘,说我来登记一下,看有没有合适的姑娘(汉子)—— int (*probe)(struct platform_device *) 匹配成功后驱动执行的第一个函数),当然如果男的跟小三跑了(设备卸载),女方也不会继续待下去的(
int (*remove)(struct platform_device *))。
3、设备资源结构体
在struct platform_device 结构体中有一重要成员 struct resource *resource
- struct resource {
- resource_size_t start; 资源起始地址
- resource_size_t end; 资源结束地址
- const char *name;
- unsigned long flags; 区分是资源什么类型的
- struct resource *parent, *sibling, *child;
- };
- #define IORESOURCE_MEM 0x00000200
- #define IORESOURCE_IRQ 0x00000400
flags 指资源类型,我们常用的是 IORESOURCE_MEM、IORESOURCE_IRQ 这两种。start 和 end 的含义会随着 flags而变更,如
a -- flags为IORESOURCE_MEM 时,start 、end 分别表示该platform_device占据的内存的开始地址和结束值;
b -- flags为 IORESOURCE_IRQ
时,start 、end 分别表示该platform_device使用的中断号的开始地址和结束值;
下面看一个实例:
- static struct resource beep_resource[] =
- {
- [0] = {
- .start = 0x114000a0,
- .end = 0x114000a0+0x4,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = 0x139D0000,
- .end = 0x139D0000+0x14,
- .flags = IORESOURCE_MEM,
- },
- };
- static struct file_operations hello_ops=
- {
- .open = hello_open,
- .release = hello_release,
- .unlocked_ioctl = hello_ioctl,
- };
- static int hello_remove(struct platform_device *pdev)
- {
- 注销分配的各种资源
- }
- static int hello_probe(struct platform_device *pdev)
- {
- 1.申请设备号
- 2.cdev初始化注册,&hello_ops
- 3.从pdev读出硬件资源
- 4.对硬件资源初始化,ioremap,request_irq( )
- }
- static int hello_init(void)
- {
- 只注册 platform_driver
- }
- static void hello_exit(void)
- {
- 只注销 platform_driver
- }
- struct bus_type platform_bus_type = {
- .name = "platform",
- .dev_groups = platform_dev_groups,
- .match = platform_match,
- .uevent = platform_uevent,
- .pm = &platform_dev_pm_ops,
- };
- __platform_driver_register()
- {
- drv->driver.bus = &platform_bus_type; 536行
- }
- static int platform_match(struct device *dev, struct device_driver *drv)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct platform_driver *pdrv = to_platform_driver(drv);
- 匹配设备树信息,如果有设备树,就调用 of_driver_match_device() 函数进行匹配
- if (of_driver_match_device(dev, drv))
- return 1;
- 匹配id_table
- if (pdrv->id_table)
- return platform_match_id(pdrv->id_table, pdev) != NULL;
- 最基本匹配规则
- return (strcmp(pdev->name, drv->name) == 0);
- }
a -- 为什么要用 platform 驱动?不用platform驱动可以吗?
b -- 设备驱动中引入platform 概念有什么好处?
引入platform模型符合Linux 设备模型 —— 总线、设备、驱动,设备模型中配套的sysfs节点都可以用,方便我们的开发;当然你也可以选择不用,不过就失去了一些platform带来的便利;
Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动, 下面来看一下,套上platform 外壳后的程序:
1、device.c
- #include <linux/module.h>
- #include <linux/device.h>
- #include <linux/platform_device.h>
- #include <linux/ioport.h>
- static struct resource beep_resource[] =
- {
- [0] ={
- .start = 0x114000a0,
- .end = 0x114000a0 + 0x4,
- .flags = IORESOURCE_MEM,
- },
- [1] ={
- .start = 0x139D0000,
- .end = 0x139D0000 + 0x14,
- .flags = IORESOURCE_MEM,
- }
- };
- static void hello_release(struct device *dev)
- {
- printk("hello_release\n");
- return ;
- }
- static struct platform_device hello_device=
- {
- .name = "bigbang",
- .id = -1,
- .dev.release = hello_release,
- .num_resources = ARRAY_SIZE(beep_resource),
- .resource = beep_resource,
- };
- static int hello_init(void)
- {
- printk("hello_init");
- return platform_device_register(&hello_device);
- }
- static void hello_exit(void)
- {
- printk("hello_exit");
- platform_device_unregister(&hello_device);
- return;
- }
- MODULE_LICENSE("GPL");
- module_init(hello_init);
- module_exit(hello_exit);
2、driver.c
- #include <linux/module.h>
- #include <linux/fs.h>
- #include <linux/cdev.h>
- #include <linux/device.h>
- #include <linux/platform_device.h>
- #include <asm/io.h>
- static int major = 250;
- static int minor=0;
- static dev_t devno;
- static struct class *cls;
- static struct device *test_device;
- #define TCFG0 0x0000
- #define TCFG1 0x0004
- #define TCON 0x0008
- #define TCNTB0 0x000C
- #define TCMPB0 0x0010
- static unsigned int *gpd0con;
- static void *timer_base;
- #define MAGIC_NUMBER 'k'
- #define BEEP_ON _IO(MAGIC_NUMBER ,0)
- #define BEEP_OFF _IO(MAGIC_NUMBER ,1)
- #define BEEP_FREQ _IO(MAGIC_NUMBER ,2)
- static void fs4412_beep_init(void)
- {
- writel ((readl(gpd0con)&~(0xf<<0)) | (0x2<<0),gpd0con);
- writel ((readl(timer_base +TCFG0 )&~(0xff<<0)) | (0xff <<0),timer_base +TCFG0);
- writel ((readl(timer_base +TCFG1 )&~(0xf<<0)) | (0x2 <<0),timer_base +TCFG1 );
- writel (500, timer_base +TCNTB0 );
- writel (250, timer_base +TCMPB0 );
- writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON );
- }
- void fs4412_beep_on(void)
- {
- writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x9 <<0),timer_base +TCON );
- }
- void fs4412_beep_off(void)
- {
- writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x0 <<0),timer_base +TCON );
- }
- static void beep_unmap(void)
- {
- iounmap(gpd0con);
- iounmap(timer_base);
- }
- static int beep_open (struct inode *inode, struct file *filep)
- {
- fs4412_beep_on();
- return 0;
- }
- static int beep_release(struct inode *inode, struct file *filep)
- {
- fs4412_beep_off();
- return 0;
- }
- #define BEPP_IN_FREQ 100000
- static void beep_freq(unsigned long arg)
- {
- writel(BEPP_IN_FREQ/arg, timer_base +TCNTB0 );
- writel(BEPP_IN_FREQ/(2*arg), timer_base +TCMPB0 );
- }
- static long beep_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
- {
- switch(cmd)
- {
- case BEEP_ON:
- fs4412_beep_on();
- break;
- case BEEP_OFF:
- fs4412_beep_off();
- break;
- case BEEP_FREQ:
- beep_freq( arg );
- break;
- default :
- return -EINVAL;
- }
- return 0;
- }
- static struct file_operations beep_ops=
- {
- .open = beep_open,
- .release = beep_release,
- .unlocked_ioctl = beep_ioctl,
- };
- static int beep_probe(struct platform_device *pdev)
- {
- int ret;
- printk("match ok!");
- gpd0con = ioremap(pdev->resource[0].start,pdev->resource[0].end - pdev->resource[0].start);
- timer_base = ioremap(pdev->resource[1].start, pdev->resource[1].end - pdev->resource[1].start);
- devno = MKDEV(major,minor);
- ret = register_chrdev(major,"beep",&beep_ops);
- cls = class_create(THIS_MODULE, "myclass");
- if(IS_ERR(cls))
- {
- unregister_chrdev(major,"beep");
- return -EBUSY;
- }
- test_device = device_create(cls,NULL,devno,NULL,"beep");//mknod /dev/hello
- if(IS_ERR(test_device))
- {
- class_destroy(cls);
- unregister_chrdev(major,"beep");
- return -EBUSY;
- }
- fs4412_beep_init();
- return 0;
- }
- static int beep_remove(struct platform_device *pdev)
- {
- beep_unmap();
- device_destroy(cls,devno);
- class_destroy(cls);
- unregister_chrdev(major,"beep");
- return 0;
- }
- static struct platform_driver beep_driver=
- {
- .driver.name = "bigbang",
- .probe = beep_probe,
- .remove = beep_remove,
- };
- static int beep_init(void)
- {
- printk("beep_init");
- return platform_driver_register(&beep_driver);
- }
- static void beep_exit(void)
- {
- printk("beep_exit");
- platform_driver_unregister(&beep_driver);
- return;
- }
- MODULE_LICENSE("GPL");
- module_init(beep_init);
- module_exit(beep_exit);
3、makefile
- ifneq ($(KERNELRELEASE),)
- obj-m:=device.o driver.o
- $(info "2nd")
- else
- #KDIR := /lib/modules/$(shell uname -r)/build
- KDIR := /home/fs/linux/linux-3.14-fs4412
- PWD:=$(shell pwd)
- all:
- $(info "1st")
- make -C $(KDIR) M=$(PWD) modules
- clean:
- rm -f *.ko *.o *.symvers *.mod.c *.mod.o *.order
- endif
4、test.c
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <stdio.h>
- main()
- {
- int fd,i,lednum;
- fd = open("/dev/beep",O_RDWR);
- if(fd<0)
- {
- perror("open fail \n");
- return ;
- }
- sleep(10);
- close(fd);
- }
Linux 设备驱动开发 —— platform设备驱动应用实例解析的更多相关文章
- Linux驱动开发:USB驱动之usb_skel分析
在学习了这么些天的驱动之后,个人觉得驱动就是个架构的问题,只要把架构弄清楚了 然后往里面添砖加瓦就可以了,所以似乎看起来不是太困难,但也许是是我经验不足吧,这只能算是个人浅见了 这两天在学习USB驱动 ...
- usb驱动开发22之驱动生命线
我们总是很喜欢高潮,不是吗?那就好好对待她哦.我们来看一下linux中的高潮部分设备是怎么从Address进入Configured的. usb_set_configuration函数的代码就不贴了,可 ...
- usb驱动开发21之驱动生命线
现在开始就沿着usb_generic_driver的生命线继续往下走.设备的生命线你可以为是从你的usb设备连接到hub的某个端口时开始,而驱动的生命线就必须得回溯到usb子系统的初始化函数usb_i ...
- 嵌入式驱动开发只设备数---dts
http://blog.sina.com.cn/s/blog_ad64b8200101e7q0.html
- Android系统移植与驱动开发——第六章——使用实例来理解Linux驱动开发及心得
Linux驱动的工作方式就是交互.例如向Linux打印机驱动发送一个打印命令,可以直接使用C语言函数open打开设备文件,在使用C语言函数ioctl向该驱动的设备文件发送打印命令.编写Linux驱动最 ...
- usb驱动开发23之驱动生命线
关于字符串描述符的地位仅次于设备/配置/接口/端点四大描述符,那四大设备必须得支持,而字符串描述符对设备来说则是可选的,这并不是就说字符串描述符不重要,对咱们来说,提供字符串描述符的设备要比没有提供的 ...
- [ Python ] Flask 基于 Web开发 大型程序的结构实例解析
作为一个编程入门新手,Flask是我接触到的第一个Web框架.想要深入学习,就从<FlaskWeb开发:基于Python的Web应用开发实战>这本书入手,本书由于是翻译过来的中文版,理解起 ...
- 驱动开发--【字符设备、块设备简介】【sky原创】
驱动开发 字符设备,块设备,网络设备 字符设备 以字节流的方式访问, 不能随机访问 有例外,显卡.EEPROM可以随机访问 EEPROM可以擦写1亿次,是一种字符设备,可以随机访问 读写是 ...
- linux驱动开发—基于Device tree机制的驱动编写
前言Device Tree是一种用来描述硬件的数据结构,类似板级描述语言,起源于OpenFirmware(OF).在目前广泛使用的Linux kernel 2.6.x版本中,对于不同平台.不同硬件,往 ...
随机推荐
- Ubuntu 档案权限
Linux文件属性:查看指令是:ls -al ls是list的意思,重点在显示档案的文件名与相关属性.而选项-al则表示列出所有的档案详细的权限与属性.
- 30、自定义gridview
要想实现自定义gridview效果,有下边几个步骤: 1.定义grivew中的item的xml文件 假如item需要显示一个图片,图片下边显示文字: <?xml version="1. ...
- 探究灰度测试(A/B Testing)
一段小插曲 前段时间产品改版,产品经理为了改进用户体验,就决定改版用户的注册流程页面,但又怕身份证注册验证接口不稳定(第三方的身份证校验). 于是产品经理就让我通过随机概率去控制注册流程,让一部分用户 ...
- python学习-- django 2.1.7 ajax 请求
#--------------views.py---------------------- def add(request): a = request.GET['a'] print(a) b = re ...
- HTTP的一些概念
1. 什么是回调? 回调是异步编程时的基础,将后续逻辑封装成起始函数的参数,逐层嵌套 2. 什么是同步/异步? 同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式. 异步是指: ...
- [错误解决]UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)
python2内容无法写入csv,报错:UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-3: ordin ...
- pat 1037
如果你是哈利·波特迷,你会知道魔法世界有它自己的货币系统 —— 就如海格告诉哈利的:“十七个银西可(Sickle)兑一个加隆(Galleon),二十九个纳特(Knut)兑一个西可,很容易.”现在,给定 ...
- 九度oj 题目1362:左旋转字符串(Move!Move!!Move!!!)
题目描述: 汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果.对于一个给定的字符序列S,请你把其循环左移K位后的序列输出.例如,字符序列S=”ab ...
- 在Notepad++里配置python环境
首先在语言里选择Python 然后点击运行,在弹出的对话框里输入: cmd /k cd /d "$(CURRENT_DIRECTORY)" & python " ...
- HDU-2768 Cat vs. Dog
题意一开始是理解错的...结果就各种WA啦~ 对于两个观众,假如有某只宠物,一个人讨厌另一个人却喜欢,这两个人就是有矛盾的,连边. 最后求最小顶点覆盖.因为把这个覆盖点集去掉的话剩下的图中没有两个点是 ...