Documentation/driver-api/mei/mei-client-bus.rst GitHub 원문 ↗

Linux 6.18.37 · Driver API

Intel Management Engine Client bus API

MEI firmware 기능을 kernel subsystem driver로 연결하는 client bus API와 예제를 설명하는 전문 번역입니다.

Source pathDocumentation/driver-api/mei/mei-client-bus.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

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

1. 요약·해설

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

요약과 해설

mei-client-bus.rst:1-168

MEI CL virtual bus는 GUID·name 기반 firmware client를 일반 kernel driver model과 subsystem에 통합합니다.

문서 구성
원문 줄내용
1-22Virtual bus 동기
23-68Driver·ID·I/O API
69-75Exported kernel-doc
76-122Module 등록 예제
123-160Probe와 RX callback
161-168HDCP·NFC driver 목차

2. 영어 원문 전체

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

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ==============================================
4 Intel(R) Management Engine (ME) Client bus API
5 ==============================================
6
7
8 Rationale
9 =========
10
11 The MEI character device is useful for dedicated applications to send and receive
12 data to the many FW appliance found in Intel's ME from the user space.
13 However, for some of the ME functionalities it makes sense to leverage existing software
14 stack and expose them through existing kernel subsystems.
15
16 In order to plug seamlessly into the kernel device driver model we add kernel virtual
17 bus abstraction on top of the MEI driver. This allows implementing Linux kernel drivers
18 for the various MEI features as a stand alone entities found in their respective subsystem.
19 Existing device drivers can even potentially be re-used by adding an MEI CL bus layer to
20 the existing code.
21
22
23 MEI CL bus API
24 ==============
25
26 A driver implementation for an MEI Client is very similar to any other existing bus
27 based device drivers. The driver registers itself as an MEI CL bus driver through
28 the ``struct mei_cl_driver`` structure defined in :file:`include/linux/mei_cl_bus.c`
29
30 .. code-block:: C
31
32 struct mei_cl_driver {
33 struct device_driver driver;
34 const char *name;
35
36 const struct mei_cl_device_id *id_table;
37
38 int (*probe)(struct mei_cl_device *dev, const struct mei_cl_id *id);
39 int (*remove)(struct mei_cl_device *dev);
40 };
41
42
43
44 The mei_cl_device_id structure defined in :file:`include/linux/mod_devicetable.h` allows a
45 driver to bind itself against a device name.
46
47 .. code-block:: C
48
49 struct mei_cl_device_id {
50 char name[MEI_CL_NAME_SIZE];
51 uuid_le uuid;
52 __u8 version;
53 kernel_ulong_t driver_info;
54 };
55
56 To actually register a driver on the ME Client bus one must call the :c:func:`mei_cl_add_driver`
57 API. This is typically called at module initialization time.
58
59 Once the driver is registered and bound to the device, a driver will typically
60 try to do some I/O on this bus and this should be done through the :c:func:`mei_cl_send`
61 and :c:func:`mei_cl_recv` functions. More detailed information is in :ref:`api` section.
62
63 In order for a driver to be notified about pending traffic or event, the driver
64 should register a callback via :c:func:`mei_cl_devev_register_rx_cb` and
65 :c:func:`mei_cldev_register_notify_cb` function respectively.
66
67 .. _api:
68
69 API:
70 ----
71 .. kernel-doc:: drivers/misc/mei/bus.c
72 :export: drivers/misc/mei/bus.c
73
74
75
76 Example
77 =======
78
79 As a theoretical example let's pretend the ME comes with a "contact" NFC IP.
80 The driver init and exit routines for this device would look like:
81
82 .. code-block:: C
83
84 #define CONTACT_DRIVER_NAME "contact"
85
86 static struct mei_cl_device_id contact_mei_cl_tbl[] = {
87 { CONTACT_DRIVER_NAME, },
88
89 /* required last entry */
90 { }
91 };
92 MODULE_DEVICE_TABLE(mei_cl, contact_mei_cl_tbl);
93
94 static struct mei_cl_driver contact_driver = {
95 .id_table = contact_mei_tbl,
96 .name = CONTACT_DRIVER_NAME,
97
98 .probe = contact_probe,
99 .remove = contact_remove,
100 };
101
102 static int contact_init(void)
103 {
104 int r;
105
106 r = mei_cl_driver_register(&contact_driver);
107 if (r) {
108 pr_err(CONTACT_DRIVER_NAME ": driver registration failed\n");
109 return r;
110 }
111
112 return 0;
113 }
114
115 static void __exit contact_exit(void)
116 {
117 mei_cl_driver_unregister(&contact_driver);
118 }
119
120 module_init(contact_init);
121 module_exit(contact_exit);
122
123 And the driver's simplified probe routine would look like that:
124
125 .. code-block:: C
126
127 int contact_probe(struct mei_cl_device *dev, struct mei_cl_device_id *id)
128 {
129 [...]
130 mei_cldev_enable(dev);
131
132 mei_cldev_register_rx_cb(dev, contact_rx_cb);
133
134 return 0;
135 }
136
137 In the probe routine the driver first enable the MEI device and then registers
138 an rx handler which is as close as it can get to registering a threaded IRQ handler.
139 The handler implementation will typically call :c:func:`mei_cldev_recv` and then
140 process received data.
141
142 .. code-block:: C
143
144 #define MAX_PAYLOAD 128
145 #define HDR_SIZE 4
146 static void conntact_rx_cb(struct mei_cl_device *cldev)
147 {
148 struct contact *c = mei_cldev_get_drvdata(cldev);
149 unsigned char payload[MAX_PAYLOAD];
150 ssize_t payload_sz;
151
152 payload_sz = mei_cldev_recv(cldev, payload, MAX_PAYLOAD)
153 if (reply_size < HDR_SIZE) {
154 return;
155 }
156
157 c->process_rx(payload);
158
159 }
160
161 MEI Client Bus Drivers
162 ======================
163
164 .. toctree::
165 :maxdepth: 2
166
167 hdcp
168 nfc
169

3. 한국어 전문 번역

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

MEI Client bus가 필요한 이유

1-22

MEI character device는 전용 userspace application이 Intel ME의 여러 firmware appliance와 자료를 주고받는 데 유용합니다.

일부 ME 기능은 기존 software stack을 활용해 기존 kernel subsystem으로 노출하는 편이 적합합니다.

Kernel device·driver model에 자연스럽게 연결하기 위해 MEI driver 위에 kernel virtual bus abstraction을 추가합니다. 각 MEI 기능을 해당 subsystem의 독립 Linux kernel driver로 구현할 수 있습니다.

기존 driver code에 MEI CL bus layer를 추가해 재사용할 가능성도 있습니다.

MEI 기능 노출 방식
ME firmware applianceMEI character device전용 userspace application
ME firmware featureMEI CL virtual busKernel subsystem driver

Userspace 전용 통신과 kernel subsystem 통합을 목적에 맞게 나눕니다.

.. SPDX-License-Identifier: GPL-2.0

==============================================
Intel(R) Management Engine (ME) Client bus API
==============================================


Rationale
=========

The MEI character device is useful for dedicated applications to send and receive
data to the many FW appliance found in Intel's ME from the user space.
However, for some of the ME functionalities it makes sense to leverage existing software
stack and expose them through existing kernel subsystems.

In order to plug seamlessly into the kernel device driver model we add kernel virtual
bus abstraction on top of the MEI driver. This allows implementing Linux kernel drivers
for the various MEI features as a stand alone entities found in their respective subsystem.
Existing device drivers can even potentially be re-used by adding an MEI CL bus layer to
the existing code.

MEI CL bus driver model

23-68

MEI Client driver 구현은 다른 bus 기반 device driver와 비슷합니다. Driver는 `mei_cl_driver` 구조체로 MEI CL bus driver를 등록합니다.

`mei_cl_driver`는 일반 `device_driver`, 이름, `mei_cl_device_id` table, `probe()`와 `remove()` callback을 포함합니다.

`include/linux/mod_devicetable.h`의 `mei_cl_device_id`는 name, UUID, protocol version과 `driver_info`로 driver를 device에 bind합니다.

Module 초기화 시 `mei_cl_add_driver()` API로 driver를 ME Client bus에 등록합니다.

등록·binding 후 I/O는 `mei_cl_send()`와 `mei_cl_recv()`로 수행합니다.

Pending traffic과 event를 통지받으려면 각각 `mei_cl_devev_register_rx_cb()`와 `mei_cldev_register_notify_cb()`로 callback을 등록합니다.

MEI CL driver 구성
구성 요소역할
`mei_cl_driver`Driver·ID table·probe·remove
`mei_cl_device_id`Name·UUID·version matching
`mei_cl_add_driver()`Bus 등록
`mei_cl_send()`·`mei_cl_recv()`자료 송수신
RX·notify callbackPending traffic와 event 통지

MEI CL bus API
==============

A driver implementation for an MEI Client is very similar to any other existing bus
based device drivers. The driver registers itself as an MEI CL bus driver through
the ``struct mei_cl_driver`` structure defined in :file:`include/linux/mei_cl_bus.c`

.. code-block:: C

        struct mei_cl_driver {
                struct device_driver driver;
                const char *name;

                const struct mei_cl_device_id *id_table;

                int (*probe)(struct mei_cl_device *dev, const struct mei_cl_id *id);
                int (*remove)(struct mei_cl_device *dev);
        };



The mei_cl_device_id structure defined in :file:`include/linux/mod_devicetable.h` allows a
driver to bind itself against a device name.

.. code-block:: C

        struct mei_cl_device_id {
                char name[MEI_CL_NAME_SIZE];
                uuid_le uuid;
                __u8    version;
                kernel_ulong_t driver_info;
        };

To actually register a driver on the ME Client bus one must call the :c:func:`mei_cl_add_driver`
API. This is typically called at module initialization time.

Once the driver is registered and bound to the device, a driver will typically
try to do some I/O on this bus and this should be done through the :c:func:`mei_cl_send`
and :c:func:`mei_cl_recv` functions. More detailed information is in :ref:`api` section.

In order for a driver to be notified about pending traffic or event, the driver
should register a callback via :c:func:`mei_cl_devev_register_rx_cb` and
:c:func:`mei_cldev_register_notify_cb` function respectively.

.. _api:

MEI CL bus API

69-75

MEI Client bus의 exported API는 `drivers/misc/mei/bus.c`의 kernel-doc에서 제공합니다.

API source
Source path범위
`drivers/misc/mei/bus.c`Exported MEI CL bus API

API:
----
.. kernel-doc:: drivers/misc/mei/bus.c
    :export: drivers/misc/mei/bus.c


Contact driver 등록 예제

76-122

가상 `contact` NFC IP 예제는 `mei_cl_device_id` table의 이름으로 device를 match하고 마지막에 빈 sentinel entry를 둡니다. `MODULE_DEVICE_TABLE(mei_cl, ...)`로 module device table을 공개합니다.

`mei_cl_driver`에는 ID table, driver 이름, `contact_probe`와 `contact_remove` callback을 지정합니다.

Module init은 `mei_cl_driver_register()`로 driver를 등록하고 실패하면 오류를 기록해 반환합니다. Module exit은 `mei_cl_driver_unregister()`로 등록을 해제합니다.

MEI CL module 수명주기
ID table`MODULE_DEVICE_TABLE``mei_cl_driver`
`module_init``mei_cl_driver_register()`Probe binding
`module_exit``mei_cl_driver_unregister()`

ID table과 callback을 묶은 driver를 module load·unload에 맞춰 등록합니다.

Example
=======

As a theoretical example let's pretend the ME comes with a "contact" NFC IP.
The driver init and exit routines for this device would look like:

.. code-block:: C

        #define CONTACT_DRIVER_NAME "contact"

        static struct mei_cl_device_id contact_mei_cl_tbl[] = {
                { CONTACT_DRIVER_NAME, },

                /* required last entry */
                { }
        };
        MODULE_DEVICE_TABLE(mei_cl, contact_mei_cl_tbl);

        static struct mei_cl_driver contact_driver = {
                .id_table = contact_mei_tbl,
                .name = CONTACT_DRIVER_NAME,

                .probe = contact_probe,
                .remove = contact_remove,
        };

        static int contact_init(void)
        {
                int r;

                r = mei_cl_driver_register(&contact_driver);
                if (r) {
                        pr_err(CONTACT_DRIVER_NAME ": driver registration failed\n");
                        return r;
                }

                return 0;
        }

        static void __exit contact_exit(void)
        {
                mei_cl_driver_unregister(&contact_driver);
        }

        module_init(contact_init);
        module_exit(contact_exit);

Probe와 RX callback 예제

123-160

단순화된 probe는 먼저 `mei_cldev_enable(dev)`로 MEI device를 활성화하고 `mei_cldev_register_rx_cb()`로 RX handler를 등록합니다. 이는 threaded IRQ handler 등록과 가장 비슷한 동작입니다.

RX handler는 보통 `mei_cldev_recv()`로 payload를 받고 처리합니다.

예제 callback은 `mei_cldev_get_drvdata()`로 driver 상태를 얻고 최대 128 byte payload를 수신합니다. Header 크기보다 짧으면 반환하고 그렇지 않으면 driver의 receive 처리로 전달합니다.

MEI CL receive 경로
Probe`mei_cldev_enable()``mei_cldev_register_rx_cb()`
Pending RXCallback`mei_cldev_recv()`Header 길이 검사Payload 처리

Probe에서 장치와 callback을 준비하고 callback에서 payload를 수신·검증·처리합니다.

And the driver's simplified probe routine would look like that:

.. code-block:: C

        int contact_probe(struct mei_cl_device *dev, struct mei_cl_device_id *id)
        {
                [...]
                mei_cldev_enable(dev);

                mei_cldev_register_rx_cb(dev, contact_rx_cb);

                return 0;
        }

In the probe routine the driver first enable the MEI device and then registers
an rx handler which is as close as it can get to registering a threaded IRQ handler.
The handler implementation will typically call :c:func:`mei_cldev_recv` and then
process received data.

.. code-block:: C

        #define MAX_PAYLOAD 128
        #define HDR_SIZE 4
        static void conntact_rx_cb(struct mei_cl_device *cldev)
        {
                struct contact *c = mei_cldev_get_drvdata(cldev);
                unsigned char payload[MAX_PAYLOAD];
                ssize_t payload_sz;

                payload_sz = mei_cldev_recv(cldev, payload,  MAX_PAYLOAD)
                if (reply_size < HDR_SIZE) {
                        return;
                }

                c->process_rx(payload);

        }

MEI Client Bus driver 문서

161-168

MEI Client Bus driver 하위 문서는 HDCP와 NFC client driver를 연결합니다.

Client bus driver
문서기능
`hdcp`HDCP 2.2 security negotiation
`nfc`MEI 뒤의 NFC physical device

MEI Client Bus Drivers
======================

.. toctree::
   :maxdepth: 2

   hdcp
   nfc