요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. 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.
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.
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 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 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 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.
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.
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>
References
==========
.. [1] https://lwn.net/Articles/391230/
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
현대 WMI device API 지침
1-12WMI 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-47driver를 개발하기 전에 대상 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를 조사하고 직접 호출할 수 있습니다.
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가 반드시 손상되고 비정상 동작을 일으킬 수 있습니다.
예제 구조체의 필수·선택 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-117WMI 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-129WMI 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-149WMI 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-167firmware 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을 적용할 수 있습니다.
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-188WMI 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 위반을 검사할 수 있습니다.
새 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/
요약·해설
driver-development-guide.rst:1-192device 기반 WMI driver의 조사, 구조, method·data·event API와 multi-device 설계를 설명합니다.