123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- Intel(R) Management Engine (ME) Client bus API
- ==============================================
- Rationale
- =========
- MEI misc 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 make 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 API
- ==============
- A driver implementation for an MEI Client is very similar to existing bus
- based device drivers. The driver registers itself as an MEI CL bus driver through
- the mei_cl_driver structure:
- 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);
- };
- struct mei_cl_id {
- char name[MEI_NAME_SIZE];
- kernel_ulong_t driver_info;
- };
- The mei_cl_id structure allows the driver to bind itself against a device name.
- To actually register a driver on the ME Client bus one must call the mei_cl_add_driver()
- API. This is typically called at module init time.
- Once registered on the ME Client bus, a driver will typically try to do some I/O on
- this bus and this should be done through the mei_cl_send() and mei_cl_recv()
- routines. The latter is synchronous (blocks and sleeps until data shows up).
- In order for drivers to be notified of pending events waiting for them (e.g.
- an Rx event) they can register an event handler through the
- mei_cl_register_event_cb() routine. Currently only the MEI_EVENT_RX event
- will trigger an event handler call and the driver implementation is supposed
- to call mei_recv() from the event handler in order to fetch the pending
- received buffers.
- 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:
- #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);
- And the driver's simplified probe routine would look like that:
- int contact_probe(struct mei_cl_device *dev, struct mei_cl_device_id *id)
- {
- struct contact_driver *contact;
- [...]
- mei_cl_enable_device(dev);
- mei_cl_register_event_cb(dev, contact_event_cb, contact);
- return 0;
- }
- In the probe routine the driver first enable the MEI device and then registers
- an ME bus event handler which is as close as it can get to registering a
- threaded IRQ handler.
- The handler implementation will typically call some I/O routine depending on
- the pending events:
- #define MAX_NFC_PAYLOAD 128
- static void contact_event_cb(struct mei_cl_device *dev, u32 events,
- void *context)
- {
- struct contact_driver *contact = context;
- if (events & BIT(MEI_EVENT_RX)) {
- u8 payload[MAX_NFC_PAYLOAD];
- int payload_size;
- payload_size = mei_recv(dev, payload, MAX_NFC_PAYLOAD);
- if (payload_size <= 0)
- return;
- /* Hook to the NFC subsystem */
- nfc_hci_recv_frame(contact->hdev, payload, payload_size);
- }
- }
|