当前位置: 代码迷 >> 综合 >> Linux驱动开发——(Linux内核字符设备文件的自动创建)gpio(4)
  详细解决方案

Linux驱动开发——(Linux内核字符设备文件的自动创建)gpio(4)

热度:78   发布时间:2023-11-20 00:19:36.0

文章目录

  • Linux内核字符设备文件的自动创建
    • 自动创建字符设备文件概述
        • 字符设备文件的自动创建只需要“三个保证”+“四个函数”
        • 三个保证
        • 四个函数
    • 修改之前LED驱动代码
    • 运行结果


Linux内核字符设备文件的自动创建

前面已经描述过通过mknod命令可以手动创建字符设备文件,那么如何实现自动创建设备文件呢?

自动创建字符设备文件概述

字符设备文件的自动创建只需要“三个保证”+“四个函数”

三个保证

  1. 保证根文件系统rootfs中必须有mdev可执行程序,例如:执行命令
#确认根文件系统中有mdev,mdev可以帮助自动创建设备文件
which is mdev 
  1. 保证根文件系统rootfs的启动脚本rcS中必须有以下内容:
#将来解析fstab文件
mount -a
#驱动程序解析hotplug来确认谁来帮驱动创建设备文件(需要mdev可执行程序)
echo /sbin/mdev > /proc/sys/kernel/hotplug
  1. 保证根文件系统rootfs的配置文件fstab中必须有以下三句话:
    • /proc作为procfs虚拟文件系统的入口
    • /sys作为sysfs虚拟文件系统的入口
    • /dev作为tmpfs虚拟文件系统的入口
    • 这三种文件系统的内容都是由内核创建并且创建在内存中,掉电丢失。
proc   /proc proc  defaults 0 0
sysfs  /sys  syfs  defaluts 0 0
tmpfs  /dev  tmpfs defulats 0 0

四个函数

驱动程序接下来只需要调用以下四个函数即可完成设备文件的创建:
(编写驱动时记得添加头文件 #include <linux/device.h>)

  1. 定义一个设备类指针:
struct class *cls;
  1. 创建一个设备类对象,cls指向这个对象,并且设定这个对象类名:
cls = class_create(THIS_MODULE, "myclass");
  1. 正式创建设备文件,(暂时还是利用led等示例来说明)设备文件为/dev/myled,其中dev为设备号,myled为设备文件名。
device_create(cls, NULL, dev, NULL, "myled");
  1. 删除设备文件(在卸载驱动的时候自动删除设备文件)
device_destroy(cls, dev);
  1. 删除设备类(卸载驱动时用到)。
class_destroy(cls);

修改之前LED驱动代码

  • led_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h> //copy_from_user声明
#include <linux/device.h> //设备文件的自动创建//声明描述LED硬件信息的数据结构
struct led_resource {
    int gpio; //GPIO编号char *name; //LED名称
};//定义初始化LED的硬件信息对象
static struct led_resource led_info[] = {
    {
    .name = "LED1",.gpio = PAD_GPIO_C+12},{
    .name = "LED2",.gpio = PAD_GPIO_C+7},{
    .name = "LED3",.gpio = PAD_GPIO_C+11},{
    .name = "LED4",.gpio = PAD_GPIO_B+26}
};//调用关系:应用ioctl->软中断->内核sys_ioctl->驱动led_ioctl
//例子:int index=1;ioctl(fd, LED_ON, &index);//开第1个灯
//参数关系:
// fd<---->file 亲戚关系
// cmd=LED_ON或者cmd=LED_OFF
// arg=(unsigned long)&index#define LED_ON 0x100001 //开灯命令
#define LED_OFF 0x100002 //关灯命令
static long led_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
    //1.分配内核缓冲区,保存用法发送过来的灯的编号int kindex;//2.拷贝用户缓冲区数据到内核缓冲区//kindex = *(int *)arg; //相当危险copy_from_user(&kindex, (int *)arg, sizeof(kindex));//3.解析用户发送过来的命令switch(cmd) {
    case LED_ON:gpio_set_value(led_info[kindex-1].gpio, 0);printk("%s:开第%d个灯", __func__, kindex);break;case LED_OFF:gpio_set_value(led_info[kindex-1].gpio, 1);printk("%s:关第%d个灯", __func__, kindex);break;default:printk("无效命令!\n");return -1;}return 0; //执行成功返回0,执行失败返回负值
}//定义初始化LED的硬件操作接口对象
static struct file_operations led_fops = {
    .unlocked_ioctl = led_ioctl, //不仅仅向硬件设备发送控制命令,还能和设备进行读写操作
};//定义设备号对象
static dev_t dev;//定义字符设备对象
static struct cdev led_cdev;//定义设备类指针
static struct class *cls;static int led_init(void)
{
    int i;//1.申请设备号alloc_chrdev_region(&dev, 0, 1, "myled");//2.初始化字符设备对象,本质就是给字符设备添加操作接口cdev_init(&led_cdev, &led_fops);//3.向内核注册字符设备对象并且提供硬件操作接口cdev_add(&led_cdev, dev, 1);//4.申请GPIO资源配置为输出,输出1for(i = 0; i < ARRAY_SIZE(led_info); i++) {
    gpio_request(led_info[i].gpio, led_info[i].name);gpio_direction_output(led_info[i].gpio, 1);}//5.创建设备类对象,类似长树枝//将来会创建:rootfs/sys/class/zhangsancls = class_create(THIS_MODULE, "zhangsan");//6.创建设备文件/dev/myled,类似长苹果//dev:就是提供创建设备文件时所需的设备号//myled:就是提供创建设备文件是所需的设备文件名device_create(cls, NULL, dev, NULL, "myled");return 0;
}static void led_exit(void)
{
    int i;//1.输出1,释放GPIO资源for (i = 0; i < ARRAY_SIZE(led_info); i++) {
    gpio_set_value(led_info[i].gpio, 1);gpio_free(led_info[i].gpio);}//2.释放设备号unregister_chrdev_region(dev, 1);//3.卸载字符设备对象cdev_del(&led_cdev);//4.删除设备文件(摘苹果)和设备类(砍树枝)device_destroy(cls, dev);class_destroy(cls);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
  • led_test.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>#define LED_ON 0x100001 //开灯命令
#define LED_OFF 0x100002 //关灯命令int main(int argc, char *argv[])
{
    int fd;int index; //分配用户缓冲区,保存操作灯的编号if(argc != 3) {
    printf("用法:%s <on|off> <1|2|3|4>\n", argv[0]);return -1;}//打开设备fd = open("/dev/myled", O_RDWR);if (fd < 0) {
    printf("打开设备失败!\n");return -1;}//"1"->1index = strtoul(argv[2], NULL, 0);//应用ioctl->软中断->内核sys_ioctl->驱动led_ioctlif(!strcmp(argv[1], "on"))ioctl(fd, LED_ON, &index);else if(!strcmp(argv[1], "off"))ioctl(fd, LED_OFF, &index);//关闭设备close(fd);return 0;
}
  • Makefile
obj-m += led_drv.o
all:make -C /opt/kernel SUBDIRS=$(PWD) modules
clean:make -C /opt/kernel SUBDIRS=$(PWD) clean

运行结果

加载驱动:

insmod led_drv.ko

在这里插入图片描述