嵌入式LINUX四按键驱动
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
对一个具有四个按键的按键驱动的分析
源代码:
/*Headers-------------------------------------------------*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-irq.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
#ifdef CONFIG_DEVFS_FS
#include <linux/devfs_fs_kernel.h>
#endif
/*V ars----------------------------------------------------*/
#define DEVICE_NAME "buttons"
#define EXTINT_OFF (IRQ_EINT4 - 4)
unsigned int buttons_major=0;
unsigned int buttons_minor=0;
unsigned int type = IRQT_FALLING;
struct button_info {
unsigned int irq_no;
unsigned int gpio_port;
unsigned int IN;
int button_no;
};
struct button_info realarm_button_info[4] = {
{ IRQ_EINT19, S3C2410_GPG11, S3C2410_GPG11_INP, 1 }, { IRQ_EINT8, S3C2410_GPG0, S3C2410_GPG0_INP, 2 },
{ IRQ_EINT11, S3C2410_GPG3, S3C2410_GPG3_INP, 3 }, { IRQ_EINT2, S3C2410_GPF2, S3C2410_GPF2_INP, 4 }, };
struct realarm_button_dev
{
struct button_info buttoninfo_tab[4];
int extint_num[4];
struct semaphore sem;
wait_queue_head_t wq;
struct cdev buttons_dev;
};
struct realarm_button_dev *realarm_button_device;
void s3c_irq_ack(unsigned int irqno)
{
unsigned long bitval = 1UL << (irqno - IRQ_EINT0);
__raw_writel(bitval, S3C2410_SRCPND);
__raw_writel(bitval, S3C2410_INTPND);
return;
}
void s3c_irqext_ack(unsigned int irqno)
{
unsigned long req;
unsigned long bit;
bit = 1UL << (irqno - EXTINT_OFF);
__raw_writel(bit, S3C2410_EINTPEND);
req = __raw_readl(S3C2410_EINTPEND);
if (irqno <= IRQ_EINT7 )
{
if ((req & 0xf0) == 0)
s3c_irq_ack(IRQ_EINT4t7);
}
else
{
if ((req >> 8) == 0)
s3c_irq_ack(IRQ_EINT8t23);
return;
}
int realarm_interrupt_init(unsigned int irq, unsigned int type) {
unsigned long gpcon_reg;
unsigned long gpcon_offset;
unsigned long extint_reg;
unsigned long extint_offset;
unsigned long newvalue = 0;
unsigned long value;
if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3))
{
gpcon_reg = S3C2410_GPFCON;
extint_reg = S3C2410_EXTINT0;
gpcon_offset = (irq - IRQ_EINT0) * 2;
extint_offset = (irq - IRQ_EINT0) * 4;
}
else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7))
{
gpcon_reg = S3C2410_GPFCON;
extint_reg = S3C2410_EXTINT0;
gpcon_offset = (irq - EXTINT_OFF) * 2;
extint_offset = (irq - EXTINT_OFF) * 4;
}
else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15)) {
gpcon_reg = S3C2410_GPGCON;
extint_reg = S3C2410_EXTINT1;
gpcon_offset = (irq - IRQ_EINT8) * 2;
extint_offset = (irq - IRQ_EINT8) * 4;
}
else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23)) {
gpcon_reg = S3C2410_GPGCON;
extint_reg = S3C2410_EXTINT2;
gpcon_offset = (irq - IRQ_EINT8) * 2;
extint_offset = (irq - IRQ_EINT16) * 4;
} else
{
return -1;
/* Set the GPIO to external interrupt mode */
value = __raw_readl(gpcon_reg);
value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset);
__raw_writel(value, gpcon_reg);
/* Set the external interrupt to pointed trigger type */
switch (type)
{
case IRQT_NOEDGE:
printk(KERN_WARNING "No edge setting!\n");
break;
case IRQT_RISING:
newvalue = S3C2410_EXTINT_RISEEDGE;
break;
case IRQT_FALLING:
newvalue = S3C2410_EXTINT_FALLEDGE;
break;
case IRQT_BOTHEDGE:
newvalue = S3C2410_EXTINT_BOTHEDGE;
break;
case IRQT_LOW:
newvalue = S3C2410_EXTINT_LOWLEV;
break;
case IRQT_HIGH:
newvalue = S3C2410_EXTINT_HILEV;
break;
default:
printk(KERN_ERR "No such irq type %d", type);
return -1;
}
value = __raw_readl(extint_reg);
value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset); __raw_writel(value, extint_reg);
return 0;
static irqreturn_t buttons_irq(int irq, void *dev_id, struct pt_regs *req)
{
struct button_info *k;
int i;
int found = 0;
struct realarm_button_dev* dev = (struct realarm_button_dev *) dev_id;
int up;
for (i = 0; i < sizeof dev->buttoninfo_tab / sizeof dev->buttoninfo_tab[0]; i++) { k = dev->buttoninfo_tab + i;
if (k->irq_no == irq) {
found = 1;
if(irq <= IRQ_EINT3)
s3c_irq_ack(irq);
else
s3c_irqext_ack(irq);
disable_irq(irq);
mdelay(200);
s3c2410_gpio_cfgpin(k->gpio_port, k->IN);
up = s3c2410_gpio_getpin(k->gpio_port);
if (!up) {
dev->extint_num[i]++;
wake_up_interruptible(&dev->wq);
}
realarm_interrupt_init(irq, type);
enable_irq(irq);
break;
}
}
if (!found) {
printk("bad irq %d in button\n", irq);
return IRQ_NONE;
}
return IRQ_HANDLED;
}
int realarm_request_irq(struct realarm_button_dev* dev)
{
struct button_info *k;
int i;
int ret;
unsigned int irq;
for (i = 0; i < sizeof dev->buttoninfo_tab / sizeof dev->buttoninfo_tab[0]; i++) { k = dev->buttoninfo_tab + i;
irq = k->irq_no;
realarm_interrupt_init(irq, type);
ret = request_irq(irq, &buttons_irq, SA_INTERRUPT, DEVICE_NAME, dev);
if (ret) {
printk(KERN_WARNING "buttons:can't get irq no.\n");
return ret;
}
}
return 0;
}
int buttons_open(struct inode *inode, struct file *filp)
{
struct realarm_button_dev *dev;
dev = container_of(inode->i_cdev, struct realarm_button_dev, buttons_dev);
filp->private_data = dev;
realarm_request_irq(dev);
return 0;
}
int buttons_release(struct inode *inode, struct file *filp)
{
struct realarm_button_dev *dev = (struct realarm_button_dev *)filp->private_data;
struct button_info *k;
int i;
for (i = 0; i < sizeof dev->buttoninfo_tab / sizeof dev->buttoninfo_tab[1]; i++) { k = dev->buttoninfo_tab + i;
free_irq(k->irq_no, dev);
}
return 0;
}
ssize_t buttons_read(struct file *filp,char __user *buffer,size_t count,loff_t *ppos)
{
struct realarm_button_dev *dev = (struct realarm_button_dev *)filp->private_data;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
interruptible_sleep_on(&dev->wq);
if(copy_to_user(buffer, (char *)dev->extint_num, sizeof(dev->extint_num)))
{
printk(KERN_ALERT "Copy to user error.\n");
return -EFAULT;
}
up(&dev->sem);
return sizeof(dev->extint_num);
}
struct file_operations buttons_fops = {
.owner = THIS_MODULE,
.open = buttons_open,
.release = buttons_release,
.read =buttons_read,
};
static int __init buttons_init(void)
{
int i;
int ret;
dev_t dev;
printk(KERN_INFO "Initial RealARM Buttons driver!\n");
if (buttons_major) {
dev = MKDEV(buttons_major, buttons_minor);
ret = register_chrdev_region(dev, 1, DEVICE_NAME);
} else {
ret = alloc_chrdev_region(&dev, buttons_minor, 1, DEVICE_NAME);
buttons_major = MAJOR(dev);
}
if (ret < 0) {
printk(KERN_WARNING "Buttons: can't get major %d\n", buttons_major);
return ret;
}
realarm_button_device = kmalloc(sizeof(struct realarm_button_dev), GFP_KERNEL);
if (!realarm_button_device) {
unregister_chrdev_region(dev, 1);
ret = -ENOMEM;
return ret;
}
memset(realarm_button_device, 0, sizeof(struct realarm_button_dev));
memcpy(realarm_button_device->buttoninfo_tab, realarm_button_info, sizeof(realarm_button_info));
for(i = 0; i < 4; i++)
{
realarm_button_device->extint_num[i] = 0;
}
init_MUTEX(&realarm_button_device->sem);
init_waitqueue_head(&realarm_button_device->wq);
cdev_init(&realarm_button_device->buttons_dev, &buttons_fops);
realarm_button_device->buttons_dev.owner = THIS_MODULE;
realarm_button_device->buttons_dev.ops = &buttons_fops;
ret = cdev_add(&realarm_button_device->buttons_dev, dev, 1);
if (ret) {
unregister_chrdev_region(dev, 1);
printk(KERN_NOTICE "Error %d adding buttons device\n",ret);
return ret;
}
#ifdef CONFIG_DEVFS_FS
devfs_mk_cdev(dev, S_IFCHR | S_IRUSR | S_IWUSR, DEVICE_NAME);
printk(KERN_INFO"/dev/%s has been added to your system.\n",DEVICE_NAME);
#else
printk(DEVICE_NAME "Initialized\n");
printk(KERN_INFO "You must create the dev file manually.\n");
printk(KERN_INFO "Todo: mknod c /dev/%s %d 0\n",DEVICE_NAME,buttons_major);
#endif
return 0;
}
static void __exit buttons_cleanup(void)
{
dev_t dev = MKDEV(buttons_major, buttons_minor);
cdev_del(&realarm_button_device->buttons_dev);
kfree(realarm_button_device);
unregister_chrdev_region(dev, 1);
#ifdef CONFIG_DEVFS_FS
devfs_remove(DEVICE_NAME);
#endif
printk(KERN_INFO "unregistered the %s\n",DEVICE_NAME); }
module_init(buttons_init);
module_exit(buttons_cleanup);
MODULE_AUTHOR("LiuRui");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Key driver for RealARM");
分析:
四个按键信息说明
struct button_info realarm_button_info[4] = {
{ IRQ_EINT19, S3C2410_GPG11, S3C2410_GPG11_INP, 1 }, { IRQ_EINT8, S3C2410_GPG0, S3C2410_GPG0_INP, 2 }, { IRQ_EINT11, S3C2410_GPG3, S3C2410_GPG3_INP, 3 }, { IRQ_EINT2, S3C2410_GPF2, S3C2410_GPF2_INP, 4 }, };
自定义按键结构把字符设备嵌入其中
struct realarm_button_dev
{
struct button_info buttoninfo_tab[4];//记录每个按键信息
int extint_num[4]; //用于记录按键的次数
struct semaphore sem;//消息变量
wait_queue_head_t wq;//等待队列
struct cdev buttons_dev;//嵌入字符设备
};
程序流程分析:
module_init(buttons_init);
module_exit(buttons_cleanup);
对file_operations 中各个函数的实现分析:
struct file_operations buttons_fops = {
.owner = THIS_MODULE,
.open = buttons_open,
.release = buttons_release,
.read = buttons_read,
};
buttons_open
先来说一下container_of函数
318#define container_of(ptr, type, member) ({ \
319const typeof( ((type *)0)->member ) *__mptr = (ptr); \
320(type *)( (char *)__mptr - offsetof(type,member) );})
指针ptr指向结构体type中的成员member;通过指针ptr,返回结构体type的起始地址。
type
|---------- ----- |
| |
| |
|---------- ----- |
ptr-->| member |
|---------- ----- |
| |
| |
|---------- ----- |
buttons_release
buttons_read
来分析中断处理子程序流程
ret = request_irq(irq, &buttons_irq, SA_INTERRUPT, DEVICE_NAME, dev); 通过这句话把中断号和中断处理子程序绑定起来
对中断处理程序的分析
buttons_irq
字符设备驱动自此分析完毕,发现驱动并不向大家想象的那样难。
通常各种常见驱动比如字符设备驱动,块设备驱动都有一定框架,各个模块的功能也很清晰,只要我们按照这个框架,一步一步就可以完成我们自己的驱动。