分享

Linux设备驱动模型

 lchjczw 2012-03-10

Udev
       先前所需的硬件文件节点都需要在/dev下静态创建,随2.4核而来的devfs,带来了动态设备节点创建。Devfs虽然提供了in-memory filesystem创建节点的方法,但是节点命名依然取决于设备驱动。命名管理和内核结合的不好,The place for policy is in header files, kernel module parameters, or user space。Udev将设备管理交给用户空间。
      Udev依赖于以下技术:(1)、内核sysfs支持,sysfs启动时挂载到/sys下(可查看/etc/fstab);(2)、一系列用户空间工具:udevd和udevinfo;(3)、定义在/etc/udev/rules.d/目录下的用户定义规则

使用Udev:

1、首先从sysfs的相关文件中获取文件属性。假设DVD:/dev/sr0,CD-RW:/dev/sr1。使用udevinfo来收集设备信息。bash> udevinfo -a -p /sys/block/sr0

bash> udevinfo -a -p /sys/block/sr0
...
looking at the device chain at
'/sys/devices/pci0000:00/0000:00:1d.7/usb1/1-4':
 BUS=?usb?
 ID=?1-4?
 SYSFS{bConfigurationValue}=?1?
 ...
 SYSFS{idProduct}=?0701?
 SYSFS{idVendor}=?05e3?
 SYSFS{manufacturer}=?Genesyslogic?
 SYSFS{maxchild}=?0?
 SYSFS{product}=?USB Mass Storage Device?
 ...

bash> udevinfo -a -p /sys/block/sr1
 ...
 looking at the device chain at
 '/sys/devices/pci0000:00/0000:00:1d.7/usb1/1-3':
 BUS=?usb?
 ID=?1-3?
 SYSFS{bConfigurationValue}=?2?
  ...
 SYSFS{idProduct}=?0302?
 SYSFS{idVendor}=?0dbf?
 SYSFS{manufacturer}=?Addonics?
 SYSFS{maxchild}=?0?
 SYSFS{product}=?USB to IDE Cable?
 ...

2、使用收集的产品信息来辨别设备并加入udev命名规则,创建一个/etc/udev/rules.d/40-cdvd.rules并加入如下规则:
BUS="usb", SYSFS{idProduct}="0701", SYSFS{idVendor}="05e3",
KERNEL="sr[0-9]*", NAME="%k", SYMLINK="usbdvd"

BUS="usb", SYSFS{idProduct}="0302", SYSFS{idVendor}="0dbf",
KERNEL="sr[0-9]*", NAME="%k", SYMLINK="usbcdrw"
规则1的意思是:无论何时它发现一个产品id为0x0701,vender ID是0x05e3,而且名字以sr开头的usb设备,都会在/dev下创建一个同名的节点,并produce一个命名为usbdvd的符号链接给那个节点。
为了测试您规则中的语法错误,在/sys/block/sr*上运行udevtest。为了打开/var/log/messages的verbose messages,设置/etc/udev/udev.conf中的udev_log为"yes".为使新加入的规则在/dev下生效,使用udevstart重启udev,然后您的dvd驱动会标识为/dev/usbdvd。您可以使用mount /dev/usbdvd /mnt/dvd来挂载。
udev的其他功能:Linux热插拔管理、自动按需加载模块、下载微代码到需要的设备。
Sysfs, Kobjects,and Device Classes
一般用于bus和内核实现领域,隐藏在提供给设备驱动的API服务中。
sysfs是内核架构设备模型的用户空间实现,和procfs一样都是内存文件系统,包含了内核数据结构的相关信息。procfs是内核的一般性质的窗口,而sysfs则专注于设备模型。sysfs不是用于替代procfs,因为进程描述符信息和sysctl参数都属于procfs,而非sysfs。udev依赖于sysfs实现大部分功能。
kobjects常嵌入在大的结构体里面,主要包括kref(参数计数管理,kref_init初始化,kref_get()增加计数,kref_put()减少计数,并在计数为0的时候释放kobject),指向所属kset的指针,kobj_type(描述kobject类型);kobjects与sysfs密切相关,每个内核instantiated的kobject都有一个sysfs representation。
设备类是驱动中能用到的另一个接口,类接口抽象了认为每一个设备都会属于一个设备类中,如usb鼠标、PS/2键盘等属于输入类,而且在/sys/class/input/下拥有接口。
Figure 4.4. Sysfs hierarchy of a USB mouse.

[/sys]
     +[block]
     -[bus]—[usb]—[devices]—[usb2]—[2-2]—[2-2:1.0]-[usbendpoint:usbdev2.2-ep81]
     -[class]-[input]—[mouse2]—[device]—[bus]—[usbendpoint:usbdev2.2-ep81]
             -[usb_device]—[usbdev2.2]—[device]—[bus]
             -[usb_endpoint]—[usbdev2.2-ep00]—[device]
                            —[usbdev2.2-ep81]—[device]
     -[devices]—[pci0000:00]—[0000:00:1d:1]—[usb2]—[2-2]—[2-2:1.0]
     +[firmware]
     +[fs]
     +[kernel]
     +[module]
     +[power]

类编程接口建立在kobjects和sysfs之上,让我们来看一下RTC:

bash> modprobe rtc
bash> ls -lR /sys/class/misc
drwr-xr-x 2 root root 0 Jan 15 01:23 rtc
/sys/class/misc/rtc:
total 0
-r--r--r-- 1 root root 4096 Jan 15 01:23 dev
--w------- 1 root root 4096 Jan 15 01:23 uevent
bash> ls -l /dev/rtc
crw-r--r-- 1 root root 10, 135 Jan 15 01:23 /dev/rtc
/sys/class/misc/rtc/dev包含了设备的主次设备号,/sys/class/misc/rtc/uevent用于coldplugging,/dev/rtc被应用程序用于访问RTC驱动。Misc驱动使用misc_register()初始化,里面的代码和下面的类似:
/* ... */
dev = MKDEV(MISC_MAJOR, misc->minor);

misc->class = class_device_create(misc_class, NULL, dev,
                                  misc->dev,
                                  "%s", misc->name);
if (IS_ERR(misc->class)) {
  err = PTR_ERR(misc->class);
  goto out;
}
/* ... */
下图展示了在classes、kobjects、sysfs和udev之间的转换,最终导致了/sys和/dev里面的文件的产生。
另外一个好的抽象是bus-device-driver编程接口。以I2C为例,I2C核心层对监测到的每一个I2C总线适配器使用bus_register()注册,当I2C客户端设备如EEPROM被监测到时,由device_register()注册,最终I2C EEPROM客户端驱动程序使用driver_register()注册自身,这些注册直接使用I2C核提供的服务。bus_register()会在/sys/bus增加入口,device_register()/sys/devices下增加入口,struct bus_type,struct device,struct device_driver是主要使用的数据结构。
Hotplug和Coldplug
    在系统运行时动态接入的设备叫热插拔设备,而在系统启动前就事先接入的叫冷插拔设备。在以前内核使用在/proc中注册的辅助程序通知用户空间有设备接入,而现在当内核监测到热插拔时,它们使用netlink sockets向用户空间发送uevents。netlink是内核和用户空间通信的有效工具,在用户空间管理设备节点创建和删除的udevd接收到uevents并管理hotplug。以下是热插拔的发展历程:

To see how hotplug handling has evolved recently, let's consider progressive levels of udev running different versions of the 2.6 kernel:

  1. With a udev-039 package and a 2.6.9 kernel, when the kernel detects a hotplug event, it invokes the user space helper registered with /proc/sys/kernel/hotplug. This defaults to /sbin/hotplug, which receives attributes of the hotplugged device in its environment. /sbin/hotplug looks inside the hotplug configuration directory (usually /etc/hotplug.d/default/) and runs, for example, /etc/hotplug.d/default/10-udev.hotplug, after executing other scripts under /etc/hotplug/.

    bash> ls -l /etc/hotplug.d/default/
    ...
    lrwcrwxrwx 1 root root 14 May 11 2005 10-udev.hotplug -> /sbin/udevsend
    ...

    When /sbin/udevsend thus gets executed, it passes the hotplugged device information to udevd.

  2. With udev-058 and a 2.6.11 kernel, the story changes somewhat. The udevsend utility replaces /sbin/hotplug:

    bash> cat /proc/sys/kernel/hotplug
    /sbin/udevsend
  3. With the latest levels of udev and the kernel, udevd assumes full responsibility of managing hotplug without depending on udevsend. It now pulls hotplug events directly from the kernel via netlink sockets (see Figure 4.4). /proc/sys/kernel/hotplug contains nothing:

    bash> cat /proc/sys/kernel/hotplug
    bash>

Udev还可以处理coldplug,因为udev是用户空间程序而且在内核启动后才started,需要基于coldplug设备上模拟热插拔事件。在boot 时,内核在sysfs下创建名为uevent的文件for all devices,并且emits coldplug events to those files,当udev启动时,它读取/sys下面所有的uevent文件并为每个冷插拔设备产生热插拔事件。

Microcode Download

设备驱动used to把微代码存在头文件的static数组中,但是这不好,因为微代码常因为设备厂商的不同而是分布的as binary images,解决方案是在用户空间保存微代码,然后将它们在需要时传递给内核。Sysfs和udev提供了实现的架构。

以下是Intel PRO/Wireless 2100 WiFi mini PCI card 微代码下载的例子:在http://ipw2100./firmware.php下载并存放在您系统的/lib/firmware/里面,而且您已经插入了驱动模块ipw2100.ko:
1、在初始化时,驱动唤醒request_firmware(..,"ipw2100-1.3.fw",..);
2、这dispatch一个热插拔uevent to用户空间,along with the identity of the requested microcode image。
3、Udevd接收到uevent并responds by invoking /sbin/firmware_helper,对于这点,它使用如下在/etc/udev/rules.d/下面的类似的规则:ACTION=="add", SUBSYSTEM=="firmware", RUN="/sbin/firmware_helper"
4、/sbin/firmware_helper进入/lib/firmware/并且定位微代码Image ipw2100-1.3.fw。它dumps the image to /sys/class/0000:02:02.0/data(0000:02:02是PCI bus:device:function,是WiFi卡的标记);
5、驱动接收到微代码并且把它下载到设备,当完成后,它调用release_firmware()来释放相应的数据结构。
6、驱动执行剩下的初始化和WiFi适配器。
Module Autoload
自动按需下载内核模块是Linux支持的有效特性,为理解内核如何发出"module fault"并且udev如何处理它,让我插入Xircom CardBus Ethernet adapter into a laptop's PC Card slot:
1、在编译时,对所支持设备的辨别就作为驱动模块对象的一部分产生,如drivers/net/tulip/xircom_cb.c下的:
static struct pci_device_id xircom_pci_table[] = {
    {0x115D, 0x0003, PCI_ANY_ID, PCI_ANY_ID,},
    {0,},
};

/* Mark the device table */
MODULE_DEVICE_TABLE(pci, xircom_pci_table);
 这声明了该驱动支持任意PCI vender ID为0x115D和PCI device ID为0x0003的卡,当您安装了该驱动模块后,depmod工具查看module image并且解释在device table里面的ID,然后将下面的entry加入 /lib/modules/kernel-version/modules.aliasalias pci:v0000115Dd00000003sv*sd*bc*sc*i* xircom_cb,这里v表示VenderID,d代表DeviceID,sv代表subvenderID,*代表通配。

2、当您在CardBus插槽中热插拔Xircom card时,内核产生一个uevent声称对新插入设备的辨认。您可以使用udevmonitor来查看产生的uevent:

bash> udevmonitor --env
   ...
   MODALIAS=pci:v0000115Dd00000003sv0000115Dsd00001181bc02sc00i00
   ...

3、Udevd经netlink socket接收到uevent,并唤醒modprobe with 上述的内核传递给它的MODALIAS。

modprobe pci:v0000115Dd00000003sv0000115Dsd00001181bc02sc00i00

4、modprobe在/lib/modules/kernel-version/modules.alias中发现相符的entry,并进而插入xircom_cb:
bash> lsmod
Module      Size   Used by
xircom_cb   10433 0
...

现在该card就可以上网冲浪了。

一部分不支持在嵌入式设备上使用Udev的原因:

Udev on Embedded Devices

One school of thought deprecates the use of udev in favor of statically created device nodes on embedded devices for the following reasons:

  • Udev creates /dev nodes during each reboot, compared to static nodes that are created only once during software install. If your embedded device uses flash storage, flash pages that hold /dev nodes suffer an erase-write cycle on each boot in the case of the former, and this reduces flash life span. (Flash memory is discussed in detail in Chapter 17, "Memory Technology Devices.") You do have the option of mounting /dev over a RAM-based filesystem, however.

  • Udev contributes to increased boot time.

  • Udev features such as dynamic creation of /dev nodes and autoloading of modules create a degree of indeterminism that some solution designers prefer to avoid on special-purpose embedded devices, especially ones that do not interact with the outside world via hotpluggable buses. According to this point of view, static node creation and boot-time insertion of any modules provide more control over the system and make it easier to test.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多