• linux驱动开发(三) 字符设备驱动框架


    还是老规矩先上代码

    demo.c

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/cdev.h>
    #include <linux/fs.h>
    
    
    
    int demo_major = 250;
    int demo_minor = 0;
    int demo_count = 1;
    
    struct cdev cdev;
    
    int  demo_open(struct inode *inodep, struct file * filep) // 打开设备
    {
        printk("%s,%d
    ", __func__, __LINE__);
        return 0;
    }
    
    int demo_release(struct inode * inodep, struct file * filep)  // 关闭设备
    {
        printk("%s,%d
    ", __func__, __LINE__);
        return 0;
    }
    
    struct file_operations  fops =
    {
        .owner = THIS_MODULE,
        .open = demo_open,
        .release = demo_release,
    };
    
    static int __init demo_init(void)
    {
        int ret = 0;
        dev_t  devno;
        
        printk("%s,%d
    ", __func__, __LINE__);
    
        //使用下列宏则可以通过主设备号和次设备号生成 dev_t
        devno = MKDEV(demo_major, demo_minor);
    
        printk("devno:%d
    ", devno);
        printk("demo_major:%d
    ", demo_major);
    
        /**在调用 cdev_add()函数向系统注册字符设备之前,
          *应首先调用 register_chrdev_region()或alloc_chrdev_region()函数向系统申请设备号
          **/
        if (demo_major)//静态申请
        {
            ret = register_chrdev_region(devno, 1, "demo");
        }
        else //动态分配
        {
            ret = alloc_chrdev_region(&devno, 0, 1, "demo");
        }
    
        if(ret)
        {
            printk("Failed to register_chrdev_region.
    ");
            return ret;
        }
    
        //cdev_init()函数用于初始化 cdev 的成员,并建立 cdev 和 file_operations 之间的连接
        cdev_init(&cdev, &fops);
        cdev.owner = THIS_MODULE;
    
        //系统添加一个 cdev,完成字符设备的注册。
        ret = cdev_add(&cdev, devno, demo_count);
        if(ret)
        {
            printk(KERN_NOTICE " Failed to cdev_add [Error] %d adding demo%d", ret, demo_count);
            unregister_chrdev_region(devno, demo_count);
            return ret;
        }
    
        return 0;
    }
    
    static void __exit demo_exit(void)
    {
        printk("%s,%d
    ", __func__, __LINE__);
        //删除一个 cdev,完成字符设备的注销。
        cdev_del(&cdev);
        //在调用cdev_del()函数从系统注销字符设备之后,unregister_chrdev_region()应该被调用以释放原先申请的设备号
        unregister_chrdev_region( MKDEV(demo_major, demo_minor), demo_count );
    }
    
    module_init(demo_init);
    module_exit(demo_exit);
    
    MODULE_AUTHOR(" libra13179 ");
    MODULE_LICENSE("GPL v2");

     Makefile

    VERS = $(shell uname -r)
    
    # Kernel modules
    obj-m += demo.o
    
    # Specify flags for the module compilation.
    #EXTRA_CFLAGS=-g -O0
    
    build: kernel_modules
    
    kernel_modules:
        make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
    
    clean:
        make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean

     make测试一下

     使用dmesg指令来查看

    使用 cat /proc/devices看到demo的信息

     下面截图来自https://blog.csdn.net/u012142460/article/details/78932165

    现在主要介绍demo.c中使用到函数和宏,结构体等

    <linux/cdev.h>

    使用cdev结构体描述一个字符设备

    1 struct cdev
    2 {
    3     struct kobject kobj; /* 内嵌的 kobject 对象 */
    4     struct module *owner;    /*所属模块*/
    5     struct file_operations *ops;  /*文件操作结构体*/
    6     struct list_head list;
    7     dev_t dev;           /*设备号*/
    8     unsigned int count;
    9 };
    cdev结构体

    cdev 结构体的 dev_t 成员定义了设备号,为 32 位,其中 12 位主设备号,20 位次设备号。

    比较用到三个宏

    <linux/kdev_t.h>

    #define MAJOR(dev)    ((unsigned int) ((dev) >> MINORBITS))
    #define MINOR(dev)    ((unsigned int) ((dev) & MINORMASK))
    #define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))

    用下列宏可以从 dev_t 获得主设备号和次设备号:
    MAJOR(dev_t dev)
    MINOR(dev_t dev)
    使用下列宏则可以通过主设备号和次设备号生成 dev_t:
    MKDEV(int major, int minor)

    <linux/cdev.h>

    void cdev_init(struct cdev *, const struct file_operations *);//初始化cdev的成员,并建立cdev和file_operations之间关联起来 
    
    struct cdev *cdev_alloc(void);//动态申请(构造)cdev内存(设备对象)
    
    void cdev_put(struct cdev *p);//释放cdev内存
    
    int cdev_add(struct cdev *, dev_t, unsigned);//注册cdev设备对象(添加到系统字符设备列表中)
    
    void cdev_del(struct cdev *);//将cdev对象从系统中移除(注销 )
    

    /////////////////////////////////////////////////////////切割线//////////////////////////////////////////////////////////////////////////////

    我们增加一个测试

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdio.h>
    
    
    int main(int argc, const char *argv[])
    {
        int fd;
        int val = 1;
        fd = open("/dev/xyz", O_RDWR);
        if (fd < 0)
        {
            printf("can't open!
    ");
            return -1;
        }
        else
        {
            printf("open success.
    ");
        }
     
     
        getchar();
     
     
        close(fd);
    
        return 0;
    }
    root@lin-virtual-machine:/home/lin/demo# gcc -o demodrvtest demodrvtest.c
    root@lin-virtual-machine:/home/lin/demo# ls
    demo~    demodrvtest    demo.mod.c  Makefile~       Untitled Document~
    demo.c   demodrvtest.c  demo.mod.o  modules.order
    demo.c~  demo.ko        demo.o      Module.symvers
    root@lin-virtual-machine:/home/lin/demo# pwd
    /home/lin/demo
    root@lin-virtual-machine:/home/lin/demo# ./demodrvtest
    can't open!
    root@lin-virtual-machine:/home/lin/demo# mknod /dev/xyz c 250 0
    root@lin-virtual-machine:/home/lin/demo# ./demodrvtest
    open success.

    这边使用手动来创建

    手动创建设备 mknod命令

    命令的格式是:mknod  设备名   设备类型(字符:c,块:b)  主设备号 从设备号

  • 相关阅读:
    关于MySQL数据库中null的那些事
    Java集合之Collections 剖析
    字符串类
    C++标准库
    << 操作符
    操作符的重载
    类中的重载
    友元
    二阶构造模式
    静态成员函数
  • 原文地址:https://www.cnblogs.com/libra13179/p/9367044.html
Copyright © 2020-2023  润新知