我的环境:主机:Fedora 14,内核版本2.6.38.1
开发板:ARM9 tq2440
移植内核版本:linux-2.6.30.4
1、platform简介
为了理解platform总线设备驱动模型的实际运用,我首先分析了基于S3C2410的看门狗驱动实现过程,我本着将自己学过的知识在温习一遍的态度,完成了基于platform平台总线的外部中断(按键)的基本实现过程,分别采用了混杂字符设备和一般字符设备进行了操作,其中混杂字符设备驱动、应用程序参照了 Y-Kee http://blog.csdn.net/ayangke,QQ:843308498的基本框架。
platform总线是总线设备驱动模型中的一种,内核帮助实现了总线部分,我们在设计驱动的过程中只需要完成驱动和设备的添加即可。其中设备的添加包括添加设备名和相关的硬件资源(中断号和寄存器分布)。而驱动的添加除了添加必要的驱动模型以外,还需要得到硬件的具体资源,并对资源进行相应的操作和处理。
2、中断处理简介
中断是处理外设与处理器速度不匹配过程中常用的方法,基本的思想是,当产生中断以后,CPU必须停止当前处理的任务,转去执行中断处理程序,待中断处理程序处理完成以后再回到当前处理任务的过程。嵌入式处理器中的中断主要包括两部分CPU的内部中断(主要是异常)以及外设的中断(外部中断)。同时有的中断可以被屏蔽,而有的中断又不能被屏蔽,又可以将中断分为屏蔽和不可屏蔽中断。根据入口的跳转方法又可以将中断分为向量中断和非向量中断。向量中断通常是不同的中断号有不同的处理程序入口地址(硬件提供),而非向量中断通常是指共享型的中断,入口地址通常是由用户软件提供。
在linux内核中对中断的操作主要包括两部分:1、中断的申请和释放;2、中断处理程序设计
其中的中断的申请和释放,具体的采用函数:
- /*申请中断*/
- request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
- /*释放中断*/
- void free_irq(unsigned int irq, void *dev_id)
在request_irq中的参数:
1、unsigned int irq表示具体的中断号,一般而言,针对不同的CPU,中断号存在一定的差别。
2、irq_handler_t handler表示一个回调函数,具体的就是中断操作的具体函数,称为中断处理函数。根据
typedef irqreturn_t (*irq_handler_t)(int, void *);
irq_handler_t 是一个函数指针,也就是中断处理函数应该是一个参数分别为int和void*,返回值为irqreturn_t,是枚举型参数。
- enum irqreturn {
- IRQ_NONE,
- IRQ_HANDLED,
- IRQ_WAKE_THREAD,
- };
- typedef enum irqreturn irqreturn_t;
- #define IRQ_RETVAL(x) ((x) != IRQ_NONE)
其中中断处理函数的形式就是一个函数,采用一般的C语言就可以实现。并没有什么特别需要注意的事项,但是需要保证的是中断要尽可能的短小精悍,不要出现长时间等待以及睡眠等形式,不能出现互信息量、信号量等并发机制,只能采用自旋锁实现。
3、unsigned long flags参数表示的中断的触发方式(高低电平触发等)或者处理方式(快速、一般中断),其中的IRQF_DISABLE表示快速中断模式,IRQF_SHARED表示共享中断模式。
/*可以选择的基本触发方式和处理方式*/
- #include <linux/interrupt.h>
/*中断处理方式*/
- #ifndef IRQF_DISABLED
- #define IRQF_DISABLED SA_INTERRUPT
- #define IRQF_SAMPLE_RANDOM SA_SAMPLE_RANDOM
- #define IRQF_SHARED SA_SHIRQ
- #define IRQF_PROBE_SHARED SA_PROBEIRQ
- #define IRQF_PERCPU SA_PERCPU
- #ifdef SA_TRIGGER_MASK
- #define IRQF_TRIGGER_NONE 0
- #define IRQF_TRIGGER_LOW SA_TRIGGER_LOW
- #define IRQF_TRIGGER_HIGH SA_TRIGGER_HIGH
- #define IRQF_TRIGGER_FALLING SA_TRIGGER_FALLING
- #define IRQF_TRIGGER_RISING SA_TRIGGER_RISING
- #define IRQF_TRIGGER_MASK SA_TRIGGER_MASK
- #else /*中断触发方式*/
- #define IRQF_TRIGGER_NONE 0
- #define IRQF_TRIGGER_LOW 0
- #define IRQF_TRIGGER_HIGH 0
- #define IRQF_TRIGGER_FALLING 0
- #define IRQF_TRIGGER_RISING 0
- #define IRQF_TRIGGER_MASK 0
- #endif
- #endif
4、const char *name表示具体的设备名。
5、void *dev,这个参数比较灵活,可以选择不同的值,当中断选择为共享中断模式时,dev必须是唯一的;而当中断选择为其他处理模式时,该参数可以为NULL,也可以用来传递需要处理的变量或者资源。具体后面分析。
释放函数则相对来说简单很多,主要是释放的中断号和相关的处理数据。free_irq()针对共享设备非常有用,因为不能关闭中断disable_irq(),影响其他共享该中断号的设备。
3、功能简介
我这次完成的设计主要是采用platform设备驱动模型实现外部中断的设计。具体的操作主要是包括如下两步:
1、设备的添加(主要包括资源的添加和设备的注册)
2、驱动的设计(难点),首先要注册总线驱动,第二要完成具体驱动(外部中断或者按键)的设计(字符型驱动或者混杂设备驱动),最后实现对应的操作函数。
难点分析:
1、具体设备的结构体设计,设备包含的数据,这一步是驱动设计的重点。
2、中断函数的设计,需要处理那些数据,保证短小精悍。
3、具体操作的设计,主要包括初始化和具体函数的操作。
字符设备的驱动源码代码分析,关于混杂设备的参看引用文章。
首先是设备添加代码:
- #include<linux/module.h>
- #include<linux/init.h>
- #include<linux/kernel.h>
- #include<linux/string.h>
- #include<linux/platform_device.h>
- /*硬件相关的头文件*/
- #include<mach/regs-gpio.h>
- #include<mach/hardware.h>
- #include<linux/gpio.h>
- /*这个是硬件(CPU)密切相关的中断号*/
- #include<mach/irqs.h>
- /*硬件资源量,这是根据tq2440开发板确定的*/
- static struct resource tq2440_button_resource[]=
- {
- /*EINT0*/
- [0]=
- {
- .flags = IORESOURCE_IRQ,
- .start = IRQ_EINT0,
- .end = IRQ_EINT0,
- .name = "S3C24XX_EINT0",
- },
- /*EINT1*/
- [1]=
- {
- .flags = IORESOURCE_IRQ,
- .start = IRQ_EINT1,
- .end = IRQ_EINT1,
- .name = "S3C24xx_EINT1",
- },
- /*EINT2*/
- [2]=
- {
- .flags = IORESOURCE_IRQ,
- .start = IRQ_EINT2,
- .end = IRQ_EINT2,
- .name = "S3C24xx_EINT2",
- },
- /*EINT4*/
- [3]=
- {
- .flags = IORESOURCE_IRQ,
- .start = IRQ_EINT4,
- .end = IRQ_EINT4,
- .name = "S3C24xx_EINT4",
- },
- };
- static struct platform_device tq2440_button_device=
- {
- /*设备名*/
- .name = "tq2440_button",
- .id = -1,
- /*资源数*/
- .num_resources = ARRAY_SIZE(tq2440_button_resource),
- /*资源指针*/
- .resource = tq2440_button_resource,
- };
- static int __init tq2440_button_init(void)
- {
- int ret ;
- /*设备注册*/
- ret = platform_device_register(&tq2440_button_device);
- }
- static void __exit tq2440_button_exit(void)
- {
- /*设备的注销*/
- platform_device_unregister(&tq2440_button_device);
- }
- /*加载与卸载*/
- module_init(tq2440_button_init);
- module_exit(tq2440_button_exit);
- /*LICENSE和作者信息*/
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("GP-");
然后是驱动实现代码:
- #include<linux/types.h>
-
#include<linux/kernel.h>
-
#include<linux/init.h>
-
#include<linux/module.h>
-
#include<linux/platform_device.h>
-
#include<mach/irqs.h>
-
#include<linux/irq.h>
-
#include<mach/regs-gpio.h>
-
#include<linux/device.h>
-
#include<linux/string.h>
-
#include<linux/cdev.h>
-
#include<linux/fs.h>
-
#include<linux/spinlock.h>
-
#include<linux/wait.h>
-
#include<linux/interrupt.h>
-
#include<linux/uaccess.h>
-
#include<linux/poll.h>
-
-
-
#define NUM_RESOURCE 4
-
-
/*主设备号*/
-
int dev_major = -1;
-
-
/*中断结构体定义*/
-
struct irqs
-
{
-
int pirqs[NUM_RESOURCE];
-
char *names[NUM_RESOURCE];
-
}irqs;
-
/*完成具体设备的结构体设计*/
-
struct tq2440_button
-
{
-
/*添加具体的字符设备结构*/
-
struct cdev cdev;
-
/*用于自动创建设备*/
-
struct class *myclass;
-
/*引用次数统计表*/
-
unsigned int count;
-
/*添加并行机制*/
-
spinlock_t lock;
-
/*添加等待队列*/
-
wait_queue_head_t read_wait_queue;
-
/*数据*/
-
int bepressed;
-
/*案件值*/
-
int key_values;
-
};
-
-
static struct tq2440_button tq2440_button;
-
-
static irqreturn_t tq2440_button_interrupt_handler(int irq,void *dev_id)
-
{
-
/*得到传递过来的参数*/
-
struct tq2440_button * dev = dev_id;
-
int i;
-
/*根据得到的irq值确定具体的按键值*/
-
for (i = 0; i < NUM_RESOURCE; ++ i)
-
{
-
/*判断条件*/
-
if(irq == irqs.pirqs[i])
-
{
-
/*对关键数据添加并行机制*/
-
spin_lock(&(dev->lock));
-
/*确定被按下的值*/
-
dev->key_values = i ;
-
/*表示有数据可以读*/
-
dev->bepressed = 1;
-
spin_unlock(&(dev->lock));
-
/*唤醒等待的队列*/
-
wake_up_interruptible(&(dev->read_wait_queue));
-
}
-
}
-
/*返回值*/
-
return IRQ_RETVAL(IRQ_HANDLED);
-
}
-
-
static int tq2440_button_open(struct inode *inode,struct file *filp)
-
{
-
int i = 0,ret = 0;
-
-
/*中断申请*/
-
/*这句话主要是实现间接控制,但是还是可以直接控制*/
-
filp->private_data = &tq2440_button;
-
-
/*修改被打开的次数值*/
-
spin_lock(&(tq2440_button.lock));
-
tq2440_button.count ++ ;
-
spin_unlock(&(tq2440_button.lock));
-
-
/*如果是第一次打开则需要申请中断,这是比较推荐的方法,也可以在probe函数中申请中断*/
-
if(1==tq2440_button.count)
-
{
-
for(i = 0;i < NUM_RESOURCE; ++ i)
-
{
-
/*request_irq操作*/
-
ret = request_irq(irqs.pirqs[i],
-
tq2440_button_interrupt_handler,
-
IRQ_TYPE_EDGE_BOTH,irqs.names[i],(void *)&tq2440_button);
-
-
if(ret)
-
{
-
break;
-
}
-
}
-
if(ret)/*错误处理机制*/
-
{
-
i --;
-
for(; i >=0; --i)
-
{
-
/*禁止中断*/
-
-
disable_irq(irqs.pirqs[i]);
-
-
free_irq(irqs.pirqs[i],(void *)&tq2440_button);
-
}
-
return -EBUSY;
-
}
-
}
-
return 0;
-
}
-
-
/*closed函数*/
-
static int tq2440_button_close(struct inode *inode,struct file *filp)
-
{
-
/*确保是最后一次使用设备*/
-
int i = 0;
-
if(tq2440_button.count == 1)
-
{
-
for(i = 0; i < NUM_RESOURCE; ++ i)
-
{
-
free_irq(irqs.pirqs[i],(void *)&tq2440_button);
-
}
-
}
-
/*更新设备文件引用次数*/
-
spin_lock(&(tq2440_button.lock));
-
tq2440_button.count = 0;
-
spin_unlock(&(tq2440_button.lock));
-
-
return 0;
-
}
-
-
static unsigned long tq2440_button_read(struct file *filp,char __user *buff,
-
size_t count,loff_t offp)
-
{
-
/*设备操作*/
-
struct tq2440_button *dev = filp->private_data;
-
-
unsigned long err;
-
if(!dev->bepressed)/*确保没有采用非堵塞方式读标志*/
-
{
-
if(filp->f_flags & O_NONBLOCK)
-
return -EAGAIN;
-
else
-
/*添加等待队列,条件是bepressed*/
-
wait_event_interruptible(dev->read_wait_queue,dev->bepressed);
-
}
-
-
/*复制数据到用户空间*/
-
err = copy_to_user(buff, &(dev->key_values),min(sizeof(dev->key_values),count));
-
-
/*修改标志表示没有数据可读了*/
-
spin_lock(&(dev->lock));
-
dev->bepressed = 0;
-
spin_unlock(&(dev->lock));
-
-
/*返回数据量*/
-
-
return err ? -EFAULT:min(sizeof(dev->key_values),count);
-
}
-
-
-
static unsigned int tq2440_button_poll(struct file *filp,
-
struct poll_table_struct *wait)
-
{
-
struct tq2440_button *dev = filp->private_data;
-
-
unsigned int mask = 0;
-
-
/*将结构体中的等待队列添加到wait_table*/
-
poll_wait(filp,&(dev->read_wait_queue),wait);
-
-
/*
-
返回掩码
-
POLLIN|POLLRDNORM表示有数据可读
-
*/
-
if(dev->bepressed)
-
{
-
mask |= POLLIN | POLLRDNORM;
-
}
-
-
return mask;
-
}
-
-
/*设备的具体操作函数*/
-
static const struct file_operations tq2440_fops=
-
{
-
.owner = THIS_MODULE,
-
.open = tq2440_button_open,
-
.release = tq2440_button_close,
-
.read = tq2440_button_read,
-
.poll = tq2440_button_poll,
-
};
-
-
-
/*remove函数实现字符设备的注销操作*/
-
-
static int tq2440_button_probe(struct platform_device *dev)
-
{
-
printk("The driver found a device can be handler on platform bus\n");
-
-
/*用来存储定义好的资源,即中断号*/
-
-
struct resource * irq_resource;
-
struct platform_device *pdev = dev;
-
int i = 0,ret = 0;
-
-
/*接下来完成具体字符驱动结构体的初始化*/
-
/*1、设备号申请*/
-
-
dev_t devno;
-
-
if(dev_major > 0)/*静态申请设备号*/
-
{
-
devno = MKDEV(dev_major,0);
-
ret = register_chrdev_region(devno,1,"tq2440_button");
-
}
-
else/*动态申请设备号*/
-
{
-
ret = alloc_chrdev_region(&devno,0,1,"tq2440_button");
-
dev_major = MAJOR(devno);
-
}
-
if(ret < 0)
-
{
-
return ret;
-
}
-
-
/*完成设备类的创建,主要实现设备文件的自动创建*/
-
tq2440_button.myclass = class_create(THIS_MODULE,"tq2440_button_class");
-
-
/*2、完成字符设备的加载*/
-
cdev_init(&(tq2440_button.cdev),&tq2440_fops);
-
tq2440_button.cdev.owner = THIS_MODULE;
-
ret = cdev_add(&(tq2440_button.cdev),devno,1);
-
if(ret)
-
{
-
printk("Add device error\n");
-
return ret;
-
}
-
-
/*初始化自旋锁*/
-
spin_lock_init(&(tq2440_button.lock));
-
-
/*修改引用次数值*/
-
-
spin_lock(&(tq2440_button.lock));
-
/*被打开次数统计*/
-
tq2440_button.count = 0;
-
/*键值*/
-
tq2440_button.key_values = -1;
-
spin_unlock(&(tq2440_button.lock));
-
-
/*初始化等待队列*/
-
-
init_waitqueue_head(&(tq2440_button.read_wait_queue));
-
-
-
/*设备的创建,实现设备文件自动创建*/
-
-
device_create(tq2440_button.myclass,NULL,devno,NULL,"tq2440_button");
-
-
/*3.获得资源*/
-
for(; i < NUM_RESOURCE; ++ i)
-
{
-
/*获得设备的资源*/
-
irq_resource = platform_get_resource(pdev,IORESOURCE_IRQ,i);
-
-
if(NULL == irq_resource)
-
{
-
return -ENOENT;
-
}
-
irqs.pirqs[i] = irq_resource->start;
-
-
/*实现名字的复制操作*/
-
//strcpy(tq2440_irqs.name[i],irq_resource->name);
-
-
/*这一句是将指针的地址指向一个具体的地址*/
-
irqs.names[i] = irq_resource->name;
-
}
-
/*将设备的指针指向中断号*/
-
-
return 0;
-
}
-
-
/*probe函数实现字符设备的初始化操作*/
-
static int tq2440_button_remove(struct platform_device *dev)
-
{
-
printk("The driver found a device be removed from the platform bus\n");
-
-
/*注销设备*/
-
device_destroy(tq2440_button.myclass,MKDEV(dev_major,0));
-
/*字符设备注销*/
-
cdev_del(&(tq2440_button.cdev));
-
/*注销创建的设备类*/
-
class_destroy(&(tq2440_button.myclass));
-
/*释放设备号*/
-
unregister_chrdev_region(MKDEV(dev_major,0),1);
-
return 0;
-
}
-
-
/*完成平台总线结构体的设计*/
-
static const struct platform_driver tq2440_button_driver =
-
{
-
/*完成具体设备的初始化操作*/
-
.probe = tq2440_button_probe,
-
/*完成具体设备的退出操作*/
-
.remove = tq2440_button_remove,
-
.driver =
-
{
-
.owner = THIS_MODULE,
-
.name = "tq2440_button",
-
},
-
};
-
-
/*总线设备初始化过程*/
-
static int __init platform_driver_init(void)
-
{
-
int ret;
-
-
/*总线驱动注册*/
-
ret = platform_driver_register(&tq2440_button_driver);
-
-
/*错误处理*/
-
if(ret)
-
{
-
platform_driver_unregister(&tq2440_button_driver);
-
return ret;
-
}
-
-
return 0;
-
}
-
-
static void __exit platform_driver_exit(void)
-
{
-
/*总线驱动释放*/
-
platform_driver_unregister(&tq2440_button_driver);
-
}
-
-
/*加载和卸载*/
-
module_init(platform_driver_init);
-
module_exit(platform_driver_exit);
-
-
/*LICENSE和作者信息*/
-
MODULE_LICENSE("GPL");
-
MODULE_AUTHOR("GP-");
应用程序
- #include<stdio.h>
-
#include<stdlib.h>
-
#include<unistd.h>
-
#include<sys/types.h>
-
#include<sys/ioctl.h>
-
#include<sys/stat.h>
-
#include<sys/select.h>
-
#include<sys/time.h>
-
#include<errno.h>
-
-
int main()
-
{
-
int buttons_fd;
-
int key_value = 0;
-
- /*open函数测试*/
-
buttons_fd = open("/dev/tq2440_button",0);
-
-
if(buttons_fd < 0)
-
{
-
perror("open device buttons\n");
-
exit(1);
-
}
-
-
while(1)
-
{
-
fd_set rds;
-
int ret;
-
-
FD_ZERO(&rds);
-
FD_SET(buttons_fd,&rds);
-
/*poll函数测试*/
-
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))
-
{
- /*read函数测试*/
-
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);
-
}
-
}
-
}
-
/*release函数测试*/
-
close(buttons_fd);
-
return 0;
-
}
采用plotform总线实现中断的操作,基本中断的操作。
中断操作设备驱动集合:
阅读(1455) | 评论(0) | 转发(0) |