模版使用之前的hello驱动程序。
想要操作led,首先要找到原理图,查找GPIO对应的GPIO引脚
从图中能看出来LED2对应的GPIO是GPIO5_3,同时可以得知这个LED2是低电平点亮。查看cat /sys/kernel/debug/gpio可得知GPIO5_3(第四组GPIO的第三个引脚)的引脚编号是131。
确定好GPIO编号后,接下来就是要确定led驱动程序的功能,这里我希望它能开关led,以及能读取led的状态。所以可以去掉file_operations结构体中的open和release,只保留read和write。
接下来就是GPIO操作,套路如下
1.请求 GPIO: 使用gpio_request请求所需的 GPIO 引脚。
2.配置 GPIO: 请求成功后,可以配置 GPIO 的方向(输入或输出)和初始状态。
gpio_direction_input输入,gpio_direction_output输出。
3.使用 GPIO: 根据需要读写 GPIO 引脚的值。gpio_get_value读,gpio_set_value写。
4.释放 GPIO: 完成使用后,调用gpio_free函数释放该 GPIO 引脚。
套用到我们的驱动程序中就是,在init函数中请求GPIO并设置方向为输出,在exit函数中释放GPIO,在read函数中获取GPIO的值,在write中设置GPIO的值。代码如下:
#include "asm-generic/gpio.h"
#include "asm/gpio.h"
#include "asm/uaccess.h"
#include "linux/printk.h"
#include "linux/scatterlist.h"
#include "linux/types.h"
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/backing-dev.h>
#include <linux/shmem_fs.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/uio.h>
#include <linux/module.h>#include <linux/uaccess.h>#define DEVICE_NAME "led_device"
#define LED2_NUM 131
#define LED2_ON 0
#define LED2_OFF 1
static struct class *led_class;
static struct device *led_device;
static int major;//ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
//参数含义依次为 要读取的文件指针 用户保存数据的buf 读取数据大小 文件内容的偏移量
static ssize_t led_read (struct file * filp, char __user *buf, size_t size, loff_t *offset)
{ unsigned long int ret;char value[1];value[0] = gpio_get_value(LED2_NUM) == LED2_ON ? 1 : 0;ret = copy_to_user(buf, value, 1);return 1;
}//ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
//参数含义依次为 要写入的文件指针 用户要写入的数据buf 写入数据大小 文件内容的偏移量
static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{unsigned long int ret;char value[1];ret = copy_from_user(value, buf, 1);if(1 == value[0])gpio_set_value(LED2_NUM, LED2_ON);elsegpio_set_value(LED2_NUM, LED2_OFF);return 1;
}/*构建file_operations结构体*/
static const struct file_operations led_fops = {.owner = THIS_MODULE,.read = led_read,.write = led_write
};/*init函数,实现register_chrdev*/
static int __init led_init(void)
{int ret;ret = gpio_request(LED2_NUM, "LED2");if(ret < 0){printk("Failed to request GPIO:%d, ERRNO:%d\n", LED2_NUM, ret);return ret;}gpio_direction_output(LED2_NUM, LED2_OFF);//数含义依次为 主设备号,如果为0,内核会自动分配一个可用的。设备名,会在/proc/devices中显示。 file_operations结构体//注册成功就返回主设备号major = register_chrdev(0, DEVICE_NAME, &led_fops);//为这个模块创建一个名为led的类 在/sys/class/下会生成led目录led_class = class_create(THIS_MODULE, "led");if (IS_ERR(led_class)) {pr_err("failed to allocate class\n");return PTR_ERR(led_class);}//在/sys/class/led类下创建一个led设备,同时在/dev/下创建一个led节点led_device = device_create(led_class, NULL, MKDEV(major, 0), NULL, "led");if (IS_ERR(led_device)) {pr_err("failed to allocate device\n");return PTR_ERR(led_device);}printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}/*exit函数unregister_chrdev*/
static void __exit led_exit(void)
{gpio_free(LED2_NUM);//先销毁设备,后销毁类device_destroy(led_class, MKDEV(major, 0));class_destroy(led_class);unregister_chrdev(major, DEVICE_NAME);printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
}module_init(led_init);
module_exit(led_exit);
//遵循GPL协议
MODULE_LICENSE("GPL");
配套的应用程序写入1代表开灯,写入0代表关灯,没有参数代表读取led状态。
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>static int fd;/** ./led_test on * ./led_test off* ./led_test*/
int main(int argc, char **argv)
{int ret;char buf[1];int i;/* 2. 打开文件 */fd = open("/dev/led", O_RDWR);if (fd == -1){printf("can not open file /dev/led\n");return -1;}if (argc == 2){/* write */buf[0] = strcmp(argv[1], "on") == 0 ? 1 : 0;ret = write(fd, buf, 1);}else{ret = read(fd, buf, 1);printf("led status is %s\n", buf[0] == 1 ? "on" : "off");}close(fd);return 0;
}
上机测试如下