五、Linux字符设备驱动

一、应用层访问底层驱动

1.介绍

字符设备或块设备,我们可以通过**设备文件(属性信息中包含的设备号)**来访问底层设备。

第一集

cpp 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
MODULE_LICENSE("GPL v2");

static int led_device_open(struct inode *inode,struct file *file)
{
    printk("led device open\n");
    return 0;
}
static int led_device_release(struct inode *inode,struct file *file)
{
    printk("led device release\n");
    return 0;
}

struct file_operations led_device_ops = {
    .owner = THIS_MODULE,
    .open = led_device_open,
    .release = led_device_release,
};

static int led_driver_init(void)
{
    return 0;
}

static void led_driver_exit(void)
{

}
module_init(led_driver_init);
module_exit(led_driver_exit);
bash 复制代码
ifeq ($(KERNELRELEASE),)
X86_KERNEL_BUILD = /lib/modules/$(shell uname -r)/build
ARM_KERNEL_BUILD = /home/itcast/mly/kernel/linux-imx-rel_ime_4.1.15_2.1.0.ga
MODULE_PATH = $(shell pwd)
x86_module:
	make -C $(X86_KERNEL_BUILD) M=$(MODULE_PATH) modules
arm_modules:
	make -C $(ARM_KERNEL_BUILD) M=$(MODULE_PATH) modules ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
clean:
	make -C $(X86_KERNEL_BUILD) M=$(MODULE_PATH) clean
else
	obj-m = led-driver.o
Endif

第二集

cpp 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
MODULE_LICENSE("GPL v2");

struct led_device
{
    struct cdev led_cdev;
};
static int led_device_open(struct inode *inode,struct file *file)
{
    printk("led device open\n");
    return 0;
}
static int led_device_release(struct inode *inode,struct file *file)
{
    printk("led device release\n");
    return 0;
}

struct file_operations led_device_ops = {
    .owner = THIS_MODULE,
    .open = led_device_open,
    .release = led_device_release,
};

static int led_driver_init(void)
{
    struct led_device *pled;
    pled = kmalloc(sizeof(*pled),GFP_KERNEL);
    if(pled == NULL)
    {
        printk("Fail to kmalloc\n");
        return -ENOMEM;
    }
    return 0;
}

static void led_driver_exit(void)
{

}
module_init(led_driver_init);
module_exit(led_driver_exit);

第三集

cpp 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
MODULE_LICENSE("GPL v2");

struct led_device
{
    struct cdev led_cdev;
};
static int led_device_open(struct inode *inode,struct file *file)
{
    printk("led device open\n");
    return 0;
}
static int led_device_release(struct inode *inode,struct file *file)
{
    printk("led device release\n");
    return 0;
}

struct file_operations led_device_ops = {
    .owner = THIS_MODULE,
    .open = led_device_open,
    .release = led_device_release,
};

static int led_driver_init(void)
{
    struct led_device *pled;
    pled = kmalloc(sizeof(*pled),GFP_KERNEL);
    if(pled == NULL)
    {
        printk("Fail to kmalloc\n");
        return -ENOMEM;
    }
    //pled->led_cdev.ops = &led_device_ops;
    cdev_init(&pled->led_cdev,&led_device_ops);
    return 0;
}

static void led_driver_exit(void)
{

}
module_init(led_driver_init);
module_exit(led_driver_exit);

2.设备号及设备文件创建

12bit(主设备号) + 20bit(次设备号) = 32bit

主设备号 :标识一类设备

次设备号 :为了区分同类型设备的不同设备

mknod 设备文件名 设备文件类型 主设备号 次设备号

Mknod /dev/led c 250 0

查看系统中的设备号

第四集设备号注册与注销

cpp 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>

#define LED_DEVICE_MAJOR 240
#define LED_DEVICE_MINOR 0
MODULE_LICENSE("GPL v2");

struct led_device
{
    dev_t devid;
    struct cdev led_cdev;
};
struct led_device *pled;
static int led_device_open(struct inode *inode,struct file *file)
{
    printk("led device open\n");
    return 0;
}
static int led_device_release(struct inode *inode,struct file *file)
{
    printk("led device release\n");
    return 0;
}

struct file_operations led_device_ops = {
    .owner = THIS_MODULE,
    .open = led_device_open,
    .release = led_device_release,
};

static int led_driver_init(void)
{
    int err;
    
    pled = kmalloc(sizeof(*pled),GFP_KERNEL);
    if(pled == NULL)
    {
        printk("Fail to kmalloc\n");
        return -ENOMEM;
    }
    //pled->led_cdev.ops = &led_device_ops;
    cdev_init(&pled->led_cdev,&led_device_ops);
    pled->devid = MKDEV(LED_DEVICE_MAJOR,LED_DEVICE_MINOR);
    err = register_chrdev_region(pled->devid,1,"led-device");
    if(err)
    {
        printk("Fail to register_chrdev_region\n");
        kfree(pled);
    }
    return 0;
}

static void led_driver_exit(void)
{
    unregister_chrdev_region(pled->devid,1);
    kfree(pled);
}
module_init(led_driver_init);
module_exit(led_driver_exit);

第五集根据设备号添加字符设备,设备号和设备驱动绑定

cpp 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>

#define LED_DEVICE_MAJOR 240
#define LED_DEVICE_MINOR 0
MODULE_LICENSE("GPL v2");

struct led_device
{
    dev_t devid;
    struct cdev led_cdev;
};
struct led_device *pled;
static int led_device_open(struct inode *inode,struct file *file)
{
    printk("led device open\n");
    return 0;
}
static int led_device_release(struct inode *inode,struct file *file)
{
    printk("led device release\n");
    return 0;
}

struct file_operations led_device_ops = {
    .owner = THIS_MODULE,
    .open = led_device_open,
    .release = led_device_release,
};

static int led_driver_init(void)
{
    int err;
    
    pled = kmalloc(sizeof(*pled),GFP_KERNEL);
    if(pled == NULL)
    {
        printk("Fail to kmalloc\n");
        return -ENOMEM;
    }
    //pled->led_cdev.ops = &led_device_ops;
cdev_init(&pled->led_cdev,&led_device_ops);
//注册设备号
    pled->devid = MKDEV(LED_DEVICE_MAJOR,LED_DEVICE_MINOR);
    err = register_chrdev_region(pled->devid,1,"led-device");
    if(err)
    {
        printk("Fail to register_chrdev_region\n");
        kfree(pled);
}
//根据设备号添加字符设备(让设备号和字符设备进行绑定)
    err = cdev_add(&pled->led_cdev,pled->devid,1);
    if(err)
    {
        printk("Fail to cdev_add\n");
        unregister_chrdev_region(pled->devid,1);
        kfree(pled);
        return err;
    }
    return 0;
}

static void led_driver_exit(void)
{
    cdev_del(&pled->led_cdev);
    unregister_chrdev_region(pled->devid,1);
    kfree(pled);
    return ;
}
module_init(led_driver_init);
module_exit(led_driver_exit);

第六集应用层创建设备文件

第七集应用层程序访问底层驱动程序

3.Linux系统中文件的描述

struct inode 描述文件属性信息(文件类型,权限,大小,修改时间,设备号[设备文件])

struct file 描述一个打开的文件(打开的方式,文件偏移量,...)

4.应用层访问底层驱动过程 27-5-1-8视频(linux应用层访问底层驱动分析)在面试会问

open---->设备文件

struct inode:设备号

--------->struct cdev

它的一个成员记录操作硬件设备的函数接口

(struct file_operations)

struct inode 结构体记录struct cdev这个结构体首地址

struct file 结构体记录struct file_operations这个结构体首地址

二、字符设备驱动编写步骤总结

1.描述字符设备结构体

struct cdev:Linux 针对字符设备的通用描述

struct led_device{

struct cdev cdev;//通用的字符设备描述

...

};

需要给自己设计的结构体分配空间

pled = kmalloc(sizeof(*pled), GFP_KERNEL);

if (!pled) {

err = -ENOMEM;

goto err_kmalloc;

}

//pled->led_cdev.ops = &led_fops;

cdev_init(&pled->led_cdev, &led_fops);

2.提供硬件设备的操作函数接口

static const struct file_operations led_fops = {

.owner = THIS_MODULE,

.open = led_chrdev_open,

.release = led_chrdev_release,

.unlocked_ioctl = led_chrdev_ioctl,

};

struct file_operations 结构体填充设备的操作函数接口,需要将这个结构体的首地址

记录在struct cdev结构体中

3.申请设备号

/**

* register_chrdev_region() - register a range of device numbers

* @from: the first in the desired range of device numbers; must include

* the major number.

* @count: the number of consecutive device numbers required

* @name: the name of the device or driver.

*

* Return value is zero on success, a negative error code on failure.

*/

int register_chrdev_region(dev_t from, unsigned count, const char *name)

#define MINORBITS 20

#define MINORMASK ((1U << MINORBITS) - 1)

#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))

#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))

#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))

pled->dev = MKDEV(LED_DEVICE_MAJOR, 0);

err = register_chrdev_region(pled->dev,1, "led-device");

if(err){

printk("unable to get major %d\n", LED_DEVICE_MAJOR);

goto err_register_chrdev_region;

}

4.根据设备号添加字符设备

/**

* cdev_add() - add a char device to the system

* @p: the cdev structure for the device

* @dev: the first device number for which this device is responsible

* @count: the number of consecutive minor numbers corresponding to this

* device

*

* cdev_add() adds the device represented by @p to the system, making it

* live immediately. A negative error code is returned on failure.

*/

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

//根据设备号添加字符设备

err = cdev_add(&pled->led_cdev,pled->dev,1);

if(err){

printk("Fail to cdev add\n");

goto err_cdev_add;

}

三、ioctl函数接口

1.介绍

ioctl函数接口支持应用层的程序发命令 给底层驱动程序,通过命令的方式控制设备。

2.命令设计

#define __ASHMEMIOC 0x77

#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4)

#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t)

#define WATCHDOG_IOCTL_BASE 'W'

#define WDIOC_GETSUPPORT _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info

#define WDIOC_GETSTATUS _IOR(WATCHDOG_IOCTL_BASE, 1, int)

#define WDIOC_GETBOOTSTATUS _IOR(WATCHDOG_IOCTL_BASE, 2, int)

#define WDIOC_GETTEMP _IOR(WATCHDOG_IOCTL_BASE, 3, int)

#define WDIOC_SETOPTIONS _IOR(WATCHDOG_IOCTL_BASE, 4, int)

#define WDIOC_KEEPALIVE _IOR(WATCHDOG_IOCTL_BASE, 5, int)

#define WDIOC_SETTIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 6, int)

#define WDIOC_GETTIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 7, int)

#define WDIOC_SETPRETIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 8, int)

#define WDIOC_GETPRETIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 9, int)

#define WDIOC_GETTIMELEFT _IOR(WATCHDOG_IOCTL_BASE, 10, int)

3. 实列代码

cpp 复制代码
#define LED_DEVICE_ON _IO('L', 0x01) 
#define LED_DEVICE_OFF _IO('L', 0x02) 
static long led_device_ioctl(struct file *file,unsigned int cmd, unsigned long arg) 
{ 
    switch(cmd){ 
        case LED_DEVICE_ON: 
            printk("led device on\n"); 
            break; 
        case LED_DEVICE_OFF: 
            printk("led device off\n"); 
            break; 
        } 
    return 0; 
} 
static const struct file_operations led_device_ops = { 
    .owner = THIS_MODULE, 
    .open = led_device_open, 
    .release = led_device_release, 
    .unlocked_ioctl = led_device_ioctl, 
};

第十二集底层驱动支持ioctl操作

cpp 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>

#define LED_DEVICE_MAJOR 240
#define LED_DEVICE_MINOR 0

#define LED_DEVICE_ON _IO('L',0x01)
#define LED_DEVICE_OFF _IO('L',0x02)

MODULE_LICENSE("GPL v2");

struct led_device
{
    dev_t devid;
    struct cdev led_cdev;
};
struct led_device *pled;
static int led_device_open(struct inode *inode,struct file *file)
{
    printk("led device open\n");
    return 0;
}
static int led_device_release(struct inode *inode,struct file *file)
{
    printk("led device release\n");
    return 0;
}

static long led_device_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
    switch (cmd)
    {
    case LED_DEVICE_ON:
        printk("led device on\n");
        break;
    case LED_DEVICE_OFF:
        printk("led device off\n");
        break;
    }
    return 0;
}

struct file_operations led_device_ops = {
    .owner = THIS_MODULE,
    .open = led_device_open,
    .release = led_device_release,
    .unlocked_ioctl = led_device_ioctl,
};

static int __init  led_driver_init(void)
{
    int err;
    
    pled = kmalloc(sizeof(*pled),GFP_KERNEL);
    if(pled == NULL)
    {
        printk("Fail to kmalloc\n");
        return -ENOMEM;
    }
    //pled->led_cdev.ops = &led_device_ops;
    cdev_init(&pled->led_cdev,&led_device_ops);
    pled->devid = MKDEV(LED_DEVICE_MAJOR,LED_DEVICE_MINOR);
    err = register_chrdev_region(pled->devid,1,"led-device");
    if(err)
    {
        printk("Fail to register_chrdev_region\n");
        kfree(pled);
    }
    err = cdev_add(&pled->led_cdev,pled->devid,1);
    if(err)
    {
        printk("Fail to cdev_add\n");
        unregister_chrdev_region(pled->devid,1);
        kfree(pled);
        return err;
    }
    return 0;
}

static void __exit  led_driver_exit(void)
{
    cdev_del(&pled->led_cdev);
    unregister_chrdev_region(pled->devid,1);
    kfree(pled);
    
}
module_init(led_driver_init);
module_exit(led_driver_exit);

第十三集应用程序通过ioctl访问底层驱动

cpp 复制代码
Led-app.c的修改
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#define LED_DEVICE_ON _IO('L',0x01)
#define LED_DEVICE_OFF _IO('L',0x02)
int main(int argc,const char *argv[])
{
    int ret;
    int fd;
    fd = open("/dev/led-device",O_RDWR);
    if(fd < 0)
    {
        perror("Fail to open");
        return -1;
    }
    printf("open /dev/led-device success,fd:%d\n",fd);
    while (1)
    {
        ret = ioctl(fd,LED_DEVICE_ON);
        if(ret < 0)
        {
            perror("Fail to ioctl LED_DEVICE_ON\n");
            return -1;
        }
        sleep(1);
        ret = ioctl(fd,LED_DEVICE_OFF);
        if(ret < 0)
        {
            perror("Fail to ioctl LED_DEVICE_OFF\n");
            return -1;
        }
        sleep(1);
    }
    close(fd);
    return 0;
}

编译驱动make

清理sudo dmesg -c

加载驱动sudo insmod led-driver.ko

查看设备文件还在吗 ls -l /dev/led-device (创建方法 sudo mknod /dev/led-device c 240 0)

编译应用程序gcc led-app.c -o led-app

运行app sudo ./led-app

四、动态获取设备号

/**

* alloc_chrdev_region() - register a range of char device numbers

* @dev: output parameter for first assigned number

* @baseminor: first of the requested range of minor numbers

* @count: the number of minor numbers required

* @name: the name of the associated device or driver

*

* Allocates a range of char device numbers. The major number will be

* chosen dynamically, and returned (along with the first minor number)

* in @dev. Returns zero or a negative error code.

*/

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)


err = alloc_chrdev_region(&pled->devid,0,1,"led-device")

if(err){

printk("Fail to alloc_chrdev_region\n");

kfree(pled);

return err;

}

printk("led device MAJOR:%d MINOR:%d\n",MAJOR(pled->devid),MINOR(pled->devid));

第十四集底层驱动支持动态设备号获取

cpp 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>

#define LED_DEVICE_MAJOR 240
#define LED_DEVICE_MINOR 0

#define LED_DEVICE_ON _IO('L',0x01)
#define LED_DEVICE_OFF _IO('L',0x02)

MODULE_LICENSE("GPL v2");

struct led_device
{
    dev_t devid;
    struct cdev led_cdev;
};
struct led_device *pled;
static int led_device_open(struct inode *inode,struct file *file)
{
    printk("led device open\n");
    return 0;
}
static int led_device_release(struct inode *inode,struct file *file)
{
    printk("led device release\n");
    return 0;
}

static long led_device_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
    switch (cmd)
    {
    case LED_DEVICE_ON:
        printk("led device on\n");
        break;
    case LED_DEVICE_OFF:
        printk("led device off\n");
        break;
    }
    return 0;
}

struct file_operations led_device_ops = {
    .owner = THIS_MODULE,
    .open = led_device_open,
    .release = led_device_release,
    .unlocked_ioctl = led_device_ioctl,
};

static int __init  led_driver_init(void)
{
    int err;
    
    pled = kmalloc(sizeof(*pled),GFP_KERNEL);
    if(pled == NULL)
    {
        printk("Fail to kmalloc\n");
        return -ENOMEM;
    }
    //pled->led_cdev.ops = &led_device_ops;
    cdev_init(&pled->led_cdev,&led_device_ops);
#if 0
    pled->devid = MKDEV(LED_DEVICE_MAJOR,LED_DEVICE_MINOR);
    err = register_chrdev_region(pled->devid,1,"led-device");
    if(err)
    {
        printk("Fail to register_chrdev_region\n");
        kfree(pled);
    }
#endif
    err = alloc_chrdev_region(&pled->devid,0,1,"led-device");
    if(err)
    {
        printk("Fail to alloc_chrdev_region\n");
        kfree(pled);
        return err;
    }
    printk("led device MAJOR:%d MINOR:%d\n",MAJOR(pled->devid),MINOR(pled->devid));

    err = cdev_add(&pled->led_cdev,pled->devid,1);
    if(err)
    {
        printk("Fail to cdev_add\n");
        unregister_chrdev_region(pled->devid,1);
        kfree(pled);
        return err;
    }
    return 0;
}

static void __exit  led_driver_exit(void)
{
    cdev_del(&pled->led_cdev);
    unregister_chrdev_region(pled->devid,1);
    kfree(pled);
    
}
module_init(led_driver_init);
module_exit(led_driver_exit);

五、自动创建设备文件

1.sysfs文件系统介绍

在Linux 2.6内核开始,Linux 内核实现了sysfs,默认挂载在/sys。sysfs是一种内存文件系统,和/proc类

似。sysfs这种文件系统主要的用途是内核空间和用户空间交互的接口。内核空间通过sysfs文件系统,把

一些内核参数导出,每个被导出的内核参数在sysfs文件系统中都有一个文件和他对应。文件的内容就是

内核参数的值。例如:如果驱动模块可以将自己的设备号作为内核参数导出,在sysfs文件中就有一个叫做

uevent文件记录它的值。

2.自动创建设备文件的工具

在Linux系统中devtmpfs udev mdev 三个工具可以自动帮我们创建设备文件。

(1) devtmpfs

devtmpfs是一种内核文件系统,用于在Linux启动时创建/dev目录中的设备节点 。在启动时,内核会将

devtmpfs挂载到/dev目录下,并自动创建与系统硬件设备对应的设备文件,这些设备文件用于让用户空间

应用程序与硬件设备进行通信。

(2) udev

udev是一种用户空间的设备管理工具,它在Linux启动时启动,可以通过监听内核的uevent事件来自动

创建、删除、重命名设备节点 ,同时也可以在设备插入或移除时执行用户定义的脚本来完成一些自定义的

操作,例如自动挂载文件系统等。

(3) mdev

mdev是一个轻量级的、基于BusyBox的设备管理工具 ,它在系统启动时创建设备节点,并且可以在设备

插入或移除时执行用户定义的脚本来完成一些自定义的操作。相对于udev而言,mdev更加轻量级,但也

缺少了udev一些高级的功能,例如对于网络设备的管理和规则匹配等。

(3)自动创建设备文件步骤

第十七集底层驱动创建类

cpp 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#define LED_DEVICE_MAJOR 240
#define LED_DEVICE_MINOR 0

#define LED_DEVICE_ON _IO('L',0x01)
#define LED_DEVICE_OFF _IO('L',0x02)

MODULE_LICENSE("GPL v2");

struct led_device
{
    dev_t devid;
    struct class *class;
    struct cdev led_cdev;
};
struct led_device *pled;
static int led_device_open(struct inode *inode,struct file *file)
{
    printk("led device open\n");
    return 0;
}
static int led_device_release(struct inode *inode,struct file *file)
{
    printk("led device release\n");
    return 0;
}

static long led_device_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
    switch (cmd)
    {
    case LED_DEVICE_ON:
        printk("led device on\n");
        break;
    case LED_DEVICE_OFF:
        printk("led device off\n");
        break;
    }
    return 0;
}

struct file_operations led_device_ops = {
    .owner = THIS_MODULE,
    .open = led_device_open,
    .release = led_device_release,
    .unlocked_ioctl = led_device_ioctl,
};

static int __init  led_driver_init(void)
{
    int err;
    
    pled = kmalloc(sizeof(*pled),GFP_KERNEL);
    if(pled == NULL)
    {
        printk("Fail to kmalloc\n");
        return -ENOMEM;
    }
    //pled->led_cdev.ops = &led_device_ops;
    cdev_init(&pled->led_cdev,&led_device_ops);
#if 0
    pled->devid = MKDEV(LED_DEVICE_MAJOR,LED_DEVICE_MINOR);
    err = register_chrdev_region(pled->devid,1,"led-device");
    if(err)
    {
        printk("Fail to register_chrdev_region\n");
        kfree(pled);
    }
#endif
    err = alloc_chrdev_region(&pled->devid,0,1,"led-device");
    if(err)
    {
        printk("Fail to alloc_chrdev_region\n");
        kfree(pled);
        return err;
    }
    printk("led device MAJOR:%d MINOR:%d\n",MAJOR(pled->devid),MINOR(pled->devid));

    err = cdev_add(&pled->led_cdev,pled->devid,1);
    if(err)
    {
        printk("Fail to cdev_add\n");
        unregister_chrdev_region(pled->devid,1);
        kfree(pled);
        return err;
    }
//创建
    pled->class = class_create(THIS_MODULE,"imx6ull-leds");
    if (IS_ERR(pled->class))
    {
        err = PTR_ERR(pled->class);
        printk("Fail to class_create\n");
        cdev_del(&pled->led_cdev);
        unregister_chrdev_region(pled->devid,1);
        kfree(pled);
        return err;
    }


    return 0;
}

static void __exit  led_driver_exit(void)
{
//释放
    class_destroy(pled->class);
    cdev_del(&pled->led_cdev);
    unregister_chrdev_region(pled->devid,1);
    kfree(pled);
    
}
module_init(led_driver_init);
module_exit(led_driver_exit);

编译make

sudo insmod led-driver.ko(如果已经加载,卸载重加sudo rmmod led-driver)

创建类的时候不能有同名的文件,名字不能重复,dmesg下查看

(1)创建类

//1.创建一个类 /sys/class/类名(文件夹)

pled->class = class_create(THIS_MODULE,"imx6ull-leds");

if (IS_ERR(pled->class)) {

err = PTR_ERR(pled->class);

printk("Fail to class_create\n");

cdev_del(&pled->led_cdev);

unregister_chrdev_region(pled->devid,1);

kfree(pled);

return err;

}

(2)创建设备

/**

* device_create - creates a device and registers it with sysfs

* @class: pointer to the struct class that this device should be registered to

* @parent: pointer to the parent struct device of this new device, if any

* @devt: the dev_t for the char device to be added

* @drvdata: the data to be added to the device for callbacks

* @fmt: string for the device's name

*

* This function can be used by char device classes. A struct device

* will be created in sysfs, registered to the specified class.

*

* A "dev" file will be created, showing the dev_t for the device, if

* the dev_t is not 0,0.

* If a pointer to a parent struct device is passed in, the newly created

* struct device will be a child of that device in sysfs.

* The pointer to the struct device will be returned from the call.

* Any further sysfs files that might be required can be created using this

* pointer.

*

* Returns &struct device pointer on success, or ERR_PTR() on error.

*

* Note: the struct class passed to this function must have previously

* been created with a call to class_create().

*/

struct device *device_create(struct class *class, struct device *parent,

dev_t devt, void *drvdata, const char *fmt, ...)

//2.创建一个设备,导出设备号信息

pled->dev = device_create(pled->class,NULL,pled->devid,NULL,"led-device");

if(IS_ERR(pled->dev)){

err = PTR_ERR(pled->dev);

printk("Fail to class_create\n");

class_destroy(pled->class);

cdev_del(&pled->led_cdev);

unregister_chrdev_region(pled->devid,1);

kfree(pled);

return err;

}

第十八集底层驱动创建设备导出设备号信息

cpp 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#define LED_DEVICE_MAJOR 240
#define LED_DEVICE_MINOR 0

#define LED_DEVICE_ON _IO('L',0x01)
#define LED_DEVICE_OFF _IO('L',0x02)

MODULE_LICENSE("GPL v2");

struct led_device
{
    dev_t devid;
    struct device *dev;
    struct class *class;
    struct cdev led_cdev;
};
struct led_device *pled;
static int led_device_open(struct inode *inode,struct file *file)
{
    printk("led device open\n");
    return 0;
}
static int led_device_release(struct inode *inode,struct file *file)
{
    printk("led device release\n");
    return 0;
}

static long led_device_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
    switch (cmd)
    {
    case LED_DEVICE_ON:
        printk("led device on\n");
        break;
    case LED_DEVICE_OFF:
        printk("led device off\n");
        break;
    }
    return 0;
}

struct file_operations led_device_ops = {
    .owner = THIS_MODULE,
    .open = led_device_open,
    .release = led_device_release,
    .unlocked_ioctl = led_device_ioctl,
};

static int __init  led_driver_init(void)
{
    int err;
    
    pled = kmalloc(sizeof(*pled),GFP_KERNEL);
    if(pled == NULL)
    {
        printk("Fail to kmalloc\n");
        return -ENOMEM;
    }
    //pled->led_cdev.ops = &led_device_ops;
    cdev_init(&pled->led_cdev,&led_device_ops);
#if 0
    pled->devid = MKDEV(LED_DEVICE_MAJOR,LED_DEVICE_MINOR);
    err = register_chrdev_region(pled->devid,1,"led-device");
    if(err)
    {
        printk("Fail to register_chrdev_region\n");
        kfree(pled);
    }
#endif
    err = alloc_chrdev_region(&pled->devid,0,1,"led-device");
    if(err)
    {
        printk("Fail to alloc_chrdev_region\n");
        kfree(pled);
        return err;
    }
    printk("led device MAJOR:%d MINOR:%d\n",MAJOR(pled->devid),MINOR(pled->devid));

    err = cdev_add(&pled->led_cdev,pled->devid,1);
    if(err)
    {
        printk("Fail to cdev_add\n");
        unregister_chrdev_region(pled->devid,1);
        kfree(pled);
        return err;
    }
//创建类
    pled->class = class_create(THIS_MODULE,"imx6ull-leds");
    if (IS_ERR(pled->class))
    {
        err = PTR_ERR(pled->class);
        printk("Fail to class_create\n");
        cdev_del(&pled->led_cdev);
        unregister_chrdev_region(pled->devid,1);
        kfree(pled);
        return err;
    }
    //创建设备;导出设备号信息
    pled->dev = device_create(pled->class,NULL,pled->devid,NULL,"led-device");
    if(IS_ERR(pled->dev))
    {
        printk("Fail to device_create\n");
        err = PTR_ERR(pled->dev);
        class_destroy(pled->class);
        cdev_del(&pled->led_cdev);
        unregister_chrdev_region(pled->devid,1);
        kfree(pled);
        return err;
    }

    return 0;
}

static void __exit  led_driver_exit(void)
{
    device_destroy(pled->class,pled->devid);
    class_destroy(pled->class);
    cdev_del(&pled->led_cdev);
    unregister_chrdev_region(pled->devid,1);
    kfree(pled);
    
}
module_init(led_driver_init);
module_exit(led_driver_exit);

编译make

sudo rm -rf /dev/led-device

sudo insmod led-driver.ko

ls /dev/led-device -l

六、案例代码

cpp 复制代码
#include <linux/init.h> 
#include <linux/module.h> 
#include <linux/fs.h> 
#include <linux/cdev.h> 
#include <linux/slab.h> 
#include <linux/device.h> 
#define LED_DEVICE_MAJOR 240 
#define LED_DEVICE_MINOR 0 
#define LED_DEVICE_ON _IO('L', 0x01) 
#define LED_DEVICE_OFF _IO('L', 0x02) 
MODULE_LICENSE("GPL v2");
struct led_device 
{ 
    dev_t devid; 
    struct device *dev; 
    struct class *class; 
    struct cdev led_cdev; 
}; 
struct led_device *pled; 
static int led_device_open(struct inode *inode, struct file *file) 
{ 
    printk("led device open\n"); 
    return 0; 
} 
static int led_device_release(struct inode *inode, struct file *file) 
{ 
    printk("led device release\n"); 
    return 0; 
} 
static long led_device_ioctl(struct file *file,unsigned int cmd, unsigned long arg) 
{ 
    switch(cmd){ 
        case LED_DEVICE_ON: 
            printk("led device on\n"); 
            break; 
        case LED_DEVICE_OFF: 
            printk("led device off\n"); 
            break; 
    } 
    return 0; 
} 
/* 
字符设备驱动核心思想: 
1.字符设备驱动需要提供设备的操作函数接口 
2.应用层如何找到这些函数接口 
*/ 
static const struct file_operations led_device_ops = { 
    .owner = THIS_MODULE, 
    .open = led_device_open, 
    .release = led_device_release, 
    .unlocked_ioctl = led_device_ioctl, 
}; 
static int led_driver_init(void) 
{ 
    int err; 
    pled = kmalloc(sizeof(*pled),GFP_KERNEL); 
    if(!pled){ 
        printk("Fail to kmalloc\n"); 
        err = -ENOMEM; 
        goto err_kmalloc; 
} 
cdev_init(&pled->led_cdev,&led_device_ops); 
//注册设备号 
err = alloc_chrdev_region(&pled->devid,0,1,"led-device");
if(err){ 
    printk("Fail to alloc_chrdev_region\n"); 
    goto err_alloc_chrdev_region; 
} 
    printk("led device MAJOR:%d MINOR:%d\n",MAJOR(pled->devid),MINOR(pled->devid)); 
//根据设备号来添加字符设备(让设备号与字符设备进行绑定) 
    err = cdev_add(&pled->led_cdev,pled->devid,1); 
    if(err){ 
        printk("Fail to cdev_add\n"); 
        goto err_cdev_add; 
    } 
    //创建类 
    pled->class = class_create(THIS_MODULE,"imx6ull-leds"); 
        if (IS_ERR(pled->class)) { 
        err = PTR_ERR(pled->class); 
        printk("Fail to class_create\n"); 
        goto err_class_create; 
    } 
    //创建设备:导出设备号信息 
    pled->dev = device_create(pled->class,NULL,pled->devid,NULL,"led-device"); 
    if(IS_ERR(pled->dev)){ 
        printk("Fail to device_create\n"); 
        err = PTR_ERR(pled->dev); 
        goto err_device_create; 
    } 
    return 0; 
    err_device_create: 
    class_destroy(pled->class); 
    err_class_create: 
    cdev_del(&pled->led_cdev); 
    err_cdev_add: 
    unregister_chrdev_region(pled->devid,1); 
    err_alloc_chrdev_region: 
    kfree(pled); 
    err_kmalloc: 
    return err; 
} 
static void led_driver_exit(void) 
{ 
    device_destroy(pled->class,pled->devid); 
    class_destroy(pled->class); 
    cdev_del(&pled->led_cdev); 
    unregister_chrdev_region(pled->devid,1); 
    kfree(pled); 
    return; 
} 
module_init(led_driver_init); 
module_exit(led_driver_exit);

第十九集底层驱动出错处理优化

cpp 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#define LED_DEVICE_MAJOR 240
#define LED_DEVICE_MINOR 0

#define LED_DEVICE_ON _IO('L',0x01)
#define LED_DEVICE_OFF _IO('L',0x02)

MODULE_LICENSE("GPL v2");

struct led_device
{
    dev_t devid;
    struct device *dev;
    struct class *class;
    struct cdev led_cdev;
};
struct led_device *pled;
static int led_device_open(struct inode *inode,struct file *file)
{
    printk("led device open\n");
    return 0;
}
static int led_device_release(struct inode *inode,struct file *file)
{
    printk("led device release\n");
    return 0;
}

static long led_device_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
    switch (cmd)
    {
    case LED_DEVICE_ON:
        printk("led device on\n");
        break;
    case LED_DEVICE_OFF:
        printk("led device off\n");
        break;
    }
    return 0;
}

struct file_operations led_device_ops = {
    .owner = THIS_MODULE,
    .open = led_device_open,
    .release = led_device_release,
    .unlocked_ioctl = led_device_ioctl,
};

static int __init  led_driver_init(void)
{
    int err;
    
    pled = kmalloc(sizeof(*pled),GFP_KERNEL);
    if(pled == NULL)
    {
        printk("Fail to kmalloc\n");
        err = -ENOMEM;
        goto err_kmalloc;
    }
    //pled->led_cdev.ops = &led_device_ops;
    cdev_init(&pled->led_cdev,&led_device_ops);
#if 0
    pled->devid = MKDEV(LED_DEVICE_MAJOR,LED_DEVICE_MINOR);
    err = register_chrdev_region(pled->devid,1,"led-device");
    if(err)
    {
        printk("Fail to register_chrdev_region\n");
        kfree(pled);
    }
#endif
    err = alloc_chrdev_region(&pled->devid,0,1,"led-device");
    if(err)
    {
        printk("Fail to alloc_chrdev_region\n");
        goto err_alloc_chrdev_region;
      
    }
    printk("led device MAJOR:%d MINOR:%d\n",MAJOR(pled->devid),MINOR(pled->devid));

    err = cdev_add(&pled->led_cdev,pled->devid,1);
    if(err)
    {
        printk("Fail to cdev_add\n");
        goto err_cdev_add;
        
    }

    pled->class = class_create(THIS_MODULE,"imx6ull-leds");
    if (IS_ERR(pled->class))
    {
        err = PTR_ERR(pled->class);
        printk("Fail to class_create\n");
        goto err_class_create;
        
    }
    //
    pled->dev = device_create(pled->class,NULL,pled->devid,NULL,"led-device");
    if(IS_ERR(pled->dev))
    {
        printk("Fail to device_create\n");
        err = PTR_ERR(pled->dev);
        goto err_device_create;
        
    }

    return 0;
err_device_create:
    class_destroy(pled->class);
err_class_create:
    cdev_del(&pled->led_cdev);
err_cdev_add:
    unregister_chrdev_region(pled->devid,1);
err_alloc_chrdev_region:
    kfree(pled);
err_kmalloc:
    return err;
}

static void __exit  led_driver_exit(void)
{
    device_destroy(pled->class,pled->devid);
    class_destroy(pled->class);
    cdev_del(&pled->led_cdev);
    unregister_chrdev_region(pled->devid,1);
    kfree(pled);
    
}
module_init(led_driver_init);
module_exit(led_driver_exit);
相关推荐
生信大表哥1 小时前
Claude Code / Gemini CLI / Codex CLI 安装大全(Linux 服务器版)
linux·python·ai·r语言·数信院生信服务器
繁星蓝雨1 小时前
我与C++的故事(杂谈)
开发语言·c++
知识分享小能手1 小时前
CentOS Stream 9入门学习教程,从入门到精通, Linux文本编辑器 —— 语法详解与实战案例(5)
linux·学习·centos
网安老伯1 小时前
什么是网络安全?网络安全包括哪几个方面?学完能做一名黑客吗?
linux·数据库·python·web安全·网络安全·php·xss
贝塔实验室1 小时前
新手如何使用Altium Designer创建第一张原理图(三)
arm开发·单片机·嵌入式硬件·fpga开发·射频工程·基带工程·嵌入式实时数据库
tang_vincent1 小时前
Linux虚拟内存固定映射区-fixmap
linux
企鹅侠客1 小时前
Ubuntu本地部署AnythingLLM实现本地文档RAG
linux·运维·ubuntu·llm
Victoria.a2 小时前
Linux环境基础开发工具使用
linux
Queenie_Charlie2 小时前
和为k的连续区间
数据结构·c++·map