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