当前位置:文档之家› 嵌入式LINUX四按键驱动

嵌入式LINUX四按键驱动

对一个具有四个按键的按键驱动的分析源代码:/*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);elses3c_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_FSdevfs_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);#elseprintk(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);#endifreturn 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_FSdevfs_remove(DEVICE_NAME);#endifprintk(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的起始地址。

相关主题