• 6、Linux驱动开发:设备-更简单的设备注册


    目录

    🍅点击这里查看所有博文

      随着自己工作的进行,接触到的技术栈也越来越多。给我一个很直观的感受就是,某一项技术/经验在刚开始接触的时候都记得很清楚。往往过了几个月都会忘记的差不多了,只有经常会用到的东西才有可能真正记下来。存在很多在特殊情况下有一点用处的技巧,用的不多的技巧可能一个星期就忘了。

      想了很久想通过一些手段把这些事情记录下来。也尝试过在书上记笔记,这也只是一时的,书不在手边的时候那些笔记就和没记一样,不是很方便。

      很多时候我们遇到了问题,一般情况下都是选择在搜索引擎检索相关内容,这样来的也更快一点,除非真的找不到才会去选择翻书。后来就想到了写博客,博客作为自己的一个笔记平台倒是挺合适的。随时可以查阅,不用随身携带。

      同时由于写博客是对外的,既然是对外的就不能随便写,任何人都可以看到。经验对于我来说那就只是经验而已,公布出来说不一定我的一些经验可以帮助到其他的人。遇到和我相同问题时可以少走一些弯路。

      既然决定了要写博客,那就只能认真去写。不管写的好不好,尽力就行。千里之行始于足下,一步一个脚印,慢慢来 ,写的多了慢慢也会变好的。权当是记录自己的成长的一个过程,等到以后再往回看时,就会发现自己以前原来这么菜😂。

      本系列博客所述资料均来自互联网资料,并不是本人原创(只有博客是自己写的)。出于热心,本人将自己的所学笔记整理并推出相对应的使用教程,方面其他人学习。为国内的物联网事业发展尽自己的一份绵薄之力,没有为自己谋取私利的想法。若出现侵权现象,请告知本人,本人会立即停止更新,并删除相应的文章和代码。

    更简单的方式

      通过前面的学习,我们也能发现一些规律,注册一个字符设备需要按照一定的步骤去分布执行。

      尤其是cdev的注册步骤基本是固定的,这些固定的步骤,其实在内核中也给我们提供了一个专用的函数register_chrdev。该函数可以极大的简化字符设备注册的步骤。

    static inline int register_chrdev(unsigned int major, const char *name,
    				  const struct file_operations *fops)
    {
    	return __register_chrdev(major, 0, 256, name, fops);
    }
    /**
     * __register_chrdev() - create and register a cdev occupying a range of minors
     * @major: major device number or 0 for dynamic allocation
     * @baseminor: first of the requested range of minor numbers
     * @count: the number of minor numbers required
     * @name: name of this range of devices
     * @fops: file operations associated with this devices
     *
     * If @major == 0 this functions will dynamically allocate a major and return
     * its number.
     *
     * If @major > 0 this function will attempt to reserve a device with the given
     * major number and will return zero on success.
     *
     * Returns a -ve errno on failure.
     *
     * The name of this device has nothing to do with the name of the device in
     * /dev. It only helps to keep track of the different owners of devices. If
     * your module name has only one type of devices it's ok to use e.g. the name
     * of the module here.
     */
    int __register_chrdev(unsigned int major, unsigned int baseminor,
    		      unsigned int count, const char *name,
    		      const struct file_operations *fops)
    {
    	struct char_device_struct *cd;
    	struct cdev *cdev;
    	int err = -ENOMEM;
    
    	cd = __register_chrdev_region(major, baseminor, count, name);
    	if (IS_ERR(cd))
    		return PTR_ERR(cd);
    
    	cdev = cdev_alloc();
    	if (!cdev)
    		goto out2;
    
    	cdev->owner = fops->owner;
    	cdev->ops = fops;
    	kobject_set_name(&cdev->kobj, "%s", name);
    
    	err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
    	if (err)
    		goto out;
    
    	cd->cdev = cdev;
    
    	return major ? 0 : cd->major;
    out:
    	kobject_put(&cdev->kobj);
    out2:
    	kfree(__unregister_chrdev_region(cd->major, baseminor, count));
    	return err;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

      通过函数的实现可以看出,该函数内部的实现和我们之前学习到的注册步骤几乎一致。相比之前学习到的注册方法,该函数还具有如下特性。

    • 该函数默认字符设备次设备号为0,且不可修改
    • 该函数默认创建256个次设备
    • 该函数若传入的主设备号为0,则会自动分配一个未使用的主设备号

    示例代码

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    unsigned int major = 237;
    int hello_open (struct inode *inode, struct file *file)
    {
    	printk("hello_open()\n");
    	return 0;
    }
    int hello_release (struct inode *inode, struct file *file)
    {
    	printk("hello_release()\n");
    	return 0;
    }
    struct file_operations fops ={
    	.open = hello_open,
    	.release = hello_release,
    };
    static int hello_init(void)
    {
    	int ret;
    	printk("hello_init()\n");
    	ret = register_chrdev(major, "hello", &fops);
    	if(ret<0)
    	{
    		printk("register_chrdev fail\n");
    		return ret;
    	}
    	return 0;	
    }
    static void hello_exit(void)
    {
    	printk("hello_exit()\n");
    	unregister_chrdev(major, "hello");
    	return ;
    }
    MODULE_LICENSE("GPL");
    module_init(hello_init);
    module_exit(hello_exit);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    运行结果

      实验过程请参考5.设备-设备注册章节。

    root@ubuntu:/# gcc ./test.c -o test
    root@ubuntu:/# ./test 
    open ok 
    close ok 
    root@ubuntu:/# dmesg
    [66019.123506] hello_open()
    [66019.123571] hello_release()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

      那么本篇博客就到此结束了,这里只是记录了一些我个人的学习笔记,其中存在大量我自己的理解。文中所述不一定是完全正确的,可能有的地方我自己也理解错了。如果有些错的地方,欢迎大家批评指正。如有问题直接在对应的博客评论区指出即可,不需要私聊我。我们交流的内容留下来也有助于其他人查看,说不一定也有其他人遇到了同样的问题呢😂。

  • 相关阅读:
    Java数组基础之高效存储数据
    无人机数据链技术,无人机数据链路系统技术详解,无人机数传技术
    【机器学习项目实战10例】(一):利用线性回归实现股票预测分析
    学计算机必参加的含金量赛事!
    android View和ViewGroup创建以及绘制流程
    【测试功能篇 01】Jmeter 压测接口最大并发量、吞吐量、TPS
    《低代码发展白皮书(2022年)》&《2022低代码·无代码应用案例汇编》,发布了
    aspose-slides-22.5-jdk16
    学习.NET验证模块FluentValidation的基本用法
    828华为云征文 | 华为云Flexusx与Docker技术融合,打造个性化WizNote服务
  • 原文地址:https://blog.csdn.net/weixin_44570083/article/details/133907798