Device Registration in scull

Device Registration in scull

Internally, scull represents each device with a structure of type struct scull_dev. This structure is defined as: 在内部,scull 用 struct scull_dev 类型的结构表示每个设备。 该结构定义为:

struct scull_dev {

    struct scull_qset *data;  /* Pointer to first quantum set */

    int quantum;              /* the current quantum size */

    int qset;                 /* the current array size */

    unsigned long size;       /* amount of data stored here */

    unsigned int access_key;  /* used by sculluid and scullpriv */

    struct semaphore sem;     /* mutual exclusion semaphore     */

    struct cdev cdev;     /* Char device structure      */

};

We discuss the various fields in this structure as we come to them, but for now, we call attention to cdev, the struct cdev that interfaces our device to the kernel. This structure must be initialized and added to the system as described above; the scull code that handles this task is: 我们讨论这个结构中的各个字段,但现在,我们要注意 cdev,它是我们的设备与内核接口的结构 cdev。 该结构必须按照上述方式初始化并添加到系统中; 处理此任务的 scull 代码是:

static void scull_setup_cdev(struct scull_dev *dev, int index)

{

    int err, devno = MKDEV(scull_major, scull_minor + index);

   

    cdev_init(&dev->cdev, &scull_fops);

    dev->cdev.owner = THIS_MODULE;

    dev->cdev.ops = &scull_fops;

    err = cdev_add (&dev->cdev, devno, 1);

    /* Fail gracefully if need be */

    if (err)

    printk(KERN_NOTICE "Error %d adding scull%d", err, index);

}

Since the cdev structure is embedded within struct scull_dev, cdev_init must be called to perform the initialization of that structure. 由于 cdev 结构嵌入在 struct scull_dev 中,因此必须调用 cdev_init 来执行该结构的初始化。

The Older Way

If you dig through much driver code in the 2.6 kernel, you may notice that quite a few char drivers do not use the cdev interface that we have just described. What you are seeing is older code that has not yet been upgraded to the 2.6 interface. Since that code works as it is, this upgrade may not happen for a long time. For completeness, we describe the older char device registration interface, but new code should not use it; this mechanism will likely go away in a future kernel. 如果您仔细研究 2.6 内核中的大量驱动程序代码,您可能会注意到相当多的 char 驱动程序不使用我们刚刚描述的 cdev 接口。 您看到的是尚未升级到 2.6 接口的旧代码。 由于该代码按原样运行,因此此升级可能不会发生很长时间。 为了完整起见,我们描述了旧的 char 设备注册接口,但新代码不应该使用它; 这种机制可能会在未来的内核中消失。

The classic way to register a char device driver is with: 注册 char 设备驱动程序的经典方法是:

int register_chrdev(unsigned int major, const char *name,

                    struct file_operations *fops);

Here, major is the major number of interest, name is the name of the driver (it appears in /proc/devices), and fops is the default file_operations structure. A call to register_chrdev registers minor numbers 0-255 for the given major, and sets up a default cdev structure for each. Drivers using this interface must be prepared to handle open calls on all 256 minor numbers (whether they correspond to real devices or not), and they cannot use major or minor numbers greater than 255.

If you use register_chrdev, the proper function to remove your device(s) from the system is: 这里,major 是感兴趣的主设备号,name 是驱动程序的名称(它出现在 /proc/devices 中),fops 是默认的 file_operations 结构。 对 register_chrdev 的调用会为给定的主要注册次要编号 0-255,并为每个次要设置一个默认的 cdev 结构。 使用此接口的驱动程序必须准备好处理所有 256 个次要编号(无论它们是否对应于真实设备)上的打开调用,并且不能使用大于 255 的主要或次要编号。

int unregister_chrdev(unsigned int major, const char *name);

major and name must be the same as those passed to register_chrdev, or the call will fail. major 和 name 必须与传递给 register_chrdev 的相同,否则调用将失败。

open and release

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: 为驱动程序提供了 open 方法来进行任何初始化,为以后的操作做准备。 在大多数驱动程序中,open 应该执行以下任务:

Check for device-specific errors (such as device-not-ready or similar hardware problems) 检查特定于设备的错误(例如设备未就绪或类似的硬件问题)

Initialize the device if it is being opened for the first time如果设备是第一次打开,则初始化设备

Update the f_op pointer, if necessary如有必要,更新 f_op 指针

Allocate and fill any data structure to be put in filp->private_data分配并填充要放入 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: 此宏在 container_type 类型的结构中获取指向 container_field 类型字段的指针,并返回指向包含结构的指针。 在 scull_open 中,此宏用于查找合适的设备结构:

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 就会在文件结构的 private_data 字段中存储一个指向它的指针,以便将来访问。

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. 识别正在打开的设备的另一种方法是查看存储在 inode 结构中的次要编号。 如果您使用 register_chrdev 注册您的设备,则必须使用此技术。 请务必使用 iminor 从 inode 结构中获取次要编号,并确保它对应于您的驱动程序实际准备处理的设备。

The (slightly simplified) code for scull_open is: 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. 在设备上执行的唯一实际操作是在打开设备进行写入时将其截断为长度 0。 这是因为,按照设计,用更短的文件覆盖 scull 设备会导致更短的设备数据区域。 这类似于打开常规文件进行写入将其截断为零长度的方式。 如果打开设备进行读取,则该操作不执行任何操作。

We'll see later how a real initialization works when we look at the code for the other scull personalities. 稍后当我们查看其他scull的独特代码时,我们将看到真正的初始化是如何工作的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

mounter625

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

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

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

打赏作者

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

抵扣说明:

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

余额充值