字符设备驱动学习(2)

xiaoxiao2021-02-28  106

学习目标:在字符设备驱动学习(1)的按键查询基础上改为按键中断。

1、什么是中断 中断是异常的一种,异常就是可以打断cpu正常运行流程的一些事情,比如外部中断、未定义指令、swi指令。cup暂停当前的程序,先处理异常事件,然后再继续执行被中断的程序。

2、linux内核对异常的设置 内核在strrt_kernel函数中调用了trap_init函数和init_IRQ函数来设置异常的处理函数。 ①memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start); memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start); 这两个函数对异常向量的拷贝,__vectors_end 至 __vectors_start之间为异常向量表。 位于arch/arm/kernel/entry-armv.S中:拷贝到0xffff0000,将异常处理函数从__stubs_start至__stubs_end拷贝到0xffff0000+0x200处。 ②.equ stubs_offset, __vectors_start + 0x200 - __stubs_start 复制完后,重新定位跳转的位置。(向量被复制到0xffff0000处,跳转的目的代码被复制到xffff0000+0x200处) 注:定义静态符号: .equ 命令用于把常量值设置为可以在文本段中使用的符号

如: .equ factor, 3 .equ LINUX_SYS_CALL, 0x80 经过设置之后,数据符号值是不能在 程序中改动的。

3、中断处理的体系结构(内核分析) 内核中将所有的中断统一编号,使用一个irq_desc的结构数组来描述中断,数组中每项元素对应一个中断,也可能是一组中断,它们公用一个中断号,里面记录了中断名称、状态、标记(类型、是否共享)并提供中断的底层硬件访问函数(清除、屏蔽、使能中断),可提供了这个中断的处理函数入口,通过它可以调用用户注册的中断处理函数。 中断处理体系结构的初始化(irq_desc结构体注组成的数组的初始化):init_IRQ()用于初始化:

void __init init_IRQ(void) { int irq; for (irq = 0; irq < NR_IRQS; irq++) irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE; ... init_arch_irq(); }

1、for循环是初始化数组元素的状态 2、init_arch_irq():跟体系结构相关,不同平台对应函数不一样。最终调用与平台相关的函数——>XXX_init_irq(),主要功能是对数组中每项irq_desc中的元素赋值,chip、handle_irq等。

用户注册中断处理函数(驱动程序):通过request_irq()函数向内核注册处理函数。根据中断号,找到对应中断的irq_desc成员,在irq_desc的成员action(链表)添加一个节点。

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) { struct irqaction *action; int retval; ........ action = kmalloc(sizeof(struct irqaction), ... action->handler = handler; action->flags = irqflags; cpus_clear(action->mask); action->name = devname; action->next = NULL; action->dev_id = dev_id; .... retval = setup_irq(irq, action); if (retval) kfree(action); return retval;

4、在查询基础上改为中断

#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/mm.h> #include <linux/sched.h> #include <linux/init.h> #include <linux/cdev.h> #include <linux/irq.h> #include <asm/io.h> #include <asm/system.h> #include <asm/uaccess.h> #include <linux/slab.h> #include <linux/device.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <asm/irq.h> #include <asm/arch/regs-gpio.h> #define KEY_DRIVER1_MAJOR 0 /*休眠队列*/ static DECLARE_WAIT_QUEUE_HEAD(boutton_qeueue); /*中断事件标志,中断中进行置1,read中将它清0*/ static volatile int en_press = 0; /*声明用到的结构体和变量*/ struct cdev key_driver1_cdev; static int key_driver1_major = KEY_DRIVER1_MAJOR; dev_t key_dev_num;//dev_t类型设备号 static struct class *key_drv_class; static struct class_device *key_drv_class_dev; /*用于地址映射*/ volatile unsigned long *gpfcon; volatile unsigned long *gpfdat; volatile unsigned long *gpgcon; volatile unsigned long *gpgdat; #define rGPFCON 0X56000050 #define rGPGCON 0X56000060 struct pin_desc{ unsigned int pin; unsigned char key_val; }; /*按下时:0x01,02,03,04*/ struct pin_desc pin_desc[4] = { {S3C2410_GPF0, 0X01},//刚开始吧0写错为1,导致按下按键总是一个值 {S3C2410_GPF2, 0X02}, {S3C2410_GPG3, 0X03}, {S3C2410_GPG11,0X04}, }; static unsigned char key_val = 0;//按键值 /* * */ static irqreturn_t button_irq(int irq, void *dev_id) { struct pin_desc *p = (struct pin_desc *)dev_id; unsigned int pin_val; pin_val = s3c2410_gpio_getpin(p->pin); key_val = 0; if(pin_val) { /*松开*/ key_val = 0x80 | p->key_val; //printk("kernel key_val = 0x%x\n",key_val);//调试用的 } else { /*按下*/ key_val = p->key_val; //printk("kernel anxia key_val = 0x%x\n",key_val);//调试用的 } en_press = 1; //表示中断发生 wake_up_interruptible(&boutton_qeueue);//唤醒休眠的进程 return IRQ_HANDLED; } static int key_driver1_open1(struct inode *node, struct file *file) { /*采用查询方式,将按键的引脚进行配置为输入*/ //*gpfcon &= ~((0x3 << (0*2)) | (0x3 << (2*2))); //*gpgcon &= ~((0x3 << (3*2)) | (0x3 << (11*2))); /*中断注册函数*/ request_irq(IRQ_EINT0,button_irq,IRQT_BOTHEDGE,"S2",&pin_desc[0]); request_irq(IRQ_EINT2,button_irq,IRQT_BOTHEDGE,"S3",&pin_desc[1]); request_irq(IRQ_EINT11,button_irq,IRQT_BOTHEDGE,"S4",&pin_desc[2]); request_irq(IRQ_EINT19,button_irq,IRQT_BOTHEDGE,"S5",&pin_desc[3]); return 0; } static int key_driver1_close(struct inode *inode, struct file *file) { free_irq(IRQ_EINT0,&pin_desc[0]); free_irq(IRQ_EINT2,&pin_desc[1]); free_irq(IRQ_EINT11,&pin_desc[2]); free_irq(IRQ_EINT19,&pin_desc[3]); return 0; } static int key_driver1_read (struct file * file, char __user * buf, size_t size, loff_t *ppos) { int ret; if(size != 1) return -EINVAL; /* 如果没有按键安动作,休眠,如果有按键按下,就直接返回*/ wait_event_interruptible(boutton_qeueue,en_press); ret = copy_to_user(buf,&key_val,1); en_press = 0; return 1; } /*文件操作结构体*/ static const struct file_operations key_driver1_fops = { .owner = THIS_MODULE, .read = key_driver1_read, .open = key_driver1_open1, .release = key_driver1_close }; /*设备驱动模块加载函数*/ int key_driver1_init(void) { int result; int err; key_dev_num = MKDEV(key_driver1_major, 0); /* 申请设备号*/ if (key_driver1_major) result = register_chrdev_region(key_dev_num, 1, "key_driver1"); else /* 动态申请设备号 */ { result = alloc_chrdev_region(&key_dev_num, 0, 1, "key_driver1"); key_driver1_major = MAJOR(key_dev_num); } if (result < 0) return result; /* *进行cdev的初始化,将cdev与key_driver1_fops关联 */ cdev_init(&key_driver1_cdev, &key_driver1_fops); key_driver1_cdev.owner = THIS_MODULE; key_driver1_cdev.ops = &key_driver1_fops; err = cdev_add(&key_driver1_cdev, key_dev_num, 1); if (err) printk(KERN_NOTICE "Error %d ", err); /* * 为设备创建类 * 加载驱动模块时在/sys/class 目录下自动创建car_class 文件夹 */ key_drv_class = class_create(THIS_MODULE, "key_driver1"); if(IS_ERR(key_drv_class)) { printk("cannot create key_drv_class!\n"); return 0; } /*创建设备文件节点,避免需要手动创建*/ key_drv_class_dev = class_device_create(key_drv_class, NULL,key_dev_num, NULL, "key_driver1"); /*将GPIO 的物理地址映射到内核空间*/ gpfcon = (volatile unsigned long *)ioremap(rGPFCON, 16); gpfdat = gpfcon + 1; gpgcon = (volatile unsigned long *)ioremap(rGPGCON, 16); gpgdat = gpgcon + 1; return 0; } /*模块卸载函数*/ void key_driver1_exit(void) { cdev_del(&key_driver1_cdev); /*注销cdev*/ unregister_chrdev_region(key_dev_num, 1); /*释放设备号*/ /*删除设备文件和dev/class 目录下的相应文件夹*/ class_device_unregister(key_drv_class_dev); class_destroy(key_drv_class); /*解除GPIO 映射*/ iounmap(gpfcon); iounmap(gpgcon); } MODULE_AUTHOR("healer"); MODULE_LICENSE("Dual BSD/GPL"); module_init(key_driver1_init); module_exit(key_driver1_exit);

应用程序代码

#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> int main(int arvc,char **argv) { int fd; int count = 1; unsigned char key_val = 0; fd = open("/dev/key_driver1",O_RDWR); if(fd < 0) { printf("can not open!\n"); } while(1) { read(fd,&key_val,1); printf("key_val =0x%x\n",key_val); } return 0;

Makefile

ifneq ($(KERNELRELEASE),) obj-m := inter_key.o else PWD := $(shell pwd) KDIR := /home/healer/myshare/linuxkernel/linux-2.6.22.6 all: make -C $(KDIR) M=$(PWD) clean: rm -rf *.o *.ko *.mod.c *.symvers *.c~ *~ endif

makefile代码: obj-m := inter_key.o 根据自己编写的驱动文件名来定,xxx.c对应这里obj-m :=xxx.o KDIR := /home/healer/myshare/linuxkernel/linux-2.6.22.6为内核代码所在目录(根据自己内核目录定),因为我们编写的驱动时以模块的形式,需要内核代码得支持。

转载请注明原文地址: https://www.6miu.com/read-38949.html

最新回复(0)