参考蚂蚁S9矿板引脚定义.csdn设备树system-user.dtsi#include dt-bindings/gpio/gpio.h#include dt-bindings/input/input.h#include dt-bindings/media/xilinx-vip.h#include dt-bindings/phy/phy.h#include dt-bindings/interrupt-controller/irq.h/{modelz7 Board ant 789;compatiblexlnx,zynq-zc702,xlnx,zynq-7000;chosen{bootargsconsolettyPS0,115200 earlyconcdns,mmio,0xe0000000,115200n8 keep_bootcon earlyprintk root/dev/mmcblk0p2 rw rootwait;stdout-pathserial0:115200n8;};led{compatibleming,led;statusokay;default-stateon;led-gpiogpio037GPIO_ACTIVE_HIGH;};beeper{compatibleming,beeper;statusokay;default-stateoff;beeper-gpiogpio038GPIO_ACTIVE_HIGH;};key{compatibleming,key;statusokay;key-gpiogpio047GPIO_ACTIVE_LOW;interrupt-parentgpio0;interrupts47IRQ_TYPE_EDGE_BOTH;};};驱动 noblockio.c#includelinux/types.h#includelinux/kernel.h#includelinux/delay.h#includelinux/ide.h#includelinux/init.h#includelinux/module.h#includelinux/errno.h#includelinux/gpio.h#includeasm/mach/map.h#includeasm/uaccess.h#includeasm/io.h#includelinux/cdev.h#includelinux/of.h#includelinux/of_address.h#includelinux/of_gpio.h#includelinux/of_irq.h#includelinux/irq.h#includelinux/poll.h#defineKEY_CNT1/* 设备号个数 */#defineKEY_NAMEkey/* 名字 *//* 定义按键状态 */enumkey_status{KEY_PRESS0,// 按键按下KEY_RELEASE,// 按键松开KEY_KEEP,// 按键状态保持};/* 按键设备结构体 */structkey_dev{dev_tdevid;/* 设备号 */structcdevcdev;/* cdev结构体 */structclass*class;/* 类 */structdevice*device;/* 设备 */intkey_gpio;/* GPIO编号 */intirq_num;/* 中断号 */structtimer_listtimer;/* 定时器 */wait_queue_head_tr_wait;/* 读等待队列头 */};staticstructkey_devkey;/* 按键设备 */staticatomic_tstatus;/* 按键状态 *//* * description : 打开设备 * param – inode : 传递给驱动的inode * param – filp : 设备文件file结构体有个叫做private_data的成员变量 * 一般在open的时候将private_data指向设备结构体。 * return : 0 成功;其他 失败 */staticintkey_open(structinode*inode,structfile*filp){return0;}/* * description : 从设备读取数据 * param – filp : 要打开的设备文件(文件描述符) * param – buf : 返回给用户空间的数据缓冲区 * param – cnt : 要读取的数据长度 * param – offt : 相对于文件首地址的偏移 * return : 读取的字节数如果为负值表示读取失败 */staticssize_tkey_read(structfile*filp,char__user*buf,size_tcnt,loff_t*offt){intret;if(filp-f_flagsO_NONBLOCK){//非阻塞方式访问if(KEY_KEEPatomic_read(status))return-EAGAIN;}else{//阻塞式访问/* 加入等待队列当有按键按下或者松开动作发生时才会被唤醒 */retwait_event_interruptible(key.r_wait,KEY_KEEP!atomic_read(status));if(ret)returnret;}/* 将按键状态信息发送给应用程序 */retcopy_to_user(buf,status,sizeof(int));/* 状态重置 */atomic_set(status,KEY_KEEP);returnret;}/* * description : poll函数用于处理非阻塞访问 * param – filp : 要打开的设备文件(文件描述符) * param – wait : 等待列表poll_table * return : 设备或者资源状态 */staticunsignedintkey_poll(structfile*filp,structpoll_table_struct*wait){unsignedintmask0;poll_wait(filp,key.r_wait,wait);if(KEY_KEEP!atomic_read(status))//按键按下或松开动作发生maskPOLLIN|POLLRDNORM;//返回POLLINreturnmask;}/* * description : 向设备写数据 * param – filp : 设备文件表示打开的文件描述符 * param – buf : 要写给设备写入的数据 * param – cnt : 要写入的数据长度 * param – offt : 相对于文件首地址的偏移 * return : 写入的字节数如果为负值表示写入失败 */staticssize_tkey_write(structfile*filp,constchar__user*buf,size_tcnt,loff_t*offt){return0;}/* * description : 关闭/释放设备 * param – filp : 要关闭的设备文件(文件描述符) * return : 0 成功;其他 失败 */staticintkey_release(structinode*inode,structfile*filp){return0;}staticvoidkey_timer_function(structtimer_list*unused){staticintlast_val1;intcurrent_val;/* 读取按键值并判断按键当前状态 */current_valgpio_get_value(key.key_gpio);if(0current_vallast_val){// 按下atomic_set(status,KEY_PRESS);wake_up_interruptible(key.r_wait);//唤醒r_wait队列头中的所有队列}elseif(1current_val!last_val){atomic_set(status,KEY_RELEASE);// 松开wake_up_interruptible(key.r_wait);//唤醒r_wait队列头中的所有队列}elseatomic_set(status,KEY_KEEP);// 状态保持last_valcurrent_val;}staticirqreturn_tkey_interrupt(intirq,void*dev_id){/* 按键防抖处理开启定时器延时15ms */mod_timer(key.timer,jiffiesmsecs_to_jiffies(15));returnIRQ_HANDLED;}staticintkey_parse_dt(void){structdevice_node*nd;constchar*str;intret;/* 获取key节点 */ndof_find_node_by_path(/key);if(NULLnd){printk(KERN_ERRkey: Failed to get key node\n);return-EINVAL;}/* 读取status属性 */retof_property_read_string(nd,status,str);if(!ret){if(strcmp(str,okay))return-EINVAL;}/* 获取compatible属性值并进行匹配 */retof_property_read_string(nd,compatible,str);if(ret)returnret;if(strcmp(str,ming,key)){printk(KERN_ERRkey: Compatible match failed\n);return-EINVAL;}/* 获取设备树中的key-gpio属性得到按键的GPIO编号 */key.key_gpioof_get_named_gpio(nd,key-gpio,0);if(!gpio_is_valid(key.key_gpio)){printk(KERN_ERRkey: Failed to get key-gpio\n);return-EINVAL;}/* 获取GPIO对应的中断号 */key.irq_numirq_of_parse_and_map(nd,0);if(!key.irq_num)return-EINVAL;return0;}staticintkey_gpio_init(void){unsignedlongirq_flags;intret;/* 申请使用GPIO */retgpio_request(key.key_gpio,Key Gpio);if(ret)returnret;/* 将GPIO设置为输入模式 */gpio_direction_input(key.key_gpio);/* 获取设备树中指定的中断触发类型 */irq_flagsirq_get_trigger_type(key.irq_num);if(IRQF_TRIGGER_NONEirq_flags)irq_flagsIRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING;/* 申请中断 */retrequest_irq(key.irq_num,key_interrupt,irq_flags,PS Key0 IRQ,NULL);if(ret){gpio_free(key.key_gpio);returnret;}return0;}/* 设备操作函数 */staticstructfile_operationskey_fops{.ownerTHIS_MODULE,.openkey_open,.readkey_read,.writekey_write,.releasekey_release,.pollkey_poll,};staticint__initmykey_init(void){intret;/* 初始化等待队列头 */init_waitqueue_head(key.r_wait);/* 初始化按键状态 */atomic_set(status,KEY_KEEP);/* 设备树解析 */retkey_parse_dt();if(ret)returnret;/* GPIO、中断初始化 */retkey_gpio_init();if(ret)returnret;/* 初始化cdev */key.cdev.ownerTHIS_MODULE;cdev_init(key.cdev,key_fops);/* 添加cdev */retalloc_chrdev_region(key.devid,0,KEY_CNT,KEY_NAME);if(ret)gotoout1;retcdev_add(key.cdev,key.devid,KEY_CNT);if(ret)gotoout2;/* 创建类 */key.classclass_create(THIS_MODULE,KEY_NAME);if(IS_ERR(key.class)){retPTR_ERR(key.class);gotoout3;}/* 创建设备 */key.devicedevice_create(key.class,NULL,key.devid,NULL,KEY_NAME);if(IS_ERR(key.device)){retPTR_ERR(key.device);gotoout4;}/* 初始化定时器 */timer_setup(key.timer,key_timer_function,0);return0;out4:class_destroy(key.class);out3:cdev_del(key.cdev);out2:unregister_chrdev_region(key.devid,KEY_CNT);out1:free_irq(key.irq_num,NULL);gpio_free(key.key_gpio);returnret;}staticvoid__exitmykey_exit(void){/* 删除定时器 */del_timer_sync(key.timer);/* 注销设备 */device_destroy(key.class,key.devid);/* 注销类 */class_destroy(key.class);/* 删除cdev */cdev_del(key.cdev);/* 注销设备号 */unregister_chrdev_region(key.devid,KEY_CNT);/* 释放中断 */free_irq(key.irq_num,NULL);/* 释放GPIO */gpio_free(key.key_gpio);}/* 驱动模块入口和出口函数注册 */module_init(mykey_init);module_exit(mykey_exit);MODULE_AUTHOR(DengTao 773904075qq.com);MODULE_DESCRIPTION(Gpio Key Interrupt Driver);MODULE_LICENSE(GPL);keyApp.c#includestdio.h#includeunistd.h#includesys/types.h#includesys/stat.h#includefcntl.h#includestdlib.h#includestring.h#includepoll.h/* * description : main主程序 * param – argc : argv数组元素个数 * param – argv : 具体参数 * return : 0 成功;其他 失败 */intmain(intargc,char*argv[]){fd_set readfds;intfd,ret;intkey_val;/* 判断传参个数是否正确 */if(2!argc){printf(Usage:\n\t./keyApp /dev/key\n);return-1;}/* 打开设备 */fdopen(argv[1],O_RDONLY|O_NONBLOCK);if(0fd){printf(ERROR: %s file open failed!\n,argv[1]);return-1;}FD_ZERO(readfds);FD_SET(fd,readfds);/* 循环轮询读取按键数据 */for(;;){retselect(fd1,readfds,NULL,NULL,NULL);switch(ret){case0://超时/*用户自定义超时处理*/break;case-1://错误/*用户自定义错误处理*/break;default:if(FD_ISSET(fd,readfds)){read(fd,key_val,sizeof(int));if(key_val0)printf(Key Press\n);elseif(key_val1)printf(Key Release\n);}break;}}/* 关闭设备 */close(fd);return0;}编译驱动和app$CC-okeyApp keyApp.c测试rootant:~# insmod noblockio.koinsmod: can not insert noblockio.ko: Device or resource busy rootant:~# rmmod blockio.kormmod: cant unload module blockio: Resource temporarily unavailable rootant:~# kill -9 495rootant:~# rmmod blockiorootant:~# insmod noblockio.korootant:~# chmod 777 keyApprootant:~# ./keyApp /dev/keyKey Press Key Release rootant:~# rmmod noblockiorootant:~# ls /dev/keyls: /dev/key: No suchfileor directory