第三章:字符设备驱动-9:open and release--open

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 字段中,以便后续操作更便捷地访问设备。

补充说明:

  1. container_of 宏的作用与用法(驱动开发中最常用的宏,没有之一)

    该宏用于通过 “结构体成员的指针” 反推 “包含该成员的结构体的指针”,参数含义:

    1. pointer:结构体成员的指针(如 &inode->i_cdev

    2. container_type:包含该成员的结构体类型(如 struct scull_dev

    3. container_field:成员在结构体中的名称(如 cdev

  2. private_data 字段的关键作用(驱动开发中最常用的一个指针,没有之一)

    filp->private_data 是驱动跨方法传递数据的核心载体:

    1. 避免了全局变量的使用,使驱动可同时管理多个设备实例(如 scull0scull1 等)。

    2. 在 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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeeplyMind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值