一 、重要知识点:
从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
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”);
#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渠道驱动
//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 < 6; 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 >= 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 < 6; i++) {
free_irq(button_irqs.buttons_irq[i], (void *)&button_irqs.buttons_irq[i]);
}
return 0;
}
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 = 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; i<6; i++){
plat_resource = platform_get_resource(pdev,IORESOURCE_IRQ,i);
if(plat_resource == NULL)
return -ENOENT;
buttons_irq[i] = plat_resource->start;
}
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”);
//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 < 6; 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 >= 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 < 6; i++) {free_irq(button_irqs.buttons_irq[i], (void *)&button_irqs.buttons_irq[i]);}return 0;}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;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; i<6; i++){plat_resource = platform_get_resource(pdev,IORESOURCE_IRQ,i);if(plat_resource == NULL)return -ENOENT;buttons_irq[i] = plat_resource->start;}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”);
/*
* 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;
}
/** 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;}
运转测验程序后按下第二个键,中止上打印了屡次按键的键值,发生原因是由于按键颤动。导致按一下按键,发生屡次中止。