目录
一、前言
下面演示下linux下GPIO输入驱动的编写。
在驱动中使用一个整形变量来表示按键值,应用程序通过read函数来读取按键值,因为驱动要对这个整形变量写入按键值,应用程序要读取按键值,因此该整形变量就是共享资源,需要进行保护,对于整形变量,我们首选就是原子操作,通过原子操作对该变量进行赋值与读取。
二、程序编写
2.1、修改设备树
2.1.1、添加pinctrl节点
在 iomuxc 节点的 imx6ul-evk 子节点下添加一个pinctrl_key的子节点:
2.1.2、添加key节点
在根节点“/”下创建key节点,节点名字为gpiokey。
make dtbs重新编译设备树,然后启动linux系统,在/proc/decive-tree目录下查看“gpiokey”节点是否存在。
2.2、驱动编写
添加一个原子变量保存按键值。
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#define GPIOKEY_CNT 1
#define GPIOKEY_NAME "gpiokey"
#define KEY_ON 0xF0
#define KEY_OFF 0x00
//设备结构体
struct gpiokey_dev{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
struct device_node *nd; //设备节点
int key_gpio; //key所使用的GPIO编号
atomic_t value; //按键值
};
struct gpiokey_dev gpiokey;
static int key_init(void)
{
int ret = 0;
//通过节点名字查找节点
gpiokey.nd = of_find_node_by_name(NULL, "gpiokey");
if (gpiokey.nd == NULL)
{
printk("find %s fail\r\n", "gpiokey");
goto fail_nd;
}
//获取GPIO编号
gpiokey.key_gpio = of_get_named_gpio(gpiokey.nd, "key-gpio", 0);
if (gpiokey.key_gpio < 0)
{
printk("get gpio fail\r\n");
goto fail_nd;
}
ret = gpio_request(gpiokey.key_gpio, GPIOKEY_NAME);
if (ret < 0)
{
printk("fail to request the key gpio\r\n");
goto fail_nd;
}
//设置key状态
ret = gpio_direction_input(gpiokey.key_gpio);
if (ret < 0)
{
printk("set key status fail\r\n");
goto fail_setinput;
}
fail_setinput:
gpio_free(gpiokey.key_gpio);
fail_nd:
return ret;
return 0;
}
static int key_open(struct inode *inode, struct file *filp)
{
int ret = 0;
filp->private_data = &gpiokey;
ret = key_init();
if (ret < 0)
return ret;
return 0;
}
static ssize_t key_read(struct file *filp, __user char *buf, size_t count, loff_t *ppos)
{
int ret = 0;
int value;
struct gpiokey_dev *dev = filp->private_data;
if (gpio_get_value(dev->key_gpio) == 0) //按下
{
while(!gpio_get_value(dev->key_gpio)); //等待松开
atomic_set(&dev->value, KEY_ON);
}
else {
//没按下
atomic_set(&dev->value, KEY_OFF);
}
value = atomic_read(&dev->value);
ret = copy_to_user(buf, &value, sizeof(value));
return ret;
}
static ssize_t key_write(struct file *filp, const char __user *buf, size_t count, loff_t *oppos)
{
printk("key write\n");
return 0;
}
static int key_release(struct inode *inode, struct file *filp)
{
printk("key release\n");
return 0;
}
static const struct file_operations key_fops = {
.owner = THIS_MODULE,
.open = key_open,
.read = key_read,
.write = key_write,
.release = key_release,
};
static int __init mykey_init(void)
{
int ret = 0;
//按键值初始化
atomic_set(&gpiokey.value, KEY_OFF);
//分配设备号
if ( gpiokey.major) {
gpiokey.devid = MKDEV( gpiokey.major, 0);
register_chrdev_region( gpiokey.devid, GPIOKEY_CNT, GPIOKEY_NAME);
} else {
alloc_chrdev_region(&gpiokey.devid, 0, GPIOKEY_CNT, GPIOKEY_NAME);
gpiokey.major = MAJOR(gpiokey.devid);
gpiokey.minor = MINOR(gpiokey.devid);
}
printk(" gpiokey major=%d, minor= %d\r\n", gpiokey.major, gpiokey.minor);
//初始化cdev
gpiokey.cdev.owner = THIS_MODULE;
cdev_init(&gpiokey.cdev, &key_fops);
//添加cdev
ret = cdev_add(&gpiokey.cdev, gpiokey.devid, GPIOKEY_CNT);
if (ret < 0)
{
printk("cdev add fail\r\n");
goto fail_cdev;
}
//创建类
gpiokey.class = class_create(THIS_MODULE, GPIOKEY_NAME);
if (IS_ERR(gpiokey.class))
{
printk("creat class fail\r\n");
ret = PTR_ERR(gpiokey.class);
goto fail_class;
}
//创建设备
gpiokey.device = device_create(gpiokey.class, NULL, gpiokey.devid, NULL, GPIOKEY_NAME);
if (IS_ERR(gpiokey.device))
{
printk("creat device fail\r\n");
ret = PTR_ERR(gpiokey.device);
goto fail_device;
}
return 0;
fail_device:
class_destroy(gpiokey.class);
fail_class:
cdev_del(&gpiokey.cdev);
fail_cdev:
unregister_chrdev_region(gpiokey.devid, GPIOKEY_CNT);
return ret;
}
static void __exit mykey_exit(void)
{
//删除设备的类
device_destroy(gpiokey.class, gpiokey.devid);
//删除类
class_destroy(gpiokey.class);
//删除设备
cdev_del(&gpiokey.cdev);
//注销设备号
unregister_chrdev_region(gpiokey.devid, GPIOKEY_CNT);
printk(" dev exit\n");
}
module_init( mykey_init);
module_exit( mykey_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZK");
2.3、应用程序编写
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define KEY0VALUE 0xF0
#define INVAKEY 0x00
/*
@argc:应用程序参数个数
@argv[]:具体的参数内容,字符串形式
@ ./keyAPP /dev/key
*/
int main(int argc, char *argv[])
{
int ret = 0;
int fd = 0;
int value = 0;
char *filename;
if (argc != 2){
printf("Error usage!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if (fd < 0){
printf("can't open file\r\n", filename);
return -1;
}
while(1) {
read(fd, &value, sizeof(value));
if (value == KEY0VALUE) {
printf("KEY0 Press, value = %d\r\n", value);
}
}
/*close*/
ret = close(fd);
if (ret < 0){
printf("close file %s failed\r\n", filename);
}
return 0;
}
三、测试