Commit | Line | Data |
---|---|---|
e5354107 | 1 | Intel(R) Management Engine (ME) Client bus API |
cfba6784 | 2 | ============================================== |
e5354107 SO |
3 | |
4 | ||
5 | Rationale | |
6 | ========= | |
cfba6784 | 7 | |
e5354107 SO |
8 | MEI misc character device is useful for dedicated applications to send and receive |
9 | data to the many FW appliance found in Intel's ME from the user space. | |
10 | However for some of the ME functionalities it make sense to leverage existing software | |
11 | stack and expose them through existing kernel subsystems. | |
12 | ||
13 | In order to plug seamlessly into the kernel device driver model we add kernel virtual | |
14 | bus abstraction on top of the MEI driver. This allows implementing linux kernel drivers | |
15 | for the various MEI features as a stand alone entities found in their respective subsystem. | |
16 | Existing device drivers can even potentially be re-used by adding an MEI CL bus layer to | |
17 | the existing code. | |
18 | ||
19 | ||
20 | MEI CL bus API | |
cfba6784 JB |
21 | ============== |
22 | ||
e5354107 SO |
23 | A driver implementation for an MEI Client is very similar to existing bus |
24 | based device drivers. The driver registers itself as an MEI CL bus driver through | |
25 | the mei_cl_driver structure: | |
26 | ||
27 | struct mei_cl_driver { | |
28 | struct device_driver driver; | |
29 | const char *name; | |
30 | ||
31 | const struct mei_cl_device_id *id_table; | |
32 | ||
33 | int (*probe)(struct mei_cl_device *dev, const struct mei_cl_id *id); | |
34 | int (*remove)(struct mei_cl_device *dev); | |
35 | }; | |
36 | ||
37 | struct mei_cl_id { | |
38 | char name[MEI_NAME_SIZE]; | |
39 | kernel_ulong_t driver_info; | |
40 | }; | |
41 | ||
42 | The mei_cl_id structure allows the driver to bind itself against a device name. | |
43 | ||
44 | To actually register a driver on the ME Client bus one must call the mei_cl_add_driver() | |
45 | API. This is typically called at module init time. | |
46 | ||
47 | Once registered on the ME Client bus, a driver will typically try to do some I/O on | |
48 | this bus and this should be done through the mei_cl_send() and mei_cl_recv() | |
49 | routines. The latter is synchronous (blocks and sleeps until data shows up). | |
50 | In order for drivers to be notified of pending events waiting for them (e.g. | |
51 | an Rx event) they can register an event handler through the | |
52 | mei_cl_register_event_cb() routine. Currently only the MEI_EVENT_RX event | |
53 | will trigger an event handler call and the driver implementation is supposed | |
54 | to call mei_recv() from the event handler in order to fetch the pending | |
55 | received buffers. | |
56 | ||
57 | ||
58 | Example | |
59 | ======= | |
cfba6784 | 60 | |
e5354107 SO |
61 | As a theoretical example let's pretend the ME comes with a "contact" NFC IP. |
62 | The driver init and exit routines for this device would look like: | |
63 | ||
64 | #define CONTACT_DRIVER_NAME "contact" | |
65 | ||
66 | static struct mei_cl_device_id contact_mei_cl_tbl[] = { | |
67 | { CONTACT_DRIVER_NAME, }, | |
68 | ||
69 | /* required last entry */ | |
70 | { } | |
71 | }; | |
72 | MODULE_DEVICE_TABLE(mei_cl, contact_mei_cl_tbl); | |
73 | ||
74 | static struct mei_cl_driver contact_driver = { | |
5e857b66 JB |
75 | .id_table = contact_mei_tbl, |
76 | .name = CONTACT_DRIVER_NAME, | |
e5354107 | 77 | |
5e857b66 JB |
78 | .probe = contact_probe, |
79 | .remove = contact_remove, | |
e5354107 SO |
80 | }; |
81 | ||
82 | static int contact_init(void) | |
83 | { | |
84 | int r; | |
85 | ||
86 | r = mei_cl_driver_register(&contact_driver); | |
87 | if (r) { | |
88 | pr_err(CONTACT_DRIVER_NAME ": driver registration failed\n"); | |
89 | return r; | |
90 | } | |
91 | ||
92 | return 0; | |
93 | } | |
94 | ||
95 | static void __exit contact_exit(void) | |
96 | { | |
97 | mei_cl_driver_unregister(&contact_driver); | |
98 | } | |
99 | ||
100 | module_init(contact_init); | |
101 | module_exit(contact_exit); | |
102 | ||
103 | And the driver's simplified probe routine would look like that: | |
104 | ||
105 | int contact_probe(struct mei_cl_device *dev, struct mei_cl_device_id *id) | |
106 | { | |
107 | struct contact_driver *contact; | |
108 | ||
109 | [...] | |
e46980a1 SO |
110 | mei_cl_enable_device(dev); |
111 | ||
e5354107 SO |
112 | mei_cl_register_event_cb(dev, contact_event_cb, contact); |
113 | ||
114 | return 0; | |
5e857b66 | 115 | } |
e5354107 | 116 | |
e46980a1 SO |
117 | In the probe routine the driver first enable the MEI device and then registers |
118 | an ME bus event handler which is as close as it can get to registering a | |
119 | threaded IRQ handler. | |
e5354107 SO |
120 | The handler implementation will typically call some I/O routine depending on |
121 | the pending events: | |
122 | ||
123 | #define MAX_NFC_PAYLOAD 128 | |
124 | ||
125 | static void contact_event_cb(struct mei_cl_device *dev, u32 events, | |
126 | void *context) | |
127 | { | |
128 | struct contact_driver *contact = context; | |
129 | ||
130 | if (events & BIT(MEI_EVENT_RX)) { | |
131 | u8 payload[MAX_NFC_PAYLOAD]; | |
132 | int payload_size; | |
133 | ||
134 | payload_size = mei_recv(dev, payload, MAX_NFC_PAYLOAD); | |
135 | if (payload_size <= 0) | |
136 | return; | |
137 | ||
138 | /* Hook to the NFC subsystem */ | |
139 | nfc_hci_recv_frame(contact->hdev, payload, payload_size); | |
140 | } | |
141 | } |