4412驱动-等待队列

来源:互联网 发布:教学计划编制c语言 编辑:程序博客网 时间:2024/06/14 19:54
定义等待队列:
wait_queue_head_t  button_waitq
初始化等待队列头:
init_waitqueue_head(button_waitq) 
 定义并初始化等待队列头(相当于上面两句合并)

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);//定义并初始化等待队列头


驱动

#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/interrupt.h> //request_irq#include <mach/irqs.h> //中断号,已包含plat/irqs.h#include <linux/fs.h>#include <linux/device.h> //class_create device_create#include <mach/regs-gpio.h>#include <linux/io.h> //ioremap ioread32 iowrite32#include <linux/sched.h>#include <linux/of.h>  #include <linux/of_device.h>  #include <linux/delay.h>  #include <asm/uaccess.h>  #define DEV_NAME    "wait-dev"  static struct class *keydrv_class;  static struct class_device  *keydrv_class_dev;  static DECLARE_WAIT_QUEUE_HEAD(button_waitq);/* 中断事件标志, 中断服务程序将它置1,third_drv_read将它清0 */static volatile int ev_press = 0;//LED灯IO口的地址,也就是刚刚我们在上面的芯片手册看到的Address  #define GPM4COM     0x110002E0  //定义配置模式的指针变量  volatile unsigned long *led_config = NULL ;   //定义配置状态的指针变量  volatile unsigned long *led_dat = NULL ;   //定义蜂鸣器配置IO的地址 #define GPD0CON  0x114000A0volatile unsigned long *bell_config = NULL ; volatile unsigned long *bell_dat = NULL ; //定义按键配置寄存器的地址 #define GPX3CON     0x11000C60volatile unsigned long *button_config = NULL ; volatile unsigned long *button_dat = NULL ; int major ;unsigned char key_val  ;/*  * 确定按键值  */static irqreturn_t buttons_irq(int irq, void *dev_id){//获取按键的键值,因为按键是从该寄存器的第二位开始的,所以需要左移2位,接着与上0xf---1111//这样,如果用户按下按键,就会返回一个键值保存在key_val这个变量里 key_val = (*button_dat >> 2) & 0xf ;    ev_press = 1;                  /* 表示中断发生了 */    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */return IRQ_RETVAL(IRQ_HANDLED);}//open方法 int key_open(struct inode *inode, struct file *filp){printk("key_open\n");//配置4个按键为输入状态,因为按键是从GPXCON[2]开始的,所以要左移8位到对应的位置,将8位以后的16位清0//这样的话就将按键配置的寄存器设置为输入状态了,因为输入是0x0 //*button_config &= ~(0xffff << 8);*button_config  &= ~((0xf<<(2*4)) | (0xf<<(3*4)) | (0xf<<(4*4)) | (0xf<<(5*4)));//先对LED的端口进行清0操作*led_config &= ~((0xf<<(3*4)) | (0xf<<(2*4)) | (0xf<<(1*4)) | (0xf<<(0*4)));//清寄存器 *bell_config &= ~(0xf);//设置io为输出 *bell_config |= (0x1);//将4个IO口16位都设置为Output输出状态*led_config |= ((0x1<<(3*4)) | (0x1<<(2*4)) | (0x1<<(1*4)) | (0x1<<(0*4)));        //申请中断/* request_irq * @irq: 中断号,从两个irqs.h中获取描述中断号的宏来填充该参数,不允许直接填入硬件手册上的中断号        //* @handler:中断处理函数,当中断触发时将自动执行 * @flags:外部中断触发方式         //* @name: 注册中断的名字,可任取,中断注册成功后可从/proc/interrupts中看到         //* @dev: 传递给回调函数handler的参数,可以从handler函数的第二个参数获取         //* @return: 错误码        request_irq(IRQ_EINT(27), buttons_irq, IRQF_TRIGGER_FALLING, "k2", "k2");request_irq(IRQ_EINT(28), buttons_irq, IRQF_TRIGGER_FALLING, "k3", "k3");request_irq(IRQ_EINT(29), buttons_irq, IRQF_TRIGGER_FALLING, "k4", "k4");int ret = request_irq(IRQ_EINT(26), buttons_irq, IRQF_TRIGGER_FALLING, "k1", "k1");if (ret < 0) {printk("request_irq faild\n");return -EINVAL;}return 0;}//read方法 ssize_t key_read(struct file *file , char __user *buf ,size_t size ,loff_t *offset){////如果传进来的size小于4,那么就返回-1 if(size < 4){return -1 ; }/* 如果没有按键动作, 休眠 */wait_event_interruptible(button_waitq, ev_press);//将获取到的值拷贝到用户空间 copy_to_user(buf , &key_val , sizeof(key_val));ev_press = 0;//返回键值 return  1;}//write方法int led_write(struct file *filp , const char __user *buf , size_t count , loff_t *f_pos){int val ; //注意,这里是在内核中进行操作,我们需要使用copy_from_user这个函数将用户态的内容拷贝到内核态copy_from_user(&val , buf , count);//以下就是当val是哪个值的时候,led就执行相应的操作,这里不多说switch(val){case 0 :         //对状态寄存器进行赋值,以下雷同printk(KERN_EMERG"led1_on\n");*led_dat &= ~(1<<0) ;*bell_dat |= 0x1 ;break ;case 1 :printk(KERN_EMERG"led2_on\n");*led_dat &= ~(1<<1) ;//*bell_dat |= 0x1 ;*bell_dat &=~0x1 ;break ;case 2 :printk(KERN_EMERG"led3_on\n");*led_dat &= ~(1<<2) ;*bell_dat |= 0x1 ;break ;case 3 :printk(KERN_EMERG"ledall_off\n");*led_dat |= ((1<<0) | (1<<1) |(1<<2)| (1<<3)) ;*bell_dat &=~0x1 ;break ;case 4 :printk(KERN_EMERG"ledall_on\n");*led_dat |= ((1<<0) | (1<<1) |(1<<2)| (1<<3)) ;*bell_dat &=~0x1 ;break ;case 5 : printk(KERN_EMERG"ledall_off\n");*led_dat |= ((1<<0) | (1<<1) |(1<<2)| (1<<3)) ;*bell_dat &=~0x1 ;break ;}return 0;}//close方法 int key_close(struct inode *inode, struct file *filp){printk("key_close\n");*led_dat |= (1<<0) | (1<<1) | (1<<2) | (1<<3);  //全灭,因为高电平是灭的,0xf ----> 1111//关闭蜂鸣器 *bell_dat &= ~0x1 ;return 0;}struct file_operations fops = {.owner = THIS_MODULE ,.open = key_open,.read = key_read,.write = led_write,.release = key_close,};int test_init(void){printk("key_init\n");//注册设备 major = register_chrdev(0, DEV_NAME, &fops);keydrv_class = class_create(THIS_MODULE, DEV_NAME);keydrv_class_dev = device_create(keydrv_class, NULL, MKDEV(major, 0), NULL, DEV_NAME); /* /dev/xyz */led_config = (volatile unsigned long *)ioremap(GPM4COM , 16);led_dat = led_config + 1 ;//映射IO bell_config = (volatile unsigned long *)ioremap(GPD0CON , 16);//加4个字节偏移到GP0DAT顺便映射该物理地址 bell_dat = bell_config + 1 ;//映射端口 button_config = (volatile unsigned long *)ioremap(GPX3CON , 16);button_dat = button_config + 1 ;return 0;}void test_exit(void){printk("key_exit\n");//注销设备 unregister_chrdev(major, DEV_NAME);device_destroy(keydrv_class,  MKDEV(major, 0));class_destroy(keydrv_class);//取消映射 iounmap(button_config);iounmap(bell_config);iounmap(led_config);free_irq(IRQ_EINT(26), "k1");free_irq(IRQ_EINT(27), "k2");free_irq(IRQ_EINT(28), "k3");free_irq(IRQ_EINT(29), "k4");}module_init(test_init);module_exit(test_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("hai");MODULE_VERSION("2017.4.30");



应用

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#define DEV_NAME"/dev/wait-dev"void delay(void);int main(int argc, char **argv){int fd;unsigned int val = 0 ;int var_tmp=0;char key_buf;fd = open(DEV_NAME,O_RDWR) ;if(-1 == fd){printf("open fair!\n");return -1 ;}while(1){//在这里,我已经提前做了一个程序将按键的值读出来了,那么直接看就行了。 read(fd , &key_buf , 4);val=key_buf;switch(val){case 7 :{var_tmp=0;printf("the first_key press! key_val=%u\n",val);delay();write(fd , &var_tmp , 4);break;}case 11:{var_tmp=1;printf("the second_key press! key_val=%u\n",val);//delay();write(fd , &var_tmp , 4);break ;}case 13:{var_tmp=2;write(fd , &var_tmp , 4);printf("the third_key press! key_val=%u\n",val);//delay();break ;}case 14:{var_tmp=3;write(fd , &var_tmp , 4);        printf("the fourth_key press! key_val=%u\n",val);//delay();break ;}default : {var_tmp=5;write(fd , &var_tmp , 4);printf("no key is press!\n");//delay();}}}return 0;}void delay(void){unsigned int i = 0xffffff ; while(i--);}
 

Makefile

KERN_DIR = /home/tools/linux-3.5all:make -C $(KERN_DIR) M=`pwd` modules clean:make -C $(KERN_DIR) M=`pwd` cleanrm -rf modules.orderobj-m+= wait_driver.o




0 0