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

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

文章目录

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

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

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

三个保证

四个函数

修改之前LED驱动代码

运行结果


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

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


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

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

三个保证

保证根文件系统rootfs中必须有mdev可执行程序,例如:执行命令

#确认根文件系统中有mdev,mdev可以帮助自动创建设备文件
which is mdev

保证根文件系统rootfs的启动脚本rcS中必须有以下内容:

#将来解析fstab文件
mount -a
#驱动程序解析hotplug来确认谁来帮驱动创建设备文件(需要mdev可执行程序)
echo /sbin/mdev > /proc/sys/kernel/hotplug

保证根文件系统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资源配置为输出,输出1
    for(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/zhangsan
    cls = 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"->1
    index = strtoul(argv[2], NULL, 0);
    //应用ioctl->软中断->内核sys_ioctl->驱动led_ioctl
    if(!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

20191228210724366.png


相关文章
|
5天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
24 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
1天前
|
人工智能 Linux
Linux查找大文件的方法
Linux查找大文件的方法
|
3天前
|
固态存储 Ubuntu Linux
Linux(29) 多线程快速解压缩|删除|监视大型文件
Linux(29) 多线程快速解压缩|删除|监视大型文件
11 1
|
3天前
|
Ubuntu Linux 数据安全/隐私保护
Linux(24) 如何在Ubuntu中操作rootfs.img文件
Linux(24) 如何在Ubuntu中操作rootfs.img文件
9 0
|
8天前
|
安全 Linux 开发工具
Linux中可引起文件时间戳改变的相关命令
【4月更文挑战第12天】Linux中可引起文件时间戳改变的相关命令
18 0
|
9天前
|
算法 Linux 调度
深入理解Linux内核的进程调度机制
【4月更文挑战第17天】在多任务操作系统中,进程调度是核心功能之一,它决定了处理机资源的分配。本文旨在剖析Linux操作系统内核的进程调度机制,详细讨论其调度策略、调度算法及实现原理,并探讨了其对系统性能的影响。通过分析CFS(完全公平调度器)和实时调度策略,揭示了Linux如何在保证响应速度与公平性之间取得平衡。文章还将评估最新的调度技术趋势,如容器化和云计算环境下的调度优化。
|
10天前
|
Linux Shell 开发工具
Linux文件常用操作
Linux文件常用操作(几乎覆盖所有日常使用)
84 0
|
11天前
|
Linux 内存技术 Perl
【ZYNQ】制作从 QSPI Flash 启动 Linux 的启动文件
【ZYNQ】制作从 QSPI Flash 启动 Linux 的启动文件
|
15天前
|
算法 Linux 调度
深度解析:Linux内核的进程调度机制
【4月更文挑战第12天】 在多任务操作系统如Linux中,进程调度机制是系统的核心组成部分之一,它决定了处理器资源如何分配给多个竞争的进程。本文深入探讨了Linux内核中的进程调度策略和相关算法,包括其设计哲学、实现原理及对系统性能的影响。通过分析进程调度器的工作原理,我们能够理解操作系统如何平衡效率、公平性和响应性,进而优化系统表现和用户体验。
20 3