我在人间凑数的一天下午——Linux字符设备驱动函数(一)
字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的。比如我们最常见的点灯、按键、IIC、SPI,LCD 等等都是字符设备,这些设备的驱动就叫做字符设备驱动。
Linux 应用程序如何调用驱动的
module_init 函数的实现
当bootloader加载完kernel并解压并放置与内存中准备开始运行,首先被调用的函数是start_kernel
内核的加载的时候,会搜索".initcall"中的所有条目,并按优先级加载它们,普通驱动程序的优先级是6
tart_kernel()–>rest_init()–>kernel_init()–>do_basic_setup()–>do_initcalls()
我们使用该函数时:
module_init(fn)—> __initcall(fn) —> device_initcall(fn) —> __define_initcall(fn, 6)
register_chrdev 函数
核心:
注册一个file_operations结构体
得到返回值:主设备号
函数原型:
register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
第一个参数:主设备号,一般使用0,让系统分配分配。
第二个参数:设备名称
第三个参数:一个file_operations结构体
查看Linux源码分析函数:
register_chrdev 由 __register_chrdev实现
__register_chrdev中有这么一行代码:
cd = __register_chrdev_region(major, baseminor, count, name);
进入到 __register_chrdev_region 中
__register_chrdev_region函数:
cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
返回一个没有分配的char_device_struct 结构体。
设置相关信息
cd->major = major;
cd->baseminor = baseminor;
cd->minorct = minorct;
strlcpy(cd->name, name, sizeof(cd->name));
i = major_to_index(major);
( i= return major % CHRDEV_MAJOR_HASH_SIZE;)
cdev = cdev_alloc();
cdev->owner = fops->owner; cdev->ops = fops; kobject_set_name(&cdev->kobj, “%s”, name); err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
class_creat和device_create
Linux源码中是这样解释的
class_create
create a struct class structure
This is used to create a struct class pointer that can then be used
- in calls to device_create().
- Returns &struct class pointer on success, or ERR_PTR() on error.
函数使用:
hello_class = class_create(THIS_MODULE, “hello_class”);
第一个参数:指定类的所有者是哪个模块。
第二个参数:类名。
device_create
creates a device and registers it with sysfs
- This function can be used by char device classes. A struct device
- will be created in sysfs, registered to the specified class.
函数使用:
evice_create(hello_class, NULL, MKDEV(major, 0), NULL, “hello”);
第一个参数:指定所要创建的设备所从属的类。
第二个参数:设备的父设备,如果没有就指定为NULL
第三个参数:设备号(主、次)。
第四个参数:设备中以进行回调的数据。这里为NULL。
第五个参数:设备名称的字符串
总结:
1、内核提供了class_create函数,用它来创建一个类,这个类存放于sysfs下面。
2、调用device_create(…)函数来在/dev目录下创建相应的设备节点。
3、加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
file_operations结构体:
结构的定义详细看Linux源码
它的每一个成员函数一般都对应一个系统调用
实现open read write 等函数的
static struct file_operations my_drv = {
.owner = THIS_MODULE,
.open = my_drv_open,
.read = my_drv_read,
.write = my_drv_write,
}
my_drv_open(), my_drv_read(), my_drv_write(),三个函数的参数,要符合file_operations结构体中的定义,如何实现函数的功能。
MODULE_LICENSE(“GPL”);
声明遵从GPL许可证,否则编译时,内核会报错。
“GPL” 是指明了 这是GNU General Public License的任意版本
2020/6/11日 17:30