模版使用之前的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);
else
gpio_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;
}
上机测试如下