Linux驱动开发之动态分配设备号(参考内核驱动源代码-标准写法)

一、编写驱动文件

#include <linux/init.h> // 所有模块都必须包含的头文件 
#include <linux/module.h> // 一些宏定义,例如这里的KERN_INFO
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>

#include "hello_world.h"

#define VICHIP_DEV "vichip"

static struct class *driver_class; // 定义类
static dev_t vichip_device_no; // 定义设备号

// 定义结构体
struct vichip_control {
struct cdev cdev;
};

static struct vichip_control vichip;

static int vichip_open(struct inode *inode, struct file *file)
{
int ret = 0;

printk(KERN_INFO "----vichip_open----\n");

return ret;
}

static int vichip_release(struct inode *inode, struct file *file)
{
int ret = 0;

printk(KERN_INFO "----vichip_release----\n");

return ret;
}

static const struct file_operations vichip_fops = {
.owner = THIS_MODULE,
.open = vichip_open,
.release = vichip_release
};

static int __init hello_init(void)
{
int rc;
struct device *class_dev;

rc = alloc_chrdev_region(&vichip_device_no, 0, 1, VICHIP_DEV); // 动态分配设备编号
if (rc < 0) {
pr_err("alloc_chrdev_region failed %d\n", rc);
return rc;
}

driver_class = class_create(THIS_MODULE, VICHIP_DEV); // 创建类
if (IS_ERR(driver_class)) {
rc = -ENOMEM;
pr_err("class_create failed %d\n", rc);
goto exit_unreg_chrdev_region;
}

class_dev = device_create(driver_class, NULL, vichip_device_no, NULL, VICHIP_DEV); // 创建设备(这个设备属于哪一类设备)
if (IS_ERR(class_dev)) {
pr_err("class_device_create failed %d\n", rc);
rc = -ENOMEM;
goto exit_destroy_class;
}

cdev_init(&vichip.cdev, &vichip_fops); // 初始化字符设备
vichip.cdev.owner = THIS_MODULE;
rc = cdev_add(&vichip.cdev, MKDEV(MAJOR(vichip_device_no), 0), 1); // 将字符设备添加到内核中 
if (rc < 0) {
pr_err("cdev_add failed %d\n", rc);
cdev_del(&vichip.cdev); // 将字符设备从内核中移除
goto exit_destroy_device;
}

    printk(KERN_INFO "----hello_init rc = [%d]----\n", rc);

return rc;

exit_destroy_device:
device_destroy(driver_class, vichip_device_no); // 销毁设备
exit_destroy_class:
class_destroy(driver_class); // 销毁类
exit_unreg_chrdev_region:
unregister_chrdev_region(vichip_device_no, 1); // 注销动态分配的设备号
return rc;
}

static void __exit hello_exit(void)
{
cdev_del(&vichip.cdev);
device_destroy(driver_class, vichip_device_no);
class_destroy(driver_class);
unregister_chrdev_region(vichip_device_no, 1);

    printk(KERN_INFO "----hello_exit----\n");
}

module_init(hello_init);// 用宏来指定入口,加载模块时里面的加载函数会被调用  
module_exit(hello_exit);

// 模块的许可证 
MODULE_LICENSE("GPL");
// 模块的作者
MODULE_AUTHOR(DRIVER_AUTHOR);
// 模块的描述

MODULE_DESCRIPTION(DRIVER_DESC);

二、头文件hello_world.h

#define DRIVER_AUTHOR "[email protected]"

#define DRIVER_DESC   "A sample driver" 

三、编写Makefile

ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:                               
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:                                             
        $(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
else
    obj-m := hello_world.o

endif

四、编译模块 

make all

五、安装内核模块

insmod hello_world.ko

六、编写测试文件test.c

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

#define DEV_NAME "/dev/vichip"

int main(int argc, char *argv[])
{

        int fd = -1;

        printf("------------start---------------\n");

        fd = open(DEV_NAME, O_RDWR);
        if(fd < 0)
        {
                perror("open file error:");
                return -1;
        }

        close(fd);


        printf("------------end---------------\n");

        return 0;

}

七、编译测试文件 

gcc test.c -o test

八、测试驱动程序

./test

九、测试结果

1、查看创建的类

结果:在/sys/class目录下有vichip目录。

2、查看创建的设备

结果:在/dev目录下有vichip设备文件

crw-------   1 root root    243,   0 May  9 16:42 vichip

或cat /proc/devices

243 vichip

3、查看输出的消息

dmesg

----hello_init rc = [0]----

4、卸载已安装的内核模块

rmmod hello_world

执行如下操作:

cdev_del(&vichip.cdev);
device_destroy(driver_class, vichip_device_no);
class_destroy(driver_class);
unregister_chrdev_region(vichip_device_no, 1);

猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80255987