1.TQ2440按键资源
k1 -- GPF1 -- EINT1
k2 -- GPF4 -- EINT4
k3 -- GPF2 -- EINT2
k4 -- GPF0 -- EINT0
2.内核介绍
本驱动基于linux-2.6.32.67内核
3.platform介绍
platform是linux2.6之后加入的一种虚拟总线,主要由platfor_device和platform_driver两部分构成。
(1)平台设备 platfor_device
-
struct platform_device {
-
const char * name;
-
int id;
-
struct device dev;
-
u32 num_resources;
-
struct resource * resource;
-
-
struct platform_device_id *id_entry;
-
-
/* arch specific additions */
-
struct pdev_archdata archdata;
-
};
(2)平台驱动 platform_driver
-
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;
-
struct platform_device_id *id_table;
-
};
4.平台设备实现
plat_device.c
-
#include "linux/module.h"
-
#include "linux/types.h"
-
#include "linux/fs.h"
-
#include "linux/platform_device.h"
-
#include "mach/regs-gpio.h"
-
#include "mach/irqs.h"
-
#include "linux/interrupt.h"
-
#include "linux/device.h"
-
#include "linux/io.h"
-
-
//resource
-
static struct resource s3c_buttons_resource[]={
-
[0]={
-
.start=S3C24XX_PA_GPIO,
-
.end=S3C24XX_PA_GPIO+S3C24XX_SZ_GPIO-1,
-
.flags=IORESOURCE_MEM,
-
},
-
[1]={
-
.start=IRQ_EINT1,
-
.end=IRQ_EINT1,
-
.flags=IORESOURCE_IRQ,
-
},
-
[2]={
-
.start=IRQ_EINT4,
-
.end=IRQ_EINT4,
-
.flags=IORESOURCE_IRQ,
-
},
-
[3]={
-
.start=IRQ_EINT2,
-
.end=IRQ_EINT2,
-
.flags=IORESOURCE_IRQ,
-
},
-
[4]={
-
.start=IRQ_EINT0,
-
.end=IRQ_EINT0,
-
.flags=IORESOURCE_IRQ,
-
},
-
};
-
-
static struct platform_device *s3c_buttons;
-
-
static int platform_init(void){
-
//alloc and register device
-
s3c_buttons=platform_device_alloc("2440-buttons",-1);
-
platform_device_add_resources(s3c_buttons,s3c_buttons_resource,5);
-
platform_device_add(s3c_buttons);
-
-
return 0;
-
}
-
static void platform_exit(void){
-
platform_device_unregister(s3c_buttons);
-
}
-
module_init(platform_init);
-
module_exit(platform_exit);
-
-
MODULE_LICENSE("GPL");
5.平台驱动实现
platform_driver.c
-
#include "linux/module.h"
-
#include "linux/types.h"
-
#include "linux/fs.h"
-
#include "linux/platform_device.h"
-
#include "mach/regs-gpio.h"
-
#include "mach/irqs.h"
-
#include "linux/interrupt.h"
-
#include "linux/device.h"
-
#include "linux/io.h"
-
#include "linux/miscdevice.h"
-
#include "linux/clk.h"
-
#include "linux/uaccess.h"
-
//#include "linux/map.h"
-
#include "linux/sched.h"
-
#include "linux/poll.h"
-
#include "linux/unistd.h"
-
#include "linux/irq.h"
-
-
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
-
static volatile int ev_press=0;
-
static int key_value;
-
static struct resource *button_mem;
-
static struct resource *button_irq;
-
static void __iomem *button_base;
-
static int button_irqs[6]; //record irq no
-
-
//isr
-
static irqreturn_t buttons_isr(int irq,void *dev_id){
-
int i;
-
-
printk("==>intrupt number=%d\n",irq);
-
for(i=0;i<4;i++){
-
if(irq==button_irqs[i]){
-
key_value=i;
-
ev_press=1;
-
wake_up_interruptible(&button_waitq);
-
}
-
}
-
return IRQ_RETVAL(IRQ_HANDLED);
-
}
-
-
//misc opt
-
static int s3c2440_buttons_open(struct inode *inode,struct file *file){
-
int i;
-
int err=0;
-
-
for(i=0;i<4;i++){
-
if(button_irqs[i]<0){
-
continue;
-
}
-
-
err=request_irq(button_irqs[i],buttons_isr,IRQ_TYPE_EDGE_RISING,NULL,NULL);
-
if(err)
-
break;
-
}
-
-
if(err){
-
i--;
-
for(;i>=0;i--){
-
if(button_irqs[i]<0)
-
continue;
-
disable_irq(button_irqs[i]);
-
free_irq(button_irqs[i],NULL);
-
}
-
return -EBUSY;
-
}
-
-
ev_press=0;
-
return 0;
-
}
-
static int s3c2440_buttons_close(struct inode *inode,struct file *file){
-
int i;
-
for(i=0;i<4;i++){
-
if(button_irqs[i]<0)
-
continue;
-
free_irq(button_irqs[i],NULL);
-
}
-
return 0;
-
}
-
static int s3c2440_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,&key_value,sizeof(key_value));
-
return sizeof(key_value);
-
}
-
static unsigned int s3c2440_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); //can read
-
return mask;
-
}
-
//misc ops
-
static struct file_operations s3c2440_buttons_fops={
-
.owner=THIS_MODULE,
-
.open=s3c2440_buttons_open,
-
.release=s3c2440_buttons_close,
-
.read=s3c2440_buttons_read,
-
.poll=s3c2440_buttons_poll,
-
};
-
-
//misc
-
static struct miscdevice s3c2440_miscdev={
-
.minor=MISC_DYNAMIC_MINOR,
-
.name="buttons",
-
.fops=&s3c2440_buttons_fops,
-
};
-
-
//driver
-
static int s3c2440_buttons_probe(struct platform_device *pdev){
-
struct resource *res;
-
struct device *dev;
-
int ret;
-
int size;
-
int i;
-
-
printk("probe:driver-device mach ok.\n");
-
dev=&pdev->dev;
-
-
res=platform_get_resource(pdev,IORESOURCE_MEM,0);
-
if(res==NULL){
-
dev_err(dev,"no memory resource specified.\n");
-
return -ENOENT;
-
}
-
-
//alloc PA
-
size=(res->end-res->start)+1;
-
button_mem=request_mem_region(res->start,size,pdev->name);
-
if(button_mem==NULL){
-
dev_err(dev,"failed to get memory region.\n");
-
ret=-ENOENT;
-
goto err_req;
-
}
-
//remap:PA->VA
-
button_base=ioremap(res->start,size);
-
if(button_base==NULL){
-
dev_err(dev,"failed to ioremap region.\n");
-
ret=-EINVAL;
-
goto err_req;
-
}
-
printk(KERN_DEBUG"probe:mapped button_base=%p.\n",button_base);
-
-
for(i=0;i<4;i++){
-
button_irq=platform_get_resource(pdev,IORESOURCE_IRQ,i);
-
if(button_irq==NULL){
-
dev_err(dev,"no irq resource specified.\n");
-
ret=-ENOENT;
-
goto err_map;
-
}
-
button_irqs[i]=button_irq->start;
-
printk("button_irqs[%d]=%d.\n",i,button_irqs[i]);
-
}
-
-
ret=misc_register(&s3c2440_miscdev);
-
-
return 0;
-
-
err_map:
-
iounmap(button_base);
-
err_req:
-
release_resource(button_mem);
-
kfree(button_mem);
-
-
return ret;
-
}
-
static int s3c2440_buttons_remove(struct platform_device *dev){
-
release_resource(button_mem);
-
kfree(button_mem);
-
button_mem=NULL;
-
-
iounmap(button_base);
-
misc_deregister(&s3c2440_miscdev);
-
printk("dirver find device unliuged.\n");
-
return 0;
-
}
-
-
static struct platform_driver buttons_driver={
-
.probe=s3c2440_buttons_probe,
-
.remove=s3c2440_buttons_remove,
-
.driver={
-
.owner=THIS_MODULE,
-
.name="2440-buttons",
-
},
-
};
-
-
-
static int buttons_init(void){
-
printk("2440 buttons driver init.\n");
-
-
//driver register
-
platform_driver_register(&buttons_driver);
-
-
return 0;
-
}
-
-
static void buttons_exit(void){
-
platform_driver_unregister(&buttons_driver);
-
}
-
module_init(buttons_init);
-
module_exit(buttons_exit);
-
-
MODULE_LICENSE("GPL");
6.测试程序
根据驱动设计特点,编写测试程序如下:
app_button.c
-
#include "stdio.h"
-
#include "string.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",0);
-
if(buttons_fd<0){
-
perror("open device buttons.\n");
-
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 error.\n");
-
exit(1);
-
}
-
if(ret==0){
-
printf("timerout.\n");
-
}
-
else{
-
if(FD_ISSET(buttons_fd,&rds)){
-
int ret1=read(buttons_fd,&key_value,sizeof(key_value));
-
if(ret1!=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;
-
}
7.编译内核模块和测试程序,生成plat_device.ko、plat_driver.ko和app_button,通过nfs方式下载至TQ2440开发板,运行后,结果如下:
阅读(1909) | 评论(0) | 转发(0) |