Linux设备与驱动学习笔记(概述)

时间:2022-06-20 19:01:59

由于在下能力相当有限,有不当之处,还望大家批评指正^_^


本文基于Linux内核2.6.32

一、宏观架构

在内核中,大量的驱动与设备(但不是全部),都按总线进行分类管理。

例如,凡是pci设备或pci设备的驱动,都归到pci总线下管理。

1. 从sys文件系统感受这种架构。

/sys/bus/目录下列出了各种总线类型,像pci、i2c、usb、platform等。

我们以pci总线为例,看看相关概念。


/sys/bus/pci/devices/目录下则则列出了所有系统中存在的所有pci设备(不管有没有安装对应的驱动程序)的名称,pci设备的名称即pci地址(即 "域:pci总线号:槽位号:功能号")。下面的示例输出,限于篇幅,只列出了几个设备作为样例。

[root@211-157-CGSLV5 char]# ls /sys/bus/pci/devices/
0000:00:00.0  0000:03:00.0  0000:06:12.7  0000:06:16.3


/sys/bus/pci/drivers目录下则列出了所有已加载的所有pci设备的驱动。

下面的示例输出,列出了pci设备驱动ixgbe(一款网卡驱动程序),以及当前接管了哪些设备(0000:06:00.0和0000:06:00.1)。


[root@211-157-CGSLV5 char]# ls /sys/bus/pci/drivers/ixgbe  
0000:06:00.0  0000:06:00.1  bind  module  new_id  remove_id  uevent  unbind

示例输出中还有一些别的文件,像bind用来指定某个pci设备由此驱动接管,unbind则起相反的作用。通常,一个驱动都有自己支持的设备型号的列表。new_id用于动态增加此驱动可以接管的设备型号,remove_id则起相反作用。


【注】

pci设备的名称,是通过如下方式设置的

dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),
     dev->bus->number, PCI_SLOT(dev->devfn),
     PCI_FUNC(dev->devfn));

而其他类型的设备,命名方式也名异。下面列出了其他几种设备的命名方式

(i) isa

dev_set_name(&isa_dev->dev, "%s.%u",
    isa_driver->driver.name, id);


(ii)platform

if (pdev->id != -1)
dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
else
dev_set_name(&pdev->dev, "%s", pdev->name);

(iii)I2C

dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
    client->addr);


【注】

有时候,可能会存在多个驱动,都可以驱动某一个设备的情况。

这时候,用户可以执行将设备与驱动进行绑定或解除绑定的操作。

将设备与驱动绑定的方法为:

echo  dev_name  > /sys/bus/bus_type/drivers/driver_name/bind

将设备与驱动解除绑定的方法为:

echo  dev_name  > /sys/bus/bus_type/drivers/driver_name/unbind

例如,将名称为 0000:06:00.1的pci设备与名为ixgbe的驱动绑定,则命令如下:

echo 0000:06:00.1 > /sys/bus/pci/drivers/ixgbe/bind

echo命令执行后,由driver_bind/driver_unbind函数执行具体的绑定与解绑。

这两个函数对应于sys文件系统的store操作。


当然,除了pci总线,还有别的总线类型。

不同类型的总线,有着不同的原理与概念。

下面列出了若干不同类型总线的设备,可以看到其命名风格的不同。


[root@211-157-CGSLV5 char]# ls /sys/bus/i2c/devices/
2-007c  i2c-0  i2c-1  i2c-2  i2c-3

[root@211-157-CGSLV5 char]# ls /sys/bus/usb/devices/
1-0:1.0  1-1  1-1:1.0  1-1.3  1-1.3:1.0  1-1.3:1.1  2-0:1.0  2-1  2-1:1.0  usb1  usb2


[root@211-157-CGSLV5 char]# ls /sys/bus/platform/devices/
alarmtimer  coretemp.1        GHES.0  gpio_ich         iTCO_wdt   pcspkr
coretemp.0  Fixed MDIO bus.0  GHES.1  ipmi_bmc.3060.8  microcode  serial8250


2. 从源代码中感受这种架构。

设备驱动方面的核心代码,位于drivers/base目录下。core.c是设备管理的核心文件,driver.c是驱动管理的核心文件,bus.c是总线处理的核心文件。我们可以以他们为线索,看出不同模块之间的调用关系,同时也能看出内核在管理设备与驱动方面的精妙设计理念。

在看代码之前,先来聊聊设备、驱动、总线之间的关系,同时引出一些内核中的重要数据结构。首先说说设备。不管是什么奇奇怪怪的设备,它毕竟都是一个设备。所以,在这个层次上,可以将一切设备都归纳到设备这个集合中去管理。就像动物一样,不管是人,还是猪马牛羊,站在动物这个层次上,都可以归属到动物这个类别中去。

在内核中,用于表示一个设备的数据结构是 struct device。

再来说说驱动。同理,一切驱动都是驱动,自然也是统一管理。

内核中,表示一个驱动的数据结构是 struct device_driver。

好了,现在已经有了设备与驱动的表示方法。但是,这是站在最高层次上做出的概念抽象。

不同类型总线的设备,其标识方法也各有不同。

实际上,内核源码中也为此定义了不同类型的结构来标识之。

例如,struct platform_device_id用于标识platform设备,struct pci_device_id用于标识pci设备。


未完...