嵌入式学习(肆壹)

一、字符设备驱动内核工作实现

1.1 框图

1.2 分配对象

头文件:#include <slab.h> #include <cdev.h>

入口:insmod执行

    struct cdev *cdev_alloc(void)

    函数功能:分配struct cdev结构体指针

    参数:无

    返回值:成功返回struct cdev结构体指针,失败返回NULL

出口:

  void kfree(const void *block)

  函数功能:释放分配到的空间 

1.3 初始化对象

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

函数功能:对struct cdev 结构体指针进行初始化

参数:

    cdev:分配成功之后结构体指针

    fops:操作方法结构体

返回值:无   

1.4 注册设备号

静态指定设备号

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

函数功能:静态指定设备号值,需要注意不能和/proc/device目录下设备号重复

参数:9

    from:设备号

      MKDEV(主设备号, 次设备号) ====> 功能:将主设备号和次设备号合成设备号

      MAJOR(dev)  ====> 功能:根据设备号的值,得到主设备号值

      MINOR(dev) ====> 功能:根据设备号的值,得到次设备号值

    count:设备的个数

    name:设备名字

返回值:成功返回0,失败返回-1,置位错误码

动态分配设备号

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

函数功能:动态分配设备号值

参数:

    dev:分配到的设备号

    baseminor:次设备号的起始值

    count:设备的个数

    name:设备名字   

返回值:成功返回0,失败返回-1,置位错误码

注销设备号

void unregister_chrdev_region(dev_t from, unsigned count)

函数功能:注销设备号

参数:

  from:设备号

  count:设备的个数

1.5 注册对象

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

函数功能:注册字符设备驱动

参数:

    p:分配成功之后结构体指针

    dev:设备号

    count:设备的个数

返回值:成功返回0,失败返回-1,置位错误码   

1.6 注销对象

void cdev_del(struct cdev *p)

函数功能:注销字符设备驱动

参数:

      p:分配成功之后结构体指针

二、编写代码实例

2.1 驱动代码编写

#include <linux/init.h>

#include <linux/module.h>

#include <linux/slab.h>

#include <linux/cdev.h>

#include <linux/fs.h>

#include <linux/device.h>

#if 0

unsigned int major = 500; //定义一个变量存放静态主设备号值

#else

unsigned int major = 0; //定义一个变量存放主设备号值

#endif

unsigned int minor = 0; //定义一个变量存放次设备号值

struct cdev *cdev;

int count = 3;

#define CNAME "myled"

struct class *cls;

struct device* dev;

int myled_open(struct inode *inode, struct file *file)

{

    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);

    return 0;

}

ssize_t myled_read(struct file *file, char __user *ubuf, size_t size, loff_t *loffs)

{

    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);

    return 0;

}

ssize_t myled_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loffs)

{

    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);

    return 0;

}

int myled_close(struct inode *inode, struct file *file)

{

    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);

    return 0;

}

const struct file_operations fop = {

        .open = myled_open,

        .read = myled_read,

        .write = myled_write,

        .release = myled_close,

};

//入口

static int __init demo_init(void)

{

    int ret,i;

    dev_t devno;

    //1.分配对象,就是分配struct cdev结构体指针 cdev_alloc

    cdev = cdev_alloc();

    if(cdev == NULL){

        printk("cdev alloc is error\n");

        ret = -ENOMEM;

        goto ERR1;

    }

    //2.初始化对象,就是初始化分配到struct cdev结构体指针 cdev_init

    cdev_init(cdev,&fop);

    //3.注册设备号

    if(major > 0){ //静态指定设备号 register_chrdev_region

        ret = register_chrdev_region(MKDEV(major,minor),count,CNAME);   

        if(ret){

            printk("register chrdev region is error\n");

            ret = -EIO;

            goto ERR2;

        }

    }else if(major == 0){//动态分配设备号 alloc_chrdev_region cat /proc/devices

        ret = alloc_chrdev_region(&devno,minor,count,CNAME);

        if(ret){

            printk("register chrdev region is error\n");

            ret = -EIO;

            goto ERR2;

        }   

        major = MAJOR(devno);//根据设备号,获取到主设备号

        minor = MINOR(devno);//根据设备号,获取到次设备号   

    }       

    //4.注册对象,就是注册字符设备驱动 cdev_add

    ret = cdev_add(cdev,MKDEV(major,minor),count);

    if(ret){

        printk("cdev add is error\n");

        ret = -EIO;

        goto ERR3;

    }   

    //5.向上层提交目录信息 class_create /sys/class

    cls = class_create(THIS_MODULE,CNAME);

    if(IS_ERR(cls)){

        printk("class create is error\n");

        ret = PTR_ERR(cls);

        goto ERR4;

    }

    //6.向上层提交设备节点信息,提交三个设备节点信息 /dev/myled0 /dev/myled1 /dev/myled2  device_create

    for(i=0;i<count;i++)

    {

      dev = device_create(cls,NULL,MKDEV(major,i),NULL,"myled%d",i);

        if(IS_ERR(dev)){

            printk("device create is error\n");

            ret = PTR_ERR(dev);

            goto ERR5;

        }

    }

    return 0;

ERR5:

    //如果创建三个设备节点,第一个设备节点和第二个设备节点创建成功,但是第三个创建失败

    //需要释放第一个和第二个设备节点

    for(--i;i>0;i--)

    {

        device_destroy(cls,MKDEV(major,i));

    }

    class_destroy(cls);

ERR4:

    cdev_del(cdev);

ERR3:

    unregister_chrdev_region(MKDEV(major,minor),count);

ERR2:

    kfree(cdev);

ERR1:

    return ret;   

}

//出口

static void __exit demo_exit(void)

{

    int i = 0;

    for(i=0;i<count;i++)

    {

        device_destroy(cls,MKDEV(major,i));

    }

    class_destroy(cls);

    cdev_del(cdev);

    unregister_chrdev_region(MKDEV(major,minor),count);

    kfree(cdev);

}

module_init(demo_init);//指定入口地址

module_exit(demo_exit);//指定出口地址

MODULE_LICENSE("GPL");//许可证

2.2 应用层代码编写

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

#include <stdlib.h>

#include <string.h>

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

{

    int fd = -1;

    char buf[128] = {0};

    //打开设备文件

    fd = open("/dev/myled0",O_RDWR);

    if(fd < 0){

        perror("open is error");

        exit(1);

    }

    write(fd,buf,sizeof(buf));//写函数

    read(fd,buf,sizeof(buf));//读函数

    close(fd);//关闭函数

    return 0;

}

2.3 测试步骤

1、保证编译驱动成功,安装驱动

2、查看目录信息

linux@ubuntu:~/DC23021/08_step_chrdev$ sudo insmod demo.ko

linux@ubuntu:~/DC23021/08_step_chrdev$ cat /proc/devices

成功现象:

236 myled

3、查看创建设备节点信息

linux@ubuntu:~/DC23021/08_step_chrdev$ cd /sys/class/myled/

linux@ubuntu:/sys/class/myled$ ls

myled0  myled1  myled2

linux@ubuntu:/sys/class/myled$ ls /dev/myled* -ll

crw-r--r-- 1 root root 236, 0 Nov  2 09:20 /dev/myled

crw------- 1 root root 236, 0 Nov  2 13:58 /dev/myled0

crw------- 1 root root 236, 1 Nov  2 13:58 /dev/myled1

crw------- 1 root root 236, 2 Nov  2 13:58 /dev/myled2

4、测试驱动程序

linux@ubuntu:~/DC23021/08_step_chrdev$ gcc test.c

linux@ubuntu:~/DC23021/08_step_chrdev$ sudo ./a.out

linux@ubuntu:~/DC23021/08_step_chrdev$ dmesg

[18025.669546] /home/linux/DC23021/08_step_chrdev/demo.c:myled_open:22

[18025.669553] /home/linux/DC23021/08_step_chrdev/demo.c:myled_write:34

[18025.669554] /home/linux/DC23021/08_step_chrdev/demo.c:myled_read:28

[18025.669556] /home/linux/DC23021/08_step_chrdev/demo.c:myled_close:40

5、卸载驱动

linux@ubuntu:~/DC23021/08_step_chrdev$ sudo rmmod demo

linux@ubuntu:~/DC23021/08_step_chrdev$ ls /dev/myled* -ll

    crw-r--r-- 1 root root 236, 0 Nov  2 09:20 /dev/myled

    成功现象:没有之前创建的设备节点/dev/myled0,/dev/myled1,/dev/myled2

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容