参考
蚂蚁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>/{model="z7 Board ant 789";compatible="xlnx,zynq-zc702","xlnx,zynq-7000";chosen{bootargs="console=ttyPS0,115200 earlycon=cdns,mmio,0xe0000000,115200n8 keep_bootcon earlyprintk root=/dev/mmcblk0p2 rw rootwait";stdout-path="serial0:115200n8";};led{compatible="ming,led";status="okay";default-state="on";led-gpio=<&gpio037GPIO_ACTIVE_HIGH>;};beeper{compatible="ming,beeper";status="okay";default-state="off";beeper-gpio=<&gpio038GPIO_ACTIVE_HIGH>;};key{compatible="ming,key";status="okay";key-gpio=<&gpio047GPIO_ACTIVE_LOW>;interrupt-parent=<&gpio0>;interrupts=<47IRQ_TYPE_EDGE_BOTH>;};};驱动 noblockio.c
#include<linux/types.h>#include<linux/kernel.h>#include<linux/delay.h>#include<linux/ide.h>#include<linux/init.h>#include<linux/module.h>#include<linux/errno.h>#include<linux/gpio.h>#include<asm/mach/map.h>#include<asm/uaccess.h>#include<asm/io.h>#include<linux/cdev.h>#include<linux/of.h>#include<linux/of_address.h>#include<linux/of_gpio.h>#include<linux/of_irq.h>#include<linux/irq.h>#include<linux/poll.h>#defineKEY_CNT1/* 设备号个数 */#defineKEY_NAME"key"/* 名字 *//* 定义按键状态 */enumkey_status{KEY_PRESS=0,// 按键按下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_flags&O_NONBLOCK){//非阻塞方式访问if(KEY_KEEP==atomic_read(&status))return-EAGAIN;}else{//阻塞式访问/* 加入等待队列,当有按键按下或者松开动作发生时,才会被唤醒 */ret=wait_event_interruptible(key.r_wait,KEY_KEEP!=atomic_read(&status));if(ret)returnret;}/* 将按键状态信息发送给应用程序 */ret=copy_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){unsignedintmask=0;poll_wait(filp,&key.r_wait,wait);if(KEY_KEEP!=atomic_read(&status))//按键按下或松开动作发生mask=POLLIN|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_val=1;intcurrent_val;/* 读取按键值并判断按键当前状态 */current_val=gpio_get_value(key.key_gpio);if(0==current_val&&last_val){// 按下atomic_set(&status,KEY_PRESS);wake_up_interruptible(&key.r_wait);//唤醒r_wait队列头中的所有队列}elseif(1==current_val&&!last_val){atomic_set(&status,KEY_RELEASE);// 松开wake_up_interruptible(&key.r_wait);//唤醒r_wait队列头中的所有队列}elseatomic_set(&status,KEY_KEEP);// 状态保持last_val=current_val;}staticirqreturn_tkey_interrupt(intirq,void*dev_id){/* 按键防抖处理,开启定时器延时15ms */mod_timer(&key.timer,jiffies+msecs_to_jiffies(15));returnIRQ_HANDLED;}staticintkey_parse_dt(void){structdevice_node*nd;constchar*str;intret;/* 获取key节点 */nd=of_find_node_by_path("/key");if(NULL==nd){printk(KERN_ERR"key: Failed to get key node\n");return-EINVAL;}/* 读取status属性 */ret=of_property_read_string(nd,"status",&str);if(!ret){if(strcmp(str,"okay"))return-EINVAL;}/* 获取compatible属性值并进行匹配 */ret=of_property_read_string(nd,"compatible",&str);if(ret)returnret;if(strcmp(str,"ming,key")){printk(KERN_ERR"key: Compatible match failed\n");return-EINVAL;}/* 获取设备树中的key-gpio属性,得到按键的GPIO编号 */key.key_gpio=of_get_named_gpio(nd,"key-gpio",0);if(!gpio_is_valid(key.key_gpio)){printk(KERN_ERR"key: Failed to get key-gpio\n");return-EINVAL;}/* 获取GPIO对应的中断号 */key.irq_num=irq_of_parse_and_map(nd,0);if(!key.irq_num)return-EINVAL;return0;}staticintkey_gpio_init(void){unsignedlongirq_flags;intret;/* 申请使用GPIO */ret=gpio_request(key.key_gpio,"Key Gpio");if(ret)returnret;/* 将GPIO设置为输入模式 */gpio_direction_input(key.key_gpio);/* 获取设备树中指定的中断触发类型 */irq_flags=irq_get_trigger_type(key.irq_num);if(IRQF_TRIGGER_NONE==irq_flags)irq_flags=IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING;/* 申请中断 */ret=request_irq(key.irq_num,key_interrupt,irq_flags,"PS Key0 IRQ",NULL);if(ret){gpio_free(key.key_gpio);returnret;}return0;}/* 设备操作函数 */staticstructfile_operationskey_fops={.owner=THIS_MODULE,.open=key_open,.read=key_read,.write=key_write,.release=key_release,.poll=key_poll,};staticint__initmykey_init(void){intret;/* 初始化等待队列头 */init_waitqueue_head(&key.r_wait);/* 初始化按键状态 */atomic_set(&status,KEY_KEEP);/* 设备树解析 */ret=key_parse_dt();if(ret)returnret;/* GPIO、中断初始化 */ret=key_gpio_init();if(ret)returnret;/* 初始化cdev */key.cdev.owner=THIS_MODULE;cdev_init(&key.cdev,&key_fops);/* 添加cdev */ret=alloc_chrdev_region(&key.devid,0,KEY_CNT,KEY_NAME);if(ret)gotoout1;ret=cdev_add(&key.cdev,key.devid,KEY_CNT);if(ret)gotoout2;/* 创建类 */key.class=class_create(THIS_MODULE,KEY_NAME);if(IS_ERR(key.class)){ret=PTR_ERR(key.class);gotoout3;}/* 创建设备 */key.device=device_create(key.class,NULL,key.devid,NULL,KEY_NAME);if(IS_ERR(key.device)){ret=PTR_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 <773904075@qq.com>");MODULE_DESCRIPTION("Gpio Key Interrupt Driver");MODULE_LICENSE("GPL");keyApp.c
#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<stdlib.h>#include<string.h>#include<poll.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;}/* 打开设备 */fd=open(argv[1],O_RDONLY|O_NONBLOCK);if(0>fd){printf("ERROR: %s file open failed!\n",argv[1]);return-1;}FD_ZERO(&readfds);FD_SET(fd,&readfds);/* 循环轮询读取按键数据 */for(;;){ret=select(fd+1,&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_val==0)printf("Key Press\n");elseif(key_val==1)printf("Key Release\n");}break;}}/* 关闭设备 */close(fd);return0;}编译驱动和app
$CC-okeyApp keyApp.c测试
root@ant:~# insmod noblockio.koinsmod: can not insert noblockio.ko: Device or resource busy root@ant:~# rmmod blockio.kormmod: can't unload module 'blockio': Resource temporarily unavailable root@ant:~# kill -9 495root@ant:~# rmmod blockioroot@ant:~# insmod noblockio.koroot@ant:~# chmod 777 keyApproot@ant:~# ./keyApp /dev/keyKey Press Key Release root@ant:~# rmmod noblockioroot@ant:~# ls /dev/keyls: /dev/key: No suchfileor directory