Driver porting: Devices and attributes

 转载自: http://lwn.net/Articles/31220/

When driver authors want to work with the kernel device model, they are probably wanting to (1) ensure that their devices are represented in the system hierarchy, or (2) set up some custom attributes in sysfs. To meet those needs, this article will look at device and attribute registration. These tasks represent only part of what the device model is about, but they are a good starting place.

Some time ago, this series looked at a simple block driver . That driver will now be augmented with simple driver model and sysfs support. The relevant bits of code will be shown below; the full source is available here .

 

The device structure

Once upon a time, struct device referred to a network interface. That structure has long since been renamed net_device, and, in 2.5, struct device became the "base class" for representing all devices in the system. The full structure can be seen in <linux/device.h>; for most drivers, however, there are only a few fields that are worth worrying about:

 

char name [DEVICE_NAME_SIZE];
char bus_id [BUS_ID_SIZE];
void *driver_data ;
struct device_driver *driver ;

 

name:      is a descriptive name (not something found in /dev; it can be something like "Barmatic VX773 Frobnicator").

bus_id:    describes where the device can be found on the bus; for PCI devices it is a string like "00:09.0".

driver_data:     The driver can put anything it wants into the driver_data field.

driver:    describes the driver for this device; we'll get there shortly.


As a general rule, your driver will want to remember more about a device than can be represented in struct device; this structure just exists to hold the data common to all devices in the system. So drivers do not normally deal with bare device structures; instead, this structure is embedded within something larger. Thus, if you look at the definition of, say, struct pci_dev or struct usb_device, you'll find a struct device lurking within.


A general rule has been adopted that struct device is not the first field in any other structure which contains it. The idea is that programmers should think carefully about which structure they are dealing with at any given time, and not just cast pointers back and forth. Going from the larger structure to struct device is just a matter of referencing the appropriate field. To go the other way, the container_of() macro should be used. Thus, for example, a USB driver could turn a struct device pointer (called, say, devp) into a struct usb_device pointer with:

 

container_of(devp, struct usb_device, dev)

Here, "dev" is the name of the struct usb_device field containing the device structure. Normally, a bus subsystem will define a macro for doing this conversion; in the USB case, it is to_usb_device().


The example simple block device (SBD) does not attach to a physical bus, of course. For this type of device, the kernel exports a "system" bus which may be used for virtual attachments. Usually, the system bus contains devices like the processor, interrupt controller, and timer. But we can attach virtual disks there too.

System bus devices are represented by struct sys_device, defined as:


 

struct sys_device {
    char *name;
    u32 id;
    struct device dev;
};

(A couple of fields have been omitted). Here, the name should be a /dev-style name - it will be used (along with the id value) to create the device's entry in sysfs under devices/sys.


The simple block device will be represented as a sys_device. Before we get to that, however, there is another structure that deserves a look.

 

The device driver structure

Device drivers, too, are represented in the device model, and in sysfs. The relevant structure (again, with a few fields omitted) looks like:

 

struct device_driver {
    char *name;
    struct bus_type *bus;
    int (*probe) (struct device * dev);
    int (*remove) (struct device * dev);
    void (*shutdown) (struct device * dev);
    int (*suspend) (struct device * dev, u32 state, u32 level);
    int (*resume) (struct device * dev, u32 level);
};

The name this time around is the name of the driver, of course. The bus field is normally filled in by the bus-layer logic; drivers need not worry about initializing it. The various methods provided in the structure are for the handling of device discovery and power management tasks. Usually, again, these methods are provided at the bus level, with bus-specific calls down into the driver itself.


Time to look at some code. The SBD driver sets up its structure as:


 

static struct device_driver sbd_driver = {
    .name = "sbd",
};

A driver for a real device may, at the least, want to add methods for suspend and resume events. There is nothing in particular that SBD needs to do in response to such events, however, so no such methods have been provided.

In the SBD module init code, the driver structure is registered with:


 

driver_register(&sbd_driver);

 

It is also necessary to call driver_unregister() in the shutdown code, of course.


This call is sufficent to create the directory bus/system/sbd in the sysfs hierarchy. As SBD devices are registered, they will appear as symbolic links in that directory. There will be no other data there, at least not yet.

 

Driver attributes

Suppose we wanted to put something else in the driver sysfs directory? That can be done through the creation of driver attributes . The SBD driver adds a file called version which contains the version of the driver code; user-space scripts could query that file to get a sense for what capabilities might or might not be available.

Each driver attribute requires a name, file permissions, and functions to format and set the value of the attribute. Generally the functions are defined first. The SBD function to display the version is:

 


static ssize_t show_version(struct device_driver *drv, char *buf)
{
    strcpy(buf, Version);
    return strlen(Version) + 1;
}

The buffer passed into the show function is one full page, so there's plenty of room. In general, however, values for sysfs attributes should be short. The convention is that an attribute contains a single value - not pages of information as can be found in some /proc files.


The store function has the prototype: 

 

 

ssize_t (*store)(struct device driver *drv, const char *buf, size_t count);

The return value is the number of bytes consumed by the operation (usually count); it can also be one of the usual error codes. Since there is little point in changing the driver's version string from user space, SBD provides no store function.


Creating attributes requires filling in a driver_attribute structure. This is usually done with the DRIVER_ATTR macro:


 

DRIVER_ATTR(name, mode, show, store);

 

In the case of the SBD driver, the relevant declaration is:


 

static DRIVER_ATTR(version, S_IRUGO, show_version, NULL);

This line creates a structure called driver_attr_version; it will ultimately create a file called version in the driver's sysfs directory. That file will have read-only permissions, and will call show_version() when read.

 

Actually creating the file, however, requires one more step. This line appears in the module initialization code, immediately after the call to driver_register():


 

driver_create_file(&sbd_driver, &driver_attr_version);

There is a driver_remove_file(), but normally it is unnecessary to call it - the files will be removed automatically when the driver is unregistered.

 

Device registration

Now that we are done looking at driver registration, we can get around to creating our device. SBD is a "system bus" device; the bus-specific device structure is created as:

 

static struct sys_device sbd_sys_device = {
    .name = "sbd",
    .dev = { /* struct device stuff */
        .name = "Simple block device",
        .driver = &sbd_driver
    },
};

The id field defaults to zero, so this device will eventually be sbd0. Note the assignment of the dev.driver field, which connects the device with the driver that handles it.


At initialization time, the device is registered with:


 

sys_device_register(&sbd_sys_device);

 

sys_device_register() is a wrapper around device_register() which handles "system bus" details. Once this call has been made, the sysfs directory device/sys/sbd0 is created. Two attributes exist there: name contains "Simple block device", and power contains the device's current power state. Most importantly, however, the device exists within the device model data structure, where it can respond to hotplug and power management events.


Devices, too, can have custom attributes. For SBD, an attribute device contains the device number assigned to the virtual disk; this value could be used, for example, to create a /dev entry automatically in user space. The implementation is very similar to the driver attribute we set up before:


 

static ssize_t show_devnum(struct device *dev, char *buf)
{
    return sprintf(buf, "%02x00", major_num);
}
DEVICE_ATTR(device, S_IRUGO, show_devnum, 0);
...
device_create_file(&sbd_sys_device.dev, &dev_attr_device);

One final step, specific to block devices, is taken in SBD. Before the virtual disk's gendisk structure is registered with add_disk(), a pointer to the device structure is stored:


 

Device.gd->driverfs_dev = &sbd_sys_device.dev;

This assignment causes a couple of extra symbolic links to be created in sysfs; devices/sys/sbd0/block points to block/sbd0, and block/sbd0/device points back to devices/sys/sbd0. In this way, the relationship between the various entries is made explicit.

 

Going further

This article barely touches on the device model interface. Many details have necessarily been omitted; many of them will be topics for future articles. The next article in the series, which will appear soon (promise) will look at the class interface. Power management also deserves a look, but that interface remains in flux as of this writing. Expect an article when the dust settles a bit.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于PyTorch的Embedding和LSTM的自动写诗实验LSTM (Long Short-Term Memory) 是一种特殊的循环神经网络(RNN)架构,用于处理具有长期依赖关系的序列数据。传统的RNN在处理长序列时往往会遇到梯度消失或梯度爆炸的问题,导致无法有效地捕捉长期依赖。LSTM通过引入门控机制(Gating Mechanism)和记忆单元(Memory Cell)来克服这些问题。 以下是LSTM的基本结构和主要组件: 记忆单元(Memory Cell):记忆单元是LSTM的核心,用于存储长期信息。它像一个传送带一样,在整个链上运行,只有一些小的线性交互。信息很容易地在其上保持不变。 输入门(Input Gate):输入门决定了哪些新的信息会被加入到记忆单元中。它由当前时刻的输入和上一时刻的隐藏状态共同决定。 遗忘门(Forget Gate):遗忘门决定了哪些信息会从记忆单元中被丢弃或遗忘。它也由当前时刻的输入和上一时刻的隐藏状态共同决定。 输出门(Output Gate):输出门决定了哪些信息会从记忆单元中输出到当前时刻的隐藏状态中。同样地,它也由当前时刻的输入和上一时刻的隐藏状态共同决定。 LSTM的计算过程可以大致描述为: 通过遗忘门决定从记忆单元中丢弃哪些信息。 通过输入门决定哪些新的信息会被加入到记忆单元中。 更新记忆单元的状态。 通过输出门决定哪些信息会从记忆单元中输出到当前时刻的隐藏状态中。 由于LSTM能够有效地处理长期依赖关系,它在许多序列建模任务中都取得了很好的效果,如语音识别、文本生成、机器翻译、时序预测等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值