← Documents Documentation/wmi/driver-development-guide.rst GitHub 원문 ↗

Linux 6.18.37 · WMI / Development

WMI driver development guide

device 기반 WMI driver의 조사, 구조, method·data·event API와 multi-device 설계를 설명합니다.

Source pathDocumentation/wmi/driver-development-guide.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

driver-development-guide.rst:1-192

device 기반 WMI driver의 조사, 구조, method·data·event API와 multi-device 설계를 설명합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0-or-later
2
3 ============================
4 WMI driver development guide
5 ============================
6
7 The WMI subsystem provides a rich driver API for implementing WMI drivers,
8 documented at Documentation/driver-api/wmi.rst. This document will serve
9 as an introductory guide for WMI driver writers using this API. It is supposed
10 to be a successor to the original LWN article [1]_ which deals with WMI drivers
11 using the deprecated GUID-based WMI interface.
12
13 Obtaining WMI device information
14 --------------------------------
15
16 Before developing an WMI driver, information about the WMI device in question
17 must be obtained. The `lswmi <https://pypi.org/project/lswmi>`_ utility can be
18 used to extract detailed WMI device information using the following command:
19
20 ::
21
22 lswmi -V
23
24 The resulting output will contain information about all WMI devices available on
25 a given machine, plus some extra information.
26
27 In order to find out more about the interface used to communicate with a WMI device,
28 the `bmfdec <https://github.com/pali/bmfdec>`_ utilities can be used to decode
29 the Binary MOF (Managed Object Format) information used to describe WMI devices.
30 The ``wmi-bmof`` driver exposes this information to userspace, see
31 Documentation/wmi/devices/wmi-bmof.rst.
32
33 In order to retrieve the decoded Binary MOF information, use the following command (requires root):
34
35 ::
36
37 ./bmf2mof /sys/bus/wmi/devices/05901221-D566-11D1-B2F0-00A0C9062910[-X]/bmof
38
39 Sometimes, looking at the disassembled ACPI tables used to describe the WMI device
40 helps in understanding how the WMI device is supposed to work. The path of the ACPI
41 method associated with a given WMI device can be retrieved using the ``lswmi`` utility
42 as mentioned above.
43
44 If you are attempting to port a driver to Linux and are working on a Windows
45 system, `WMIExplorer <https://github.com/vinaypamnani/wmie2>`_ can be useful
46 for inspecting available WMI methods and invoking them directly.
47
48 Basic WMI driver structure
49 --------------------------
50
51 The basic WMI driver is build around the struct wmi_driver, which is then bound
52 to matching WMI devices using a struct wmi_device_id table:
53
54 ::
55
56 static const struct wmi_device_id foo_id_table[] = {
57 /* Only use uppercase letters! */
58 { "936DA01F-9ABD-4D9D-80C7-02AF85C822A8", NULL },
59 { }
60 };
61 MODULE_DEVICE_TABLE(wmi, foo_id_table);
62
63 static struct wmi_driver foo_driver = {
64 .driver = {
65 .name = "foo",
66 .probe_type = PROBE_PREFER_ASYNCHRONOUS, /* recommended */
67 .pm = pm_sleep_ptr(&foo_dev_pm_ops), /* optional */
68 },
69 .id_table = foo_id_table,
70 .probe = foo_probe,
71 .remove = foo_remove, /* optional, devres is preferred */
72 .shutdown = foo_shutdown, /* optional, called during shutdown */
73 .notify = foo_notify, /* optional, for event handling */
74 .no_notify_data = true, /* optional, enables events containing no additional data */
75 .no_singleton = true, /* required for new WMI drivers */
76 };
77 module_wmi_driver(foo_driver);
78
79 The probe() callback is called when the WMI driver is bound to a matching WMI device. Allocating
80 driver-specific data structures and initialising interfaces to other kernel subsystems should
81 normally be done in this function.
82
83 The remove() callback is then called when the WMI driver is unbound from a WMI device. In order
84 to unregister interfaces to other kernel subsystems and release resources, devres should be used.
85 This simplifies error handling during probe and often allows to omit this callback entirely, see
86 Documentation/driver-api/driver-model/devres.rst for details.
87
88 The shutdown() callback is called during shutdown, reboot or kexec. Its sole purpose is to disable
89 the WMI device and put it in a well-known state for the WMI driver to pick up later after reboot
90 or kexec. Most WMI drivers need no special shutdown handling and can thus omit this callback.
91
92 Please note that new WMI drivers are required to be able to be instantiated multiple times,
93 and are forbidden from using any deprecated GUID-based WMI functions. This means that the
94 WMI driver should be prepared for the scenario that multiple matching WMI devices are present
95 on a given machine.
96
97 Because of this, WMI drivers should use the state container design pattern as described in
98 Documentation/driver-api/driver-model/design-patterns.rst.
99
100 .. warning:: Using both GUID-based and non-GUID-based functions for querying WMI data blocks and
101 handling WMI events simultaneously on the same device is guaranteed to corrupt the
102 WMI device state and might lead to erratic behaviour.
103
104 WMI method drivers
105 ------------------
106
107 WMI drivers can call WMI device methods using wmidev_evaluate_method(), the
108 structure of the ACPI buffer passed to this function is device-specific and usually
109 needs some tinkering to get right. Looking at the ACPI tables containing the WMI
110 device usually helps here. The method id and instance number passed to this function
111 are also device-specific, looking at the decoded Binary MOF is usually enough to
112 find the right values.
113
114 The maximum instance number can be retrieved during runtime using wmidev_instance_count().
115
116 Take a look at drivers/platform/x86/inspur_platform_profile.c for an example WMI method driver.
117
118 WMI data block drivers
119 ----------------------
120
121 WMI drivers can query WMI device data blocks using wmidev_block_query(), the
122 structure of the returned ACPI object is again device-specific. Some WMI devices
123 also allow for setting data blocks using wmidev_block_set().
124
125 The maximum instance number can also be retrieved using wmidev_instance_count().
126
127 Take a look at drivers/platform/x86/intel/wmi/sbl-fw-update.c for an example
128 WMI data block driver.
129
130 WMI event drivers
131 -----------------
132
133 WMI drivers can receive WMI events via the notify() callback inside the struct wmi_driver.
134 The WMI subsystem will then take care of setting up the WMI event accordingly. Please note that
135 the structure of the ACPI object passed to this callback is device-specific, and freeing the
136 ACPI object is being done by the WMI subsystem, not the driver.
137
138 The WMI driver core will take care that the notify() callback will only be called after
139 the probe() callback has been called, and that no events are being received by the driver
140 right before and after calling its remove() or shutdown() callback.
141
142 However WMI driver developers should be aware that multiple WMI events can be received concurrently,
143 so any locking (if necessary) needs to be provided by the WMI driver itself.
144
145 In order to be able to receive WMI events containing no additional event data,
146 the ``no_notify_data`` flag inside struct wmi_driver should be set to ``true``.
147
148 Take a look at drivers/platform/x86/xiaomi-wmi.c for an example WMI event driver.
149
150 Handling multiple WMI devices at once
151 -------------------------------------
152
153 There are many cases of firmware vendors using multiple WMI devices to control different aspects
154 of a single physical device. This can make developing WMI drivers complicated, as those drivers
155 might need to communicate with each other to present a unified interface to userspace.
156
157 On such case involves a WMI event device which needs to talk to a WMI data block device or WMI
158 method device upon receiving an WMI event. In such a case, two WMI drivers should be developed,
159 one for the WMI event device and one for the other WMI device.
160
161 The WMI event device driver has only one purpose: to receive WMI events, validate any additional
162 event data and invoke a notifier chain. The other WMI driver adds itself to this notifier chain
163 during probing and thus gets notified every time a WMI event is received. This WMI driver might
164 then process the event further for example by using an input device.
165
166 For other WMI device constellations, similar mechanisms can be used.
167
168 Things to avoid
169 ---------------
170
171 When developing WMI drivers, there are a couple of things which should be avoided:
172
173 - usage of the deprecated GUID-based WMI interface which uses GUIDs instead of WMI device structs
174 - bypassing of the WMI subsystem when talking to WMI devices
175 - WMI drivers which cannot be instantiated multiple times.
176
177 Many older WMI drivers violate one or more points from this list. The reason for
178 this is that the WMI subsystem evolved significantly over the last two decades,
179 so there is a lot of legacy cruft inside older WMI drivers.
180
181 New WMI drivers are also required to conform to the linux kernel coding style as specified in
182 Documentation/process/coding-style.rst. The checkpatch utility can catch many common coding style
183 violations, you can invoke it with the following command:
184
185 ::
186
187 ./scripts/checkpatch.pl --strict <path to driver file>
188
189 References
190 ==========
191
192 .. [1] https://lwn.net/Articles/391230/
193

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

현대 WMI device API 지침

1-12

WMI subsystem은 WMI driver 구현을 위한 풍부한 API를 제공하며 `Documentation/driver-api/wmi.rst`에 문서화되어 있습니다. 이 글은 해당 API를 사용하는 driver 작성자의 입문 지침입니다.

deprecated GUID 기반 WMI interface를 다룬 기존 LWN 글을 계승하며, 새 driver는 device 기반 API를 사용해야 합니다.

.. SPDX-License-Identifier: GPL-2.0-or-later

============================
WMI driver development guide
============================

The WMI subsystem provides a rich driver API for implementing WMI drivers,
documented at Documentation/driver-api/wmi.rst. This document will serve
as an introductory guide for WMI driver writers using this API. It is supposed
to be a successor to the original LWN article [1]_ which deals with WMI drivers
using the deprecated GUID-based WMI interface.

WMI device 정보 수집

13-47

driver를 개발하기 전에 대상 WMI device 정보를 확보해야 합니다. `lswmi -V`는 장치의 모든 WMI device와 추가 정보를 자세히 출력합니다.

`bmfdec` 도구는 WMI device를 설명하는 Binary MOF를 decode합니다. `wmi-bmof` driver가 이를 userspace에 노출하며 root 권한으로 `./bmf2mof /sys/bus/wmi/devices/05901221-D566-11D1-B2F0-00A0C9062910[-X]/bmof`를 실행할 수 있습니다.

WMI device를 설명하는 disassembled ACPI table을 보면 의도한 동작을 이해하는 데 도움이 됩니다. 연결된 ACPI method 경로는 `lswmi`로 찾을 수 있습니다.

Windows에서 Linux로 driver를 port하는 경우 `WMIExplorer`로 사용 가능한 WMI method를 조사하고 직접 호출할 수 있습니다.

WMI 조사 도구
도구용도
lswmi -VWMI device와 ACPI method 경로 확인
bmfdec / bmf2mofBinary MOF를 사람이 읽을 수 있는 설명으로 decode
ACPI disassemblyAML control flow와 buffer 구조 분석
WMIExplorerWindows에서 method 조사와 직접 호출

driver 구현 전에 interface를 파악하는 도구와 목적입니다.

Obtaining WMI device information
--------------------------------

Before developing an WMI driver, information about the WMI device in question
must be obtained. The `lswmi <https://pypi.org/project/lswmi>`_ utility can be
used to extract detailed WMI device information using the following command:

::

  lswmi -V

The resulting output will contain information about all WMI devices available on
a given machine, plus some extra information.

In order to find out more about the interface used to communicate with a WMI device,
the `bmfdec <https://github.com/pali/bmfdec>`_ utilities can be used to decode
the Binary MOF (Managed Object Format) information used to describe WMI devices.
The ``wmi-bmof`` driver exposes this information to userspace, see
Documentation/wmi/devices/wmi-bmof.rst.

In order to retrieve the decoded Binary MOF information, use the following command (requires root):

::

  ./bmf2mof /sys/bus/wmi/devices/05901221-D566-11D1-B2F0-00A0C9062910[-X]/bmof

Sometimes, looking at the disassembled ACPI tables used to describe the WMI device
helps in understanding how the WMI device is supposed to work. The path of the ACPI
method associated with a given WMI device can be retrieved using the ``lswmi`` utility
as mentioned above.

If you are attempting to port a driver to Linux and are working on a Windows
system, `WMIExplorer <https://github.com/vinaypamnani/wmie2>`_ can be useful
for inspecting available WMI methods and invoking them directly.

struct wmi_driver의 기본 구조

48-103

기본 WMI driver는 `struct wmi_driver`를 중심으로 구성하고 `struct wmi_device_id` table로 일치하는 WMI device에 bind합니다. GUID 문자열에는 대문자만 사용해야 하며 `MODULE_DEVICE_TABLE`과 `module_wmi_driver`로 등록합니다.

`probe()`는 일치하는 device에 bind할 때 호출되며 driver 전용 data 할당과 다른 kernel subsystem interface 초기화를 수행합니다. `remove()`는 unbind 때 호출되지만 resource와 interface 정리에 devres를 사용하면 보통 생략할 수 있습니다.

`shutdown()`은 shutdown, reboot, kexec 때 device를 비활성화해 다음 boot가 인식할 수 있는 알려진 상태로 둡니다. 대부분의 WMI driver에는 별도 처리가 필요 없습니다.

새 WMI driver는 여러 instance 생성을 지원해야 하고 deprecated GUID 기반 WMI 함수를 사용할 수 없습니다. 한 장치에 여러 matching WMI device가 있을 수 있으므로 state container design pattern을 사용해야 합니다.

같은 device에서 data block query와 event 처리를 위해 GUID 기반 함수와 device 기반 함수를 동시에 사용하면 WMI device state가 반드시 손상되고 비정상 동작을 일으킬 수 있습니다.

wmi_driver field
Field필수 여부역할
driver.name필수driver 이름
driver.probe_type권장PROBE_PREFER_ASYNCHRONOUS
driver.pm선택power management operation
id_table필수matching WMI GUID table
probe필수bind와 초기화
remove선택unbind; devres 사용 권장
shutdown선택shutdown·reboot·kexec 정리
notify선택WMI event 처리
no_notify_data선택추가 data 없는 event 허용
no_singleton새 driver 필수여러 instance 허용

예제 구조체의 필수·선택 callback과 flag입니다.

Basic WMI driver structure
--------------------------

The basic WMI driver is build around the struct wmi_driver, which is then bound
to matching WMI devices using a struct wmi_device_id table:

::

  static const struct wmi_device_id foo_id_table[] = {
         /* Only use uppercase letters! */
         { "936DA01F-9ABD-4D9D-80C7-02AF85C822A8", NULL },
         { }
  };
  MODULE_DEVICE_TABLE(wmi, foo_id_table);

  static struct wmi_driver foo_driver = {
        .driver = {
                .name = "foo",
                .probe_type = PROBE_PREFER_ASYNCHRONOUS,        /* recommended */
                .pm = pm_sleep_ptr(&foo_dev_pm_ops),            /* optional */
        },
        .id_table = foo_id_table,
        .probe = foo_probe,
        .remove = foo_remove,         /* optional, devres is preferred */
        .shutdown = foo_shutdown,     /* optional, called during shutdown */
        .notify = foo_notify,         /* optional, for event handling */
        .no_notify_data = true,       /* optional, enables events containing no additional data */
        .no_singleton = true,         /* required for new WMI drivers */
  };
  module_wmi_driver(foo_driver);

The probe() callback is called when the WMI driver is bound to a matching WMI device. Allocating
driver-specific data structures and initialising interfaces to other kernel subsystems should
normally be done in this function.

The remove() callback is then called when the WMI driver is unbound from a WMI device. In order
to unregister interfaces to other kernel subsystems and release resources, devres should be used.
This simplifies error handling during probe and often allows to omit this callback entirely, see
Documentation/driver-api/driver-model/devres.rst for details.

The shutdown() callback is called during shutdown, reboot or kexec. Its sole purpose is to disable
the WMI device and put it in a well-known state for the WMI driver to pick up later after reboot
or kexec. Most WMI drivers need no special shutdown handling and can thus omit this callback.

Please note that new WMI drivers are required to be able to be instantiated multiple times,
and are forbidden from using any deprecated GUID-based WMI functions. This means that the
WMI driver should be prepared for the scenario that multiple matching WMI devices are present
on a given machine.

Because of this, WMI drivers should use the state container design pattern as described in
Documentation/driver-api/driver-model/design-patterns.rst.

.. warning:: Using both GUID-based and non-GUID-based functions for querying WMI data blocks and
             handling WMI events simultaneously on the same device is guaranteed to corrupt the
             WMI device state and might lead to erratic behaviour.

WMI method driver

104-117

WMI method는 `wmidev_evaluate_method()`로 호출합니다. 전달할 ACPI buffer, method ID와 instance number는 장치별로 다르므로 ACPI table과 decode한 Binary MOF를 조사해 맞춰야 합니다.

runtime 최대 instance 수는 `wmidev_instance_count()`로 조회합니다. 예제는 `drivers/platform/x86/inspur_platform_profile.c`에 있습니다.

WMI method drivers
------------------

WMI drivers can call WMI device methods using wmidev_evaluate_method(), the
structure of the ACPI buffer passed to this function is device-specific and usually
needs some tinkering to get right. Looking at the ACPI tables containing the WMI
device usually helps here. The method id and instance number passed to this function
are also device-specific, looking at the decoded Binary MOF is usually enough to
find the right values.

The maximum instance number can be retrieved during runtime using wmidev_instance_count().

Take a look at drivers/platform/x86/inspur_platform_profile.c for an example WMI method driver.

WMI data block driver

118-129

WMI data block은 `wmidev_block_query()`로 query하며 반환 ACPI object 구조는 장치별입니다. 일부 device는 `wmidev_block_set()`으로 data block 설정도 허용합니다.

최대 instance 수는 `wmidev_instance_count()`로 조회합니다. 예제는 `drivers/platform/x86/intel/wmi/sbl-fw-update.c`입니다.

WMI data block drivers
----------------------

WMI drivers can query WMI device data blocks using wmidev_block_query(), the
structure of the returned ACPI object is again device-specific. Some WMI devices
also allow for setting data blocks using wmidev_block_set().

The maximum instance number can also be retrieved using wmidev_instance_count().

Take a look at drivers/platform/x86/intel/wmi/sbl-fw-update.c for an example
WMI data block driver.

WMI event driver와 concurrency

130-149

WMI event는 `struct wmi_driver`의 `notify()` callback으로 받으며 subsystem이 event 설정을 담당합니다. callback에 전달된 ACPI object 구조는 장치별이고 object 해제는 driver가 아니라 WMI subsystem이 수행합니다.

core는 `probe()` 뒤에만 `notify()`를 호출하고 `remove()` 또는 `shutdown()` 직전과 직후에는 event를 전달하지 않습니다. 그러나 여러 WMI event가 동시에 도착할 수 있으므로 필요한 locking은 driver가 제공해야 합니다.

추가 event data가 없는 WMI event를 받으려면 `no_notify_data`를 `true`로 설정합니다. 예제는 `drivers/platform/x86/xiaomi-wmi.c`입니다.

WMI event drivers
-----------------

WMI drivers can receive WMI events via the notify() callback inside the struct wmi_driver.
The WMI subsystem will then take care of setting up the WMI event accordingly. Please note that
the structure of the ACPI object passed to this callback is device-specific, and freeing the
ACPI object is being done by the WMI subsystem, not the driver.

The WMI driver core will take care that the notify() callback will only be called after
the probe() callback has been called, and that no events are being received by the driver
right before and after calling its remove() or shutdown() callback.

However WMI driver developers should be aware that multiple WMI events can be received concurrently,
so any locking (if necessary) needs to be provided by the WMI driver itself.

In order to be able to receive WMI events containing no additional event data,
the ``no_notify_data`` flag inside struct wmi_driver should be set to ``true``.

Take a look at drivers/platform/x86/xiaomi-wmi.c for an example WMI event driver.

여러 WMI device의 협력

150-167

firmware vendor가 물리 장치 하나의 여러 측면을 각각 다른 WMI device로 제어하는 경우가 많습니다. userspace에 통합 interface를 제공하려면 driver끼리 통신해야 할 수 있습니다.

event device가 event 수신 뒤 data block 또는 method device와 통신해야 한다면 WMI driver를 둘로 나눕니다. event driver는 event와 추가 data를 검증하고 notifier chain을 호출하는 역할만 수행합니다.

다른 WMI driver는 probe 때 notifier chain에 자신을 등록해 event를 받고, 필요하면 input device 등을 통해 후속 처리합니다. 다른 device 조합에도 비슷한 mechanism을 적용할 수 있습니다.

multi-device event 처리
event WMI device driver가 event 수신추가 event data 검증notifier chain 호출method·data block driver가 notification 수신input 등 통합 userspace interface로 전달

event device와 기능 device를 분리하는 권장 구조입니다.

Handling multiple WMI devices at once
-------------------------------------

There are many cases of firmware vendors using multiple WMI devices to control different aspects
of a single physical device. This can make developing WMI drivers complicated, as those drivers
might need to communicate with each other to present a unified interface to userspace.

On such case involves a WMI event device which needs to talk to a WMI data block device or WMI
method device upon receiving an WMI event. In such a case, two WMI drivers should be developed,
one for the WMI event device and one for the other WMI device.

The WMI event device driver has only one purpose: to receive WMI events, validate any additional
event data and invoke a notifier chain. The other WMI driver adds itself to this notifier chain
during probing and thus gets notified every time a WMI event is received. This WMI driver might
then process the event further for example by using an input device.

For other WMI device constellations, similar mechanisms can be used.

피해야 할 구현과 coding style

168-188

WMI driver 개발에서는 deprecated GUID 기반 interface 사용, WMI device 통신 때 subsystem 우회, 여러 번 instantiate할 수 없는 driver를 피해야 합니다. 오래된 driver에는 subsystem의 긴 발전 역사 때문에 이런 legacy code가 남아 있습니다.

새 driver는 `Documentation/process/coding-style.rst`의 Linux kernel coding style을 따라야 합니다. `./scripts/checkpatch.pl --strict <path to driver file>`로 흔한 style 위반을 검사할 수 있습니다.

피해야 할 항목
항목문제
deprecated GUID-based APIdevice model과 multi-instance 지원 방해
WMI subsystem 우회core 상태·수명 관리 훼손
singleton 전제같은 GUID 장치 여러 개를 처리하지 못함

새 WMI driver에서 금지하거나 피해야 할 설계입니다.

Things to avoid
---------------

When developing WMI drivers, there are a couple of things which should be avoided:

- usage of the deprecated GUID-based WMI interface which uses GUIDs instead of WMI device structs
- bypassing of the WMI subsystem when talking to WMI devices
- WMI drivers which cannot be instantiated multiple times.

Many older WMI drivers violate one or more points from this list. The reason for
this is that the WMI subsystem evolved significantly over the last two decades,
so there is a lot of legacy cruft inside older WMI drivers.

New WMI drivers are also required to conform to the linux kernel coding style as specified in
Documentation/process/coding-style.rst. The checkpatch utility can catch many common coding style
violations, you can invoke it with the following command:

::

  ./scripts/checkpatch.pl --strict <path to driver file>

참고 자료

189-192

참고 자료는 초기 GUID 기반 WMI driver 개발을 다룬 LWN 기사입니다. 새 구현에는 이 문서의 device 기반 지침을 우선 적용해야 합니다.

References
==========

.. [1] https://lwn.net/Articles/391230/