In continuation of the previous text第三章:字符设备驱动-8:Device Registration in scull, let's GO ahead.
Now that we’ve taken a quick look at the fields, we start using them in real scull functions.
在快速了解了相关字段后,我们开始在实际的 scull 函数中使用它们。
The open Method
The open method is provided for a driver to do any initialization in preparation for later operations. In most drivers, open should perform the following tasks:
• Check for device-specific errors (such as device-not-ready or similar hardwareproblems)
• Initialize the device if it is being opened for the first time
• Update the f_op pointer, if necessary
• Allocate and fill any data structure to be put in filp->private_data
open 方法用于驱动在后续操作前执行初始化准备工作。大多数驱动中,open 应完成以下任务:
-
检查设备特定错误(如设备未就绪或类似硬件问题)
-
若设备是首次被打开,对其进行初始化
-
必要时更新
f_op指针 -
分配并填充要存入
filp->private_data的数据结构
The first order of business, however, is usually to identify which device is being opened. Remember that the prototype for the open method is:
不过,首要任务通常是确定正在打开的设备。回忆一下,open 方法的原型是:
int (*open)(struct inode *inode, struct file *filp);
The inode argument has the information we need in the form of its i_cdev field, which contains the cdev structure we set up before. The only problem is that we do not normally want the cdev structure itself, we want the scull_dev structure that contains that cdev structure. The C language lets programmers play all sorts of tricks to make that kind of conversion; programming such tricks is error prone, however, and leads to code that is difficult for others to read and understand. Fortunately, in this case, the kernel hackers have done the tricky stuff for us, in the form of the
container_of macro, defined in <linux/kernel.h>:
inode 参数包含我们需要的信息 —— 其 i_cdev 字段存储了我们之前设置的 cdev 结构。但问题是,我们通常不需要 cdev 结构本身,而是需要包含该 cdev 结构的 scull_dev 结构。
C 语言允许程序员使用各种技巧实现这种转换,但这样做容易出错,且会导致代码难以被他人阅读和理解。幸运的是,内核开发者已经通过 <linux/kernel.h> 中定义的 container_of 宏为我们完成了这项复杂工作:
container_of(pointer, container_type, container_field);
This macro takes a pointer to a field of type container_field, within a structure of type container_type, and returns a pointer to the containing structure. In scull_open, this macro is used to find the appropriate device structure:
struct scull_dev *dev; /* device information */
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev; /* for other methods */
Once it has found the scull_dev structure, scull stores a pointer to it in the private_data field of the file structure for easier access in the future.
找到 scull_dev 结构后,scull 驱动会将其指针存储在 file 结构的 private_data 字段中,以便后续操作更便捷地访问设备。
补充说明:
-
container_of宏的作用与用法(驱动开发中最常用的宏,没有之一)该宏用于通过 “结构体成员的指针” 反推 “包含该成员的结构体的指针”,参数含义:
-
pointer:结构体成员的指针(如&inode->i_cdev) -
container_type:包含该成员的结构体类型(如struct scull_dev) -
container_field:成员在结构体中的名称(如cdev)
-
-
private_data字段的关键作用(驱动开发中最常用的一个指针,没有之一)filp->private_data是驱动跨方法传递数据的核心载体:-
避免了全局变量的使用,使驱动可同时管理多个设备实例(如
scull0、scull1等)。 -
在
open中存入设备实例指针后,read/write/release等方法可直接通过filp->private_data获取设备实例,无需重复通过inode计算;
-
The other way to identify the device being opened is to look at the minor number stored in the inode structure. If you register your device with register_chrdev, you must use this technique. Be sure to use iminor to obtain the minor number from the inode structure, and make sure that it corresponds to a device that your driver is actually prepared to handle. The (slightly simplified) code for scull_open is:
识别正在打开的设备的另一种方法是查看 inode 结构中存储的次设备号。如果你使用 register_chrdev 注册设备,就必须采用这种方式。务必使用 iminor 宏从 inode 结构中获取次设备号,并确保它对应于你的驱动实际准备处理的设备。(经过简化的)scull_open 代码如下:
int scull_open(struct inode *inode, struct file *filp)
{
struct scull_dev *dev; /* device information */
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev; /* for other methods */
/* now trim to 0 the length of the device if open was write-only */
if ( (filp->f_flags & O_ACCMODE) = = O_WRONLY) {
scull_trim(dev); /* ignore errors */
}
return 0; /* success */
}
The code looks pretty sparse, because it doesn’t do any particular device handling when open is called. It doesn’t need to, because the scull device is global and persistent by design. Specifically, there’s no action such as “initializing the device on first open,” because we don’t keep an open count for sculls.
这段代码看起来相当简洁,因为在调用 open 时它没有执行任何特定的设备处理操作。这是因为 scull 设备在设计上是全局且持久的,所以不需要这样做。具体来说,这里没有 “首次打开时初始化设备” 之类的操作,因为我们没有为 scull 设备维护打开计数。
The only real operation performed on the device is truncating it to a length of 0 when the device is opened for writing. This is performed because, by design, overwriting a scull device with a shorter file results in a shorter device data area. This is similar to the way opening a regular file for writing truncates it to zero length. The operation does nothing if the device is opened for reading. We’ll see later how a real initialization works when we look at the code for the other scull personalities.
对设备执行的唯一实际操作是:当设备以写方式打开时,将其长度截断为 0。这样设计是因为,用较短的文件覆盖 scull 设备时,会导致设备数据区域变短。这与以写方式打开普通文件时将其截断为 0 长度的行为类似。如果设备是以读方式打开的,此操作不会执行任何动作。稍后,当我们查看其他 scull 变体的代码时,会了解真正的初始化是如何工作的。
技术交流,欢迎加入社区:GPUers。

被折叠的 条评论
为什么被折叠?



