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;
}

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

  • 该函数默认字符设备次设备号为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);

运行结果

  实验过程请参考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()

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

你可能感兴趣的:(Linux驱动开发,linux,驱动开发,运维)