驱动之路-platform简例按键驱动☆☆☆
一 、重要知识点:
▉1.platform设备模型
从Linux 2.6起引入了一套新的驱动管理和注册机制,platform_device和platform_driver,Linux中大部分的设备驱动都可以使用这套机制。platform是一条虚拟的总线。设备用platform_device表示,驱动用platform_driver进行注册,Linux platform driver机制和传统的device driver机制(通过driver_register进行注册)相比,一个明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动中使用这些资源时通过platform device提供的标准结构进行申请并使用。这样提高了驱动和资源的独立性,并且具有较好的可移植性和安全性(这些标准接口是安全的)。
pltform机制本身使用并不复杂,由两部分组成:platform_device和platform_driver。通过platform机制开发底层驱动的大致流程为:定义platform_deive->注册platform_device->定义platform_driver->注册platform_driver。
首先要确认的就是设备的资源信息,例如设备的地址,中断号等。
1 ● platform_device
在 2.6 内核中 platform 设备用结构体 platform_device 来描述,该结构体定义在 kernel/include/linux/platform_device.h 中,
structplatform_device {
const char * name;
u32 id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
该结构一个重要的元素是resource ,该元素存入了最为重要的设备资源信息,定义在kernel/include/linux/ioport.h 中,
structresource {
const char *name;//资源的名称
unsigned long start, end;//资源起始的和结束的物理地址
unsigned long flags;//资源的类型,比如MEM,IO,IRQ类型
struct resource *parent, *sibling, *child;//资源链表的指针
};
structplatform_device的分配使用
structplatform_device *platform_device_alloc(const char *name, int id)
name是设备名,id,设备id,一般为-1,如果是-1,表示同样名字的设备只有一个
举个简单的例子,name/id是“serial/1”则它的bus_id就是serial.1 如果name/id是“serial/0”则它的bus_id就是serial.0 ,如果它的name/id是“serial/-1”则它的bus_id就是serial。
注册平台设备,使用函数
intplatform_device_add(struct platform_device *pdev)
注销使用
voidplatform_device_unregister(struct platform_device *pdev)
2 ● platform_driver
在平台设备驱动中获取平台设备资源使用
structresource *platform_get_resource(struct platform_device *dev, unsigned int type,unsigned int num)
该函数用于获取dev设备的第num个类型为type的资源,如果获取失败,则返回NULL。例如 platform_get_resource(pdev,IORESOURCE_IRQ, 0)。
平台驱动描述使用
structplatform_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 (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
Probe()函数必须验证指定设备的硬件是否真的存在,probe()可以使用设备的资源,包括时钟,platform_data(platform_data:用户自定义的私有数据)等,Platform driver可以通过下面的函数完成对驱动的注册:
int platform_driver_register(structplatform_driver *drv);一般来说设备是不能被热插拔的,所以可以将probe()函数放在init段里面来节省driver运行时候的内存开销:
int platform_driver_probe(struct platform_driver *drv, int (*probe)(structplatform_device *));
注销使用void platform_driver_unregister(struct platform_driver *drv)
▉2.中断处理
在Linux驱动程序中,为设备实现一个中断包含 两个步骤1.向内核注册(申请中断)中断 2.实现中断处理函数
request_irq用于实现中断的注册
intrequest_irq(unsigned in irq, void(*handler)(int, void *, struct pt_regs *), unsigned long flags, const char *devname, void*dev_id)
向内核申请中断号为irq,中断处理函数为handler指针指向的函数,中断标志为flag,设备名为devname的中断。成功返回0,或者返回一个错误码。
当request_irq不用于共享中断时,dev_id可以为NULL,或者指向驱动程序自己的私有数据。但用于共享中断时dev_id必须唯一。因为free_irq时也需要dev_id做参数,这样free_irq才知道要卸载共享中断上哪个中断服务处理函数。共享中断会在后面讲到。
在flag参数中,可以选以下参数
IRQF_DISABLED(SA_INTERRUPT)
如果设置该位,表示是一个“快速”中断处理程序,如果没有,那么就是一个“慢速”中断处理程序。
IRQF_SHARED(SA_SHITQ)
该位表示中断可以在设备间共享。
快速/慢速中断
这两种类型的中断处理程序的主要区别在于:快速中断保证中断处理的原子性(不被打断),而慢速中断则不保证。换句话说,也就是开启中断标志位在运行快速中断处理程序时
关闭的,因此在服务该中断时,不会被其他类型的中断打断;而调用慢速中断处理时,其他类型中断扔可以得到服务。
共享中断
共享中断就是将不同的设备挂到同一个中断信号线上。linux对共享的支持主要是位PCI设备服务。
释放中断
voidfree_irq(unsigned int irq)
当设备不再需要使用中断时(通常是设备关闭和驱动卸载时),应该使用该函数把他们返回给内核使用。
禁用中断
voiddisable_irq(int irq)
当一些代码中不能使用中断时(如支持自旋锁的上下文中)使用该函数禁用中断。
启用中断
voidenable_irq(int irq)
当禁止后可以使用该函数重新启用。
◐二、驱动代码
该驱动实现能够读取按键按下的键值,比如说如果是第一个键按下读取的键值就为1。
platform平台设备
#include <linux/device.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/gpio.h> static struct resource key_resource[]= //⑦
{
[] = {
.start = IRQ_EINT8,
.end = IRQ_EINT8,
.flags = IORESOURCE_IRQ,
},
[] = {
.start = IRQ_EINT11,
.end = IRQ_EINT11,
.flags = IORESOURCE_IRQ,
},
[]= {
.start = IRQ_EINT13,
.end = IRQ_EINT13,
.flags = IORESOURCE_IRQ,
},
[] = {
.start = IRQ_EINT14,
.end = IRQ_EINT14,
.flags = IORESOURCE_IRQ,
},
[] = {
.start = IRQ_EINT15,
.end = IRQ_EINT15,
.flags = IORESOURCE_IRQ,
},
[] = {
.start = IRQ_EINT19,
.end = IRQ_EINT19,
.flags = IORESOURCE_IRQ,
},
}; struct platform_device *my_buttons_dev; static int __init platform_dev_init(void)
{
int ret; my_buttons_dev = platform_device_alloc("my_buttons", -); platform_device_add_resources(my_buttons_dev,key_resource,);//添加资源一定要用该函数,不能使用对platform_device->resource幅值
//否则会导致platform_device_unregister调用失败,内核异常。 ret = platform_device_add(my_buttons_dev); if(ret)
platform_device_put(my_buttons_dev); return ret;
} static void __exit platform_dev_exit(void)
{
platform_device_unregister(my_buttons_dev);
} module_init(platform_dev_init);
module_exit(platform_dev_exit); MODULE_AUTHOR("Y-Kee");
MODULE_LICENSE("GPL");
platform平台驱动
//platform driver
#include <linux/module.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/unistd.h>
#include <linux/device.h> static int buttons_irq[]; struct irq_des
{
int *buttons_irq;
char *name[];
}; struct irq_des button_irqs = {
.buttons_irq = buttons_irq,
.name = {"KEY0", "KEY1", "KEY2", "KEY3", "KEY4", "KEY5"},
}; static volatile int key_values; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); static volatile int ev_press = ; static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
int i;
for(i=; i<; i++){
if(irq == buttons_irq[i]){ //④
key_values = i;
ev_press = ;
wake_up_interruptible(&button_waitq);
}
} return IRQ_RETVAL(IRQ_HANDLED);
} static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
{
int i;
int err = ; for (i = ; i < ; i++) {
err = request_irq(button_irqs.buttons_irq[i], buttons_interrupt, IRQ_TYPE_EDGE_BOTH,
button_irqs.name[i], (void *)&button_irqs.buttons_irq[i]);
if (err)
break;
} if (err) {
i--;
for (; i >= ; i--) {
if (button_irqs.buttons_irq[i] < ) {
continue;
}
disable_irq(button_irqs.buttons_irq[i]);
free_irq(button_irqs.buttons_irq[i], (void *)&button_irqs.buttons_irq[i]);
}
return -EBUSY;
} return ;
} static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
{
int i; for (i = ; i < ; i++) {
free_irq(button_irqs.buttons_irq[i], (void *)&button_irqs.buttons_irq[i]);
} return ;
} static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
unsigned long err; if (!ev_press) {
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
else
wait_event_interruptible(button_waitq, ev_press);
} ev_press = ; err = copy_to_user(buff, (const void *)&key_values, min(sizeof(key_values), count)); //③
return err ? -EFAULT : min(sizeof(key_values), count);
} static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
{
unsigned int mask = ;
poll_wait(file, &button_waitq, wait);
if (ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
} static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.open = s3c24xx_buttons_open,
.release = s3c24xx_buttons_close,
.read = s3c24xx_buttons_read,
.poll = s3c24xx_buttons_poll,
}; static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "my_buttons",
.fops = &dev_fops,
}; static int my_plat_probe(struct platform_device *dev)
{
int ret,i;
struct resource *plat_resource;
struct platform_device *pdev = dev; printk("my platform dirver find my platfrom device.\n"); for(i=; i<; i++){
plat_resource = platform_get_resource(pdev,IORESOURCE_IRQ,i); //⑥ 获取platform总线中对应资源 file_operations从platform获取资源的核心语句 ☆☆☆
if(plat_resource == NULL)
return -ENOENT;
buttons_irq[i] = plat_resource->start; //⑤
} ret = misc_register(&misc);
if(ret)
return ret;
return ;
} static int my_plat_remove(struct platform_device *dev)
{
printk("my platfrom device has removed.\n");
misc_deregister(&misc);
return ;
} struct platform_driver my_buttons_drv = {
.probe = my_plat_probe,
.remove = my_plat_remove,
.driver = {
.owner = THIS_MODULE,
.name = "my_buttons",
},
}; static int __init platform_drv_init(void)
{
int ret; ret = platform_driver_register(&my_buttons_drv); return ret;
} static void __exit platform_drv_exit(void)
{
platform_driver_unregister(&my_buttons_drv);
} module_init(platform_drv_init);
module_exit(platform_drv_exit); MODULE_AUTHOR("Y-Kee");
MODULE_LICENSE("GPL");
测试代码
/*
* Buttons Example for Matrix V
*
* Copyright (C) 2004 capbily - friendly-arm
* capbily@hotmail.com
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h> int main(void)
{
int buttons_fd;
int key_value; buttons_fd = open("/dev/buttons", );
if (buttons_fd < ) {
perror("open device buttons");
exit();
} for (;;) {
fd_set rds;
int ret; FD_ZERO(&rds);
FD_SET(buttons_fd, &rds); ret = select(buttons_fd + , &rds, NULL, NULL, NULL);
if (ret < ) {
perror("select");
exit();
}
if (ret == ) {
printf("Timeout.\n");
} else if (FD_ISSET(buttons_fd, &rds)) {
int ret = read(buttons_fd, &key_value, sizeof key_value); //②
if (ret != sizeof key_value) {
if (errno != EAGAIN)
perror("read buttons\n");
continue;
} else {
printf("buttons_value: %d\n", key_value+1); //①
} }
} close(buttons_fd);
return ;
}
测试结果:
-----------------
驱动之路-platform简例按键驱动☆☆☆的更多相关文章
- Linux Platform devices 平台设备驱动
设备总线驱动模型:http://blog.csdn.net/lizuobin2/article/details/51570196 本文主要参考:http://www.wowotech.net/devi ...
- linux-3.0下input_dev模型按键驱动
该代码在FL2440开发板上测试通过,为方便教学,将驱动中的platform_device和platform_driver故意分为两个驱动模块. [guowenxue@centos6 input_kb ...
- Linux驱动中的platform总线分析
copy from :https://blog.csdn.net/fml1997/article/details/77622860 概述 从Linux2.6内核起,引入一套新的驱动管理和注册机制:pl ...
- 入门级的按键驱动——按键驱动笔记之poll机制-异步通知-同步互斥阻塞-定时器防抖
文章对应视频的第12课,第5.6.7.8节. 在这之前还有查询方式的驱动编写,中断方式的驱动编写,这篇文章中暂时没有这些类容.但这篇文章是以这些为基础写的,前面的内容有空补上. 按键驱动——按下按键, ...
- Linux按键驱动程序设计--从简单到不简单【转】
本文转载自:http://blog.csdn.net/coding__madman/article/details/51399353 混杂设备驱动模型: 1. 混杂设备描述 在Linux系统中,存在一 ...
- Linux按键驱动程序设计详解---从简单到不简单【转】
转自:http://blog.csdn.net/coding__madman/article/details/51399353 版权声明:本文为博主原创文章,未经博主允许不得转载. 混杂设备驱动模型: ...
- 基于等待队列及poll机制的按键驱动代码分析和测试代码
按键驱动分析: #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> ...
- 【转】s3c2440 按键驱动 — 字符设备
原文网址:http://www.xuebuyuan.com/632893.html 主机:VM - redhat 9.0 开发板:FL2440,linux-2.6.12 arm-linux-gcc:3 ...
- AM335x(TQ335x)学习笔记——GPIO按键驱动移植
还是按照S5PV210的学习顺序来,我们首先解决按键问题.TQ335x有六个用户按键,分别是上.下.左.右.Enter和ESC.开始我想到的是跟学习S5PV210时一样,编写输入子系统驱动解决按键问题 ...
随机推荐
- 《APUE》第6章练习1
问题:编写一个程序,它调用uname并输出utsname结构中的所有字段,并将输出与uname(1)命令的输出结构做比较. 我本来以为utsname结构也跟passwd结构一样,也有那三个函数来获取整 ...
- asp.net导出Excel 按照预定格式,以及解决导出乱码
protected void ToExcel() { //新建一个Gridview,原因:避免当前窗口GridView外层没有直接跟form标签,从而避免“gridview1未包含在run='serv ...
- checkBox控件的CheckedChanged与CheckedStateChanged区别
Checked属性为bool类型,CheckState属性为枚举类型(CheckState.Checked.CheckState.Unchecked和CheckState.Indeterminate) ...
- PHPNow升级PHP版本的方法
在WIN上有时候需要测试一些PHP程序,又不会自行独立配置环境,那么PHPNow是非常好的选择. PHPNow自带的PHP版本为5.2.14,而最后一次更新在于2010-9-22,PHP5.2对于现在 ...
- SharedPreference 的存取
1.通过名称来获取指定的SharedPreferences,下面这句代码表示获取名字问hello的SharedPreferences,数据保存在 data/data/package名/shared_p ...
- php总结:1.php介绍
1.什么是php PHP,即“Hypertext Preprocessor”,是一种被广泛应用的开源通用脚本语言,尤其适用于 Web 开发并可嵌入 HTML 中去.它的语法利用了 C.Java 和 P ...
- iomanip,setw(),setw: undeclared identifier
今天使用setw(),提示setw: undeclared identifier,上网查了下,原来是没有包含头文件iomanip,现摘录如下: iomanip #include <iomanip ...
- 使用rar打包多个文件为exe可执行文件
需求分析:有些机友在刷recovery的时候不知道如何刷入,于是产生写bat脚本和打包为exe可执行文件,只要机友正确安装好驱动后连接手机双击就可以刷入rec了 解决过程: 需要打包的文件 操作过程截 ...
- MySQL Connector Net连接vs2012问题
最近做一.NET项目,数据库用到MySQL,可是在VS2012连接数据库是遇到问题,提示:Authentication with old password no longer supported, u ...
- 2015-4-2的阿里巴巴笔试题:乱序的序列保序输出(bit数组实现hash)
分布式系统中的RPC请求经常出现乱序的情况.写一个算法来将一个乱序的序列保序输出.例如,假设起始序号是1,对于(1, 2, 5, 8, 10, 4, 3, 6, 9, 7)这个序列,输出是:123, ...