一 、重要知识点:
从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。
首先要确认的就是设备的资源信息,例如设备的地址,中断号等。
在 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)
在平台设备驱动中获取平台设备资源使用
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 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
staticstructresourcekey_resource[]=
{
[0]={
.start=irq_eint8,
.end=irq_eint8,
.flags=ioresource_irq,
},
[1]={
.start=irq_eint11,
.end=irq_eint11,
.flags=ioresource_irq,
},
[2]={
.start=irq_eint13,
.end=irq_eint13,
.flags=ioresource_irq,
},
[3]={
.start=irq_eint14,
.end=irq_eint14,
.flags=ioresource_irq,
},
[4]={
.start=irq_eint15,
.end=irq_eint15,
.flags=ioresource_irq,
},
[5]={
.start=irq_eint19,
.end=irq_eint19,
.flags=ioresource_irq,
},
};
structplatform_device*my_buttons_dev;
staticint__initplatform_dev_init(void)
{
intret;
my_buttons_dev=platform_device_alloc(my_buttons,-1);
platform_device_add_resources(my_buttons_dev,key_resource,6);//添加资源一定要用该函数,不能使用对platform_device->resource幅值
//否则会导致platform_device_unregister调用失败,内核异常。
ret=platform_device_add(my_buttons_dev);
if(ret)
platform_device_put(my_buttons_dev);
returnret;
}
staticvoid__exitplatform_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);
#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct resource key_resource[]={[0] = {.start = irq_eint8,.end = irq_eint8,.flags = ioresource_irq,},[1] = {.start = irq_eint11,.end = irq_eint11,.flags = ioresource_irq,},[2]= {.start = irq_eint13,.end = irq_eint13,.flags = ioresource_irq,},[3] = {.start = irq_eint14,.end = irq_eint14,.flags = ioresource_irq,},[4] = {.start = irq_eint15,.end = irq_eint15,.flags = ioresource_irq,},[5] = {.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, -1);platform_device_add_resources(my_buttons_dev,key_resource,6);//添加资源一定要用该函数,不能使用对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平台驱动
//platformdriver
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
staticintbuttons_irq[6];
structirq_des
{
int*buttons_irq;
char*name[6];
};
structirq_desbutton_irqs={
.buttons_irq=buttons_irq,
.name={key0,key1,key2,key3,key4,key5},
};
staticvolatileintkey_values;
staticdeclare_wait_queue_head(button_waitq);
staticvolatileintev_press=0;
staticirqreturn_tbuttons_interrupt(intirq,void*dev_id)
{
inti;
for(i=0;i<6;i++){
if(irq==buttons_irq[i]){
key_values=i;
ev_press=1;
wake_up_interruptible(&button_waitq);
}
}
returnirq_retval(irq_handled);
}
staticints3c24xx_buttons_open(structinode*inode,structfile*file)
{
inti;
interr=0;
for(i=0;i=0;i--){
if(button_irqs.buttons_irq[i]<0){
continue;
}
disable_irq(button_irqs.buttons_irq[i]);
free_irq(button_irqs.buttons_irq[i],(void*)&button_irqs.buttons_irq[i]);
}
return-ebusy;
}
return0;
}
staticints3c24xx_buttons_close(structinode*inode,structfile*file)
{
inti;
for(i=0;if_flags&o_nonblock)
return-eagain;
else
wait_event_interruptible(button_waitq,ev_press);
}
ev_press=0;
err=copy_to_user(buff,(constvoid*)&key_values,min(sizeof(key_values),count));
returnerr?-efault:min(sizeof(key_values),count);
}
staticunsignedints3c24xx_buttons_poll(structfile*file,structpoll_table_struct*wait)
{
unsignedintmask=0;
poll_wait(file,&button_waitq,wait);
if(ev_press)
mask|=pollin|pollrdnorm;
returnmask;
}
staticstructfile_operationsdev_fops={
.owner=this_module,
.open=s3c24xx_buttons_open,
.release=s3c24xx_buttons_close,
.read=s3c24xx_buttons_read,
.poll=s3c24xx_buttons_poll,
};
staticstructmiscdevicemisc={
.minor=misc_dynamic_minor,
.name=my_buttons,
.fops=&dev_fops,
};
staticintmy_plat_probe(structplatform_device*dev)
{
intret,i;
structresource*plat_resource;
structplatform_device*pdev=dev;
printk(myplatformdirverfindmyplatfromdevice.\n);
for(i=0;istart;
}
ret=misc_register(&misc);
if(ret)
returnret;
return0;
}
staticintmy_plat_remove(structplatform_device*dev)
{
printk(myplatfromdevicehasremoved.\n);
misc_deregister(&misc);
return0;
}
structplatform_drivermy_buttons_drv={
.probe=my_plat_probe,
.remove=my_plat_remove,
.driver={
.owner=this_module,
.name=my_buttons,
},
};
staticint__initplatform_drv_init(void)
{
intret;
ret=platform_driver_register(&my_buttons_drv);
returnret;
}
staticvoid__exitplatform_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);
//platform driver#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int buttons_irq[6];struct irq_des{int *buttons_irq;char *name[6];};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 = 0;static irqreturn_t buttons_interrupt(int irq, void *dev_id){int i;for(i=0; i<6; i++){if(irq == buttons_irq[i]){key_values = i;ev_press = 1;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 = 0;for (i = 0; i = 0; i--) {if (button_irqs.buttons_irq[i] < 0) {continue;}disable_irq(button_irqs.buttons_irq[i]);free_irq(button_irqs.buttons_irq[i], (void *)&button_irqs.buttons_irq[i]);}return -ebusy;}return 0;}static int s3c24xx_buttons_close(struct inode *inode, struct file *file){int i;for (i = 0; i f_flags & o_nonblock)return -eagain;elsewait_event_interruptible(button_waitq, ev_press);}ev_press = 0;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 = 0;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=0; istart;}ret = misc_register(&misc);if(ret)return ret;return 0;}static int my_plat_remove(struct platform_device *dev){printk(my platfrom device has removed.\n);misc_deregister(&misc);return 0;}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);
/*
*buttonsexampleformatrixv
*
*copyright(c)2004capbily-friendly-arm
*capbily@hotmail.com
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
intmain(void)
{
intbuttons_fd;
intkey_value;
buttons_fd=open(/dev/buttons,0);
if(buttons_fd<0){
perror(opendevicebuttons);
exit(1);
}
for(;;){
fd_setrds;
intret;
fd_zero(&rds);
fd_set(buttons_fd,&rds);
ret=select(buttons_fd+1,&rds,null,null,null);
if(ret<0){
perror(select);
exit(1);
}
if(ret==0){
printf(timeout.\n);
}elseif(fd_isset(buttons_fd,&rds)){
intret=read(buttons_fd,&key_value,sizeofkey_value);
if(ret!=sizeofkey_value){
if(errno!=eagain)
perror(readbuttons\n);
continue;
}else{
printf(buttons_value:%d\n,key_value+1);
}
}
}
close(buttons_fd);
return0;
}
/** buttons example for matrix v** copyright (c) 2004 capbily - friendly-arm*capbily@hotmail.com*/#include #include #include #include #include #include #include #include #include #include int main(void){int buttons_fd;int key_value;buttons_fd = open(/dev/buttons, 0);if (buttons_fd < 0) {perror(open device buttons);exit(1);}for (;;) {fd_set rds;int ret;fd_zero(&rds);fd_set(buttons_fd, &rds);ret = select(buttons_fd + 1, &rds, null, null, null);if (ret < 0) {perror(select);exit(1);}if (ret == 0) {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 0;}
运行测试程序后按下第二个键,中断上打印了多次按键的键值,产生原因是因为按键抖动。导致按一下按键,产生多次中断。
关于生物特征识别技术的发展趋势及对数字处理器分析详解
PCB原理图设计的一些小技巧和常见错误
前端将PC声卡变成高速采样示波器
CoinMeet正在建立一个,以区块链生态圈为主的,数字资产钱包社交工具
国办发文优化电子电器行业管理制度,涉及电子电器产品准入自检自证
驱动之路之platform按键驱动
中兴将全球首发屏下摄像头高通骁龙888旗舰
关于芯片后缀的必备小常识
小米5C评测:颜值颇高的时尚机!史上最美小米5C精致优雅并存
2023“物联之星”智慧城市系列案例
华为推出全新AMD锐龙4000H标压处理器
EPC推出300W、双向、1/16砖型转换器评估模块——EPC9151
通过单级PFC降压LED驱动器简化T8荧光灯的更换设计
Pericom针对最新CPU芯片组 扩展其高速串行连接解决方案
听音乐就要vivoXplay6,手机HiFi音频的领头羊实力推荐!
选择合适的连接器不考虑触点定制的好处
海瑞思科技推出前放后取工装检测设备
雷达发射机高压稳压电源
Superdata预测未来三年AR/MR收入将超VR
研究显示新Petya病毒破坏性比之前更强 目标非赎金而是直接毁坏用户电脑