11_注册字符类设备

注册字符类设备

注册字符类设备简介

​ 在 Linux 内核中, 使用 cdev 结构体描述一个字符设备, cdev 结构体的定义如下:

struct cdev { //描述字符设备的一个结构体
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
};

​ cdev 结构体的 dev_t 成员定义了设备号, 为 32 位, 其中 12 位为主设备号, 20 位为次设备号。 使用下列宏可以从 dev_t 获得主设备号和次设备号:

MAJOR(dev_t dev)
MINOR(dev_t dev)

​ 而使用下列宏则可以通过主设备号和次设备号生成 dev_t

void cdev_init(struct cdev *, struct file_operations *);
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);

​ cdev_add( ) 函数和 cdev_del( ) 函数分别向系统添加和删除一个 cdev, 完成字符设备的注册和注销。 对cdev_add( ) 的调用通常发生在字符设备驱动模块加载函数中, 而对 cdev_del( ) 函数的调用则通常发生在字符设备驱动模块卸载函数中

头文件

#include <linux/cdev.h>

void cdev_init(struct cdev *, const struct file_operations *);

image-20240423151146670

int cdev_add(struct cdev *, dev_t, unsigned);

image-20240423151252884

void cdev_del(struct cdev *);

image-20240423151307844

生成设备节点

字符设备注册完以后不会自动生成设备节点。 我们需要使用 mknod 命令创建一个设备节点
格式: mknod 名称 类型 主设备号 次设备号
举例:

mknod /dev/test c 247 0

示例

chrdev.c

#include <linux/init.h>   //包含宏定义的头文件
#include <linux/module.h> //包含初始化加载模块的头文件
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>

#define DEVICE_NUMBER 1        // 次设备号的个数
#define DEVICE_SNAME "schrdev" // 静态注册设备的名称
#define DEVICE_ANAME "achrdev" // 动态注册设备的名称
#define DEVICE_MINOR_NUMBER 0  // 次设备号的起始地址

static int major_num; // 主设备号
static int minor_num; // 次设备号

struct cdev cdev; //字符设备

module_param(major_num, int, S_IRUSR);
module_param(minor_num, int, S_IRUSR);

int chrdev_open(struct inode *inode, struct file *file)
{
    printk("chrdev_open\n");
    return 0;
}

struct file_operations chrdev_ops = {
    .owner = THIS_MODULE,
    .open = chrdev_open
};

static int hello_init(void)
{
    dev_t dev_num; // 设备号
    int ret;
    if (major_num)
    {
        dev_num = MKDEV(major_num, minor_num); // 主设备号和次设备号组成一个 dev_t 设备号
        ret = register_chrdev_region(dev_num, DEVICE_NUMBER, DEVICE_SNAME); //静态分配设备号
        if(ret < 0)
        {
            printk("register_chrdev_region is error\n");
        }
        printk("register_chrdev_region is ok\n");
        printk("major_num = %d\n", major_num);
        printk("minor_num = %d\n", minor_num);
    }
    else
    {
        ret = alloc_chrdev_region(&dev_num, DEVICE_MINOR_NUMBER, DEVICE_NUMBER, DEVICE_ANAME); //动态分配设备号
        if(ret < 0)
        {
            printk("alloc_chrdev_region is error\n");
        }
        printk("alloc_chrdev_region is ok\n");
        major_num = MAJOR(dev_num); //在 dev_t 里面获取我们的主设备号
        minor_num = MINOR(dev_num); //在 dev_t 里面获取我们的次设备号
        printk("major_num = %d\n", major_num);
        printk("minor_num = %d\n", minor_num);
    }
    cdev.owner = THIS_MODULE;
    cdev_init(&cdev, &chrdev_ops); //初始化字符设备
    cdev_add(&cdev, dev_num, DEVICE_NUMBER); //注册字符设备
    return 0;
}

static void hello_exit(void)
{
    unregister_chrdev_region(MKDEV(major_num, minor_num), DEVICE_NUMBER); //注销设备号
    cdev_del(&cdev); //注销字符设备
    printk("byby\n"); // 内核模块卸载的时候打印"byb byb
}

module_init(hello_init); // 驱动模块的入口
module_exit(hello_exit); // 驱动模块的出口

MODULE_LICENSE("GPL"); // 声明模块拥有开源许可证

app.c

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, const char* argv[])
{
    int fd;
    fd = open("/dev/test", O_RDWR);
    if(fd < 0)
    {
        perror("open error\n");
        return -1;
    }
    close(fd);
    return 0;
}

Makefile

obj-m +=chrdev.o
KDIR:=/home/mzx/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga 
PWD?=$(shell pwd)

all:
	make -C $(KDIR) M=$(PWD) modules
posted @ 2024-04-25 22:34  爱吃冰激凌的黄某某  阅读(23)  评论(0编辑  收藏  举报