您的位置:首页 > 其它

基于platform总线的中断(按键)字符设备驱动设计

2012-12-27 21:59 525 查看
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-<gp19861112@yahoo.com.cn>");

然后是驱动实现代码:

#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-<gp19861112@yahoo.com.cn>");

应用程序

#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总线实现中断的操作,基本中断的操作。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-<gp19861112@yahoo.com.cn>");

然后是驱动实现代码:

#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-<gp19861112@yahoo.com.cn>");

应用程序

#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总线实现中断的操作,基本中断的操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: