如何找到我的内核加载了哪个 RTC 模块?

Oz1*_*123 2 linux linux-device-driver linux-kernel

在使用从 Ubuntu OS 16.04 获得的配置文件编译我的内核期间,我注意到我正在编译很多 RTC 驱动程序,基本上所有可能的驱动程序:

... snip ...
CC [M]  drivers/rtc/rtc-bq4802.o
CC [M]  drivers/rtc/rtc-da9052.o 
CC [M]  drivers/rtc/rtc-da9055.o
... more of those ...
...
Run Code Online (Sandbox Code Playgroud)

我想知道是否可以摆脱这么多驱动程序,所以我开始四处寻找如何:

  1. 查看lsmod我没有看到 RTC 驱动程序
  2. lshw 也没有帮助
  3. lspci -v 也没有产生太多信息。

较低级别的检查,显示设备存在:

$ ls -l /dev/rtc
lrwxrwxrwx 1 root root 4 Dec 18 09:54 /dev/rtc -> rtc0
Run Code Online (Sandbox Code Playgroud)

我得到的最接近的信息是/sys/class/rtc/

$ sudo cat /sys/class/rtc/rtc0/device/rtc/rtc0/name 
rtc_cmos
Run Code Online (Sandbox Code Playgroud)

这是否意味着我的内核使用的唯一驱动程序是rtc_cmos

  • 这是否意味着我不需要所有其他驱动程序?
  • 这是否意味着我的笔记本电脑使用兼容 CMOS 的硬件?
  • 另外,为什么modinfo rtc_cmos什么都不返回?

kkm*_*kkm 5

我知道 OP 得到了狭义问题的正确答案,但我觉得概括答案更具精神,所以我发布它,希望对回答概括性问题有所帮助:我如何找到什么驱动程序在/dev/xyz使用?.

选项 1:戳 /sys 文件系统

如果您已sysfs安装(通常在/sys; 但我还没有找到没有安装的发行版),为什么不问问内核本身呢?

1. 找出设备类型(字符/块)和节点号

$ ls -l /dev/rtc0
crw------- 1 root root 249, 0 2019-04-01 15:22:29 /dev/rtc0
^                       ^   ^
+-> character device    +---+-> Major and minor node numbers.
Run Code Online (Sandbox Code Playgroud)

2. 将数字代入 /sys/dev 路径,如下所示,(char用于字符设备block,您猜对了,用于块设备)。

$ cat /sys/dev/char/249:0/name
rtc_cmos 00:00
Run Code Online (Sandbox Code Playgroud)

这是附加到节点的内核模块的名称。无论模块是链接到内核中,还是通过 modprobe 加载,都可以正常工作。(并且00:00不是 RTC 时钟中的时间。相反,IIRC,它是其父驱动程序或总线上的设备地址或“功能”;但不要相信我,我只是模糊地记得)。


旁注,随意探索这个文件系统。它可由非 root 用户读取(安全敏感部分除外,其中大部分可以以 root 用户身份阅读),完全可以安全阅读,并且您可以在那里找到许多硬件和低级软件配置信息。与大多数 不同/proc,大多数/sys是可写的,并被程序用于更改正在运行的内核和设备参数。一方面, sysctl 完全通过它工作。但我离题了。

选项2:为你udevadm寻找/sys

如果您有可用的 udevadm,请询问它对设备的所有了解(该程序接受设备的路径/dev/sys路径):

$ udevadm info -a /dev/rtc0

[...snip intro text...]

  looking at device '/devices/pnp0/00:00/rtc/rtc0':
    KERNEL=="rtc0"
    SUBSYSTEM=="rtc"
    DRIVER==""
    ATTR{date}=="2019-06-27"
    ATTR{hctosys}=="1"
    ATTR{max_user_freq}=="64"
    ATTR{name}=="rtc_cmos 00:00"
    ATTR{since_epoch}=="1561605536"
    ATTR{time}=="03:18:56"
    ATTR{wakealarm}==""

  looking at parent device '/devices/pnp0/00:00':
    KERNELS=="00:00"
    SUBSYSTEMS=="pnp"
    DRIVERS=="rtc_cmos"   <== THIS
    ATTRS{id}=="PNP0b00"
    ATTRS{nvram}==""
    ATTRS{options}==""

  looking at parent device '/devices/pnp0':
    KERNELS=="pnp0"
    SUBSYSTEMS==""
    DRIVERS==""
Run Code Online (Sandbox Code Playgroud)

info命令指示 udevadm 提供它所知道的关于设备的所有信息,并且-a开关是在父驱动程序/总线链上上行。在这里您可以看到父设备/devices/pnp0/00:00具有驱动程序rtc_cmos,并且在由另一个设备创建的总线上被发现/激活pnp0,即即插即用总线枚举器。

顺便说一下, udevadm 打印的名称也是 sysfs 中的路径,也就是说,您可以通过在 sysfs 挂载点前加上前缀,在普通文件空间中看到它们/sys

$ ls -l /sys/devices/pnp0/00:00/rtc/rtc0
$ ls -l /sys/dev/char/249:0/
Run Code Online (Sandbox Code Playgroud)

这两个命令都会产生相同的输出。

需要注意的是,与“普通”文件系统不同,sysfs执行硬链接目录,因此不要尝试在其中进行任何递归搜索(如find . /sysls -R /sys)——这些程序在与文件系统的无限循环作斗争后只会崩溃。

那么/sys/class/rtc/rtc0/device/rtc/rtc0呢?

如您所见,从 sysfs 的根目录到设备参数节点有多个路径。哪一个是真正的麦考伊?

  • 并非每个设备都通过/dev文件系统(或任何其他设备;没有什么特别之处/dev——它只是一个临时文件; /dev/rtc0这是一个特殊的文件)。这些你不会通过路径找到/sys/dev/*

  • 并非每个设备都是子系统的一部分,这也是可选的。有不可发现的通过/sys/class*。所以这也是一个有用但可选的链接。请注意,此节点不同:它必须公开子系统特定的参数。在我们的例子中,所有 RTC 时钟都公开了一组众所周知的rtcsybsystem通用设置

  • 现在,/sys/devices/pnp0/00:00/rtc/rtc0 设备 sysfs 节点的主要规范路径。它总是对应于它在系统总线和驱动程序的层次结构中的拓扑位置:这是rtc0设备的实例,在总线上rtc寻址。它并不总是对应于它的物理连接;在我们的例子中,是一个虚拟总线,它不将设备连接到物理总线和桥接器,而只是发现和枚举它们。这是udev 用来唯一标识它的设备的规范名称(当然,没有前缀——它只是一个挂载点)。00:00pnp0pnp0/sys

  • 最后,内核模块不必在 sysfs 中公开任何内容。严格来说,只有必须从用户空间发现的模块才需要。但是如果是这样,模块决定公开什么,它的父级和祖级确定在 sysfs 中的哪个位置devices根目录下。