S5PV210在Linux下如何编写驱动
我使用了两种驱动开发的模型来写了smart210上的按键驱动程序,这里做一下总结以及提供他人参考以及建议和改进,最后一个原因是自己很久没有写过博客了,现在想分享一下linux驱动开发程序的编写。
首先是Linux的中断处理机制。裸机开发中,通过中断标识一个函数指明该函数是中断处理函数,在编译器处理时候,也会对该函数的指针保存或特殊处理,当把该程序烧写到板子的时候,会把这个中断函数的地址加载到指定的地址,当中断发生时,通过寄存器保存的该函数的指针,进行中断函数地处理。但是在Linux中采用了类似信号处理这一种机制,在Linux的底层,当有中断发生时,就会有板级的代码产生一个指定的中断号,我们的中断驱动就是在内核中注册当接收的该中断信号后的处理函数,具体函数如下:
int request_irq(irq,func, trigger_type, name, dev_id );
这里我是用伪代码描述:
irq: 需要接受或检测的中断号
func: 中断处理函数
trigger_type: 中断触发模式
name: 中断名称
dev_id: 这个参数是void类型的指针,最终传递给中断处理函数的第二个参数
中断的处理分为Top-part,和 Bottom-part,可以使用多种处理方式进行处理,比如:tasklet, queue等等。这部分内容请查阅相关书籍
1. tasklet处理中断的Top-part和Bottom-part
这里直接使用了字符驱动模型,问题在于比较麻烦,尤其是驱动本身的注册,但是这种模型的驱动比较自由的。(头文件未补全,并且只是副本--真确的版本在虚拟机中,编译可能有点问题,可以私聊要完全的代码),主要参考驱动编写流程和框架。
#include <linux/fs.h> #include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/types.h> #include <linux/cdev.h> /* In this program, We using Top-Bottom mechinenism to * handle the interrupt, we can use tasklet to resolve * the interrupt. */ #define DEVICE_NAME "interruptv1" #define CLASS_NAME "interrupts" struct key_device { cdev cdev; unsigned int count; unsigned int flags; }; static struct tasklet key_tasklet; static dev_t major; static struct class *key_class; static struct key_device *key_devp; static void interrupt_handle(unsigned long data) { printk("<kernel>-<Bottom>: Welcome to Interrupt-B, The count = %d .\n", data); } static irqreturn_t interrupt_tasklet(int irq, void *dev_id) { printk("<kernel>-<Top>: Welcome to Interrupt-Top, The irq = %d .\n", irq); key_devp->count++; tasklet_schedule(&key_tasklet); return IRQ_HANDLED; } static int interrupt_open(struct inode *inode, struct file *filep) { int ret; filep->private_data =key_devp; //request a interrupt number ret = request_irq(IRQ_EINT(16), interrupt_tasklet, IRQF_TRIGGER_FALLING, DEVICE_NAME, (void *) NULL); if(ret < 0){ printk("<kernel>: Fail to request interrupt number, We stay in open !\n"); free_irq(IRQ_EINT(16), NULL); return -EAGAIN; } printk("<kernel>: Success request interrupt number! \n"); return 0; } static int interrupt_close(struct inode *inode, struct file *filep) { free_irq(IRQ_EINT(16), NULL); tasklet_kill(&key_tasklet); printk("<kernel>: Success Release IRQ !\n"); return 0; } static int interrupt_write(struct file *filep, char __user *buf, size_t size, loff_t *ppos) { pirntk("<kernel>: Fail to write data to Interrupt driver !\n"); return 0; } static int interrupt_read(struct file *filep, char __user *buf, size_t size, loff_t *ppos) { printk("<kernel>: Fail to read data from Interrupt driver !\n"); return 0; } DECLARE_TASKLET(key_tasklet, interrupt_handle, key_dev->count); struct file_operations interrupt_fops = { .owner = THIS_MODULE, .open = interrupt_open, .release = interrupt_close, .read = interrupt_read, .write = interrupt_write, }; static void device_setup(struct cdev *devp, int minor) { int err; dev_t devno = MKDEV(major, minor); //add a char driver to kernel cdev_init(devp, &interrupt_fops); devp->owner = THIS_MODULE; err = cdev_add(devp, devno, 1); if(err) printk("<kernel> : Fail to add cdev to kernel !\n"); //add device driver to file-system key_class = class_create(THIS_MODULE, CLASS_NAME); if(IS_ERR(key_class)){ printk("<kernel>: Fail to create a class in the filesystem !\n"); return -EINVAL; } device_create(key_class, NULL, devno, NULL, DEVICE_NAME); } static int __init interrupt_init(void) { int ret; dev_t devno; if((ret = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME)) < 0){ printk("<kernel>: Fail to alloc a char-dev number !\n"); return -EINVAL; } major = MAJOR(devno); key_devp = kmalloc(sizeof(struct key_device), GPF_KERNLE); if(!key_devp){ printk("<kernel>: Fail to malloc a mem region !\n"); goto fail1; } memset(key_devp, 0, sizeof(struct key_device)); //set_irq_type(IRQ_EINT(16), IRQF_TRIGGER_FALLING); no header file device_setup(&key_devp->cdev, 0); return 0; fail1: unregister_chrdev_region(MKDEV(major, 0), 1); return -ENOMEM; } static void __exit interrupt_exit(void) { cdev_del(&key_devp->cdev); kfree(key_devp); unregister_chrdev_region(MKDEV(major, 0), 1); device_destroy(key_class, MKDEV(major, 0)); class_destroy(key_class); } module_init(interrupt_init); module_exit(interrupt_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("weirdo"); MODULE_DESCRIPTION("This module using for smart210 interrupts");
2. 普通中断驱动模型---miscdevice
#include <linux/miscdevice.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/module.h> #include <linux/irq.h> #include <linux/interrupt.h>#include <mach/gpio.h> //contain gpio_to_irq() func #define INT_NAME "key1" #define DEVICE_NAME "interruptv1" #define DEVICE_MINOR 12 static irqreturn_t interrupt_handle(int irq, void *dev_id) { printk("<kernel>: I‘m in kernel! You had beat my heart !\n"); return IRQ_HANDLED; } static int interrupt_open(struct inode *inode, struct file *filep) { int irq, ret; irq = gpio_to_irq(S5PV210_GPH2(0)); ret = request_irq(irq, interrupt_handle, IRQF_TRIGGER_FALLING, INT_NAME, 0); if(ret < 0){ printk("<kernel>: Fail to request irq number ----- 16 !\n"); return -EFAULT; } printk("<kernel>: Success register interrupt handle function --- 16! \n"); return 0; } static int interrupt_close(struct inode *inode, struct file *filep) { int irq; irq = gpio_to_irq(S5PV210_GPH2(0)); free_irq(irq, NULL); printk("<kernel>: We will close interrupt file !\n"); return 0; } static int interrupt_read(struct file *filep, char __user *buf, size_t size, loff_t *ppos) { printk("<kernel>: You can‘t read data from the device! (-_-)\n"); return 0; } static int interrupt_write(struct file *filep, const char __user *buf, size_t size, loff_t *ppos) { printk("<kernel>: You can‘t write data to the device! (-_-)\n"); return 0; } static struct file_operations interrupt_fops = { .owner = THIS_MODULE, .open = interrupt_open, .release = interrupt_close, .read = interrupt_read, .write = interrupt_write }; static struct miscdevice int_misc = { .minor = DEVICE_MINOR, .name = DEVICE_NAME, .fops = &interrupt_fops, }; static int __init interrupt_init(void) { int ret; ret = misc_register(&int_misc); if(ret != 0) { print("<kernel>: Fail to register interrupt driver! \n"); retur -EFAULT; } printk("<kernel>: Success to register interrupt driver! \n"); return 0; } static void __exit interrupt_exit(void) { misc_deregister(&int_misc); } module_init(interrupt_init); module_exit(interrupt_exit); MODULE_AUTHOR("weirdo"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("This driver write for smart210 8 keys interrupts");
3. 驱动测试文件
由于驱动测试文件在虚拟机中,个人比较lazy,但简要叙述一些驱动中的关键点:
#include <>#include <>#include <>#include <>#define DEVICE_NAME "/dev/interruptv1" int main(void) { int fd; char input; fd = open(DEVICE_NAME); if(irq){ .... } for( ; input != ‘e‘ }