162306a36Sopenharmony_ci.. SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci==============================================
462306a36Sopenharmony_ciIntel(R) Management Engine (ME) Client bus API
562306a36Sopenharmony_ci==============================================
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci
862306a36Sopenharmony_ciRationale
962306a36Sopenharmony_ci=========
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ciThe MEI character device is useful for dedicated applications to send and receive
1262306a36Sopenharmony_cidata to the many FW appliance found in Intel's ME from the user space.
1362306a36Sopenharmony_ciHowever, for some of the ME functionalities it makes sense to leverage existing software
1462306a36Sopenharmony_cistack and expose them through existing kernel subsystems.
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciIn order to plug seamlessly into the kernel device driver model we add kernel virtual
1762306a36Sopenharmony_cibus abstraction on top of the MEI driver. This allows implementing Linux kernel drivers
1862306a36Sopenharmony_cifor the various MEI features as a stand alone entities found in their respective subsystem.
1962306a36Sopenharmony_ciExisting device drivers can even potentially be re-used by adding an MEI CL bus layer to
2062306a36Sopenharmony_cithe existing code.
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ciMEI CL bus API
2462306a36Sopenharmony_ci==============
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ciA driver implementation for an MEI Client is very similar to any other existing bus
2762306a36Sopenharmony_cibased device drivers. The driver registers itself as an MEI CL bus driver through
2862306a36Sopenharmony_cithe ``struct mei_cl_driver`` structure defined in :file:`include/linux/mei_cl_bus.c`
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci.. code-block:: C
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci        struct mei_cl_driver {
3362306a36Sopenharmony_ci                struct device_driver driver;
3462306a36Sopenharmony_ci                const char *name;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci                const struct mei_cl_device_id *id_table;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci                int (*probe)(struct mei_cl_device *dev, const struct mei_cl_id *id);
3962306a36Sopenharmony_ci                int (*remove)(struct mei_cl_device *dev);
4062306a36Sopenharmony_ci        };
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ciThe mei_cl_device_id structure defined in :file:`include/linux/mod_devicetable.h` allows a
4562306a36Sopenharmony_cidriver to bind itself against a device name.
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci.. code-block:: C
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci        struct mei_cl_device_id {
5062306a36Sopenharmony_ci                char name[MEI_CL_NAME_SIZE];
5162306a36Sopenharmony_ci                uuid_le uuid;
5262306a36Sopenharmony_ci                __u8    version;
5362306a36Sopenharmony_ci                kernel_ulong_t driver_info;
5462306a36Sopenharmony_ci        };
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ciTo actually register a driver on the ME Client bus one must call the :c:func:`mei_cl_add_driver`
5762306a36Sopenharmony_ciAPI. This is typically called at module initialization time.
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciOnce the driver is registered and bound to the device, a driver will typically
6062306a36Sopenharmony_citry to do some I/O on this bus and this should be done through the :c:func:`mei_cl_send`
6162306a36Sopenharmony_ciand :c:func:`mei_cl_recv` functions. More detailed information is in :ref:`api` section.
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ciIn order for a driver to be notified about pending traffic or event, the driver
6462306a36Sopenharmony_cishould register a callback via :c:func:`mei_cl_devev_register_rx_cb` and
6562306a36Sopenharmony_ci:c:func:`mei_cldev_register_notify_cb` function respectively.
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci.. _api:
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ciAPI:
7062306a36Sopenharmony_ci----
7162306a36Sopenharmony_ci.. kernel-doc:: drivers/misc/mei/bus.c
7262306a36Sopenharmony_ci    :export: drivers/misc/mei/bus.c
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ciExample
7762306a36Sopenharmony_ci=======
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ciAs a theoretical example let's pretend the ME comes with a "contact" NFC IP.
8062306a36Sopenharmony_ciThe driver init and exit routines for this device would look like:
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci.. code-block:: C
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci        #define CONTACT_DRIVER_NAME "contact"
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci        static struct mei_cl_device_id contact_mei_cl_tbl[] = {
8762306a36Sopenharmony_ci                { CONTACT_DRIVER_NAME, },
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci                /* required last entry */
9062306a36Sopenharmony_ci                { }
9162306a36Sopenharmony_ci        };
9262306a36Sopenharmony_ci        MODULE_DEVICE_TABLE(mei_cl, contact_mei_cl_tbl);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci        static struct mei_cl_driver contact_driver = {
9562306a36Sopenharmony_ci                .id_table = contact_mei_tbl,
9662306a36Sopenharmony_ci                .name = CONTACT_DRIVER_NAME,
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci                .probe = contact_probe,
9962306a36Sopenharmony_ci                .remove = contact_remove,
10062306a36Sopenharmony_ci        };
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci        static int contact_init(void)
10362306a36Sopenharmony_ci        {
10462306a36Sopenharmony_ci                int r;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci                r = mei_cl_driver_register(&contact_driver);
10762306a36Sopenharmony_ci                if (r) {
10862306a36Sopenharmony_ci                        pr_err(CONTACT_DRIVER_NAME ": driver registration failed\n");
10962306a36Sopenharmony_ci                        return r;
11062306a36Sopenharmony_ci                }
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci                return 0;
11362306a36Sopenharmony_ci        }
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci        static void __exit contact_exit(void)
11662306a36Sopenharmony_ci        {
11762306a36Sopenharmony_ci                mei_cl_driver_unregister(&contact_driver);
11862306a36Sopenharmony_ci        }
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci        module_init(contact_init);
12162306a36Sopenharmony_ci        module_exit(contact_exit);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ciAnd the driver's simplified probe routine would look like that:
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci.. code-block:: C
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci        int contact_probe(struct mei_cl_device *dev, struct mei_cl_device_id *id)
12862306a36Sopenharmony_ci        {
12962306a36Sopenharmony_ci                [...]
13062306a36Sopenharmony_ci                mei_cldev_enable(dev);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci                mei_cldev_register_rx_cb(dev, contact_rx_cb);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci                return 0;
13562306a36Sopenharmony_ci        }
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciIn the probe routine the driver first enable the MEI device and then registers
13862306a36Sopenharmony_cian rx handler which is as close as it can get to registering a threaded IRQ handler.
13962306a36Sopenharmony_ciThe handler implementation will typically call :c:func:`mei_cldev_recv` and then
14062306a36Sopenharmony_ciprocess received data.
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci.. code-block:: C
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci        #define MAX_PAYLOAD 128
14562306a36Sopenharmony_ci        #define HDR_SIZE 4
14662306a36Sopenharmony_ci        static void conntact_rx_cb(struct mei_cl_device *cldev)
14762306a36Sopenharmony_ci        {
14862306a36Sopenharmony_ci                struct contact *c = mei_cldev_get_drvdata(cldev);
14962306a36Sopenharmony_ci                unsigned char payload[MAX_PAYLOAD];
15062306a36Sopenharmony_ci                ssize_t payload_sz;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci                payload_sz = mei_cldev_recv(cldev, payload,  MAX_PAYLOAD)
15362306a36Sopenharmony_ci                if (reply_size < HDR_SIZE) {
15462306a36Sopenharmony_ci                        return;
15562306a36Sopenharmony_ci                }
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci                c->process_rx(payload);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci        }
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ciMEI Client Bus Drivers
16262306a36Sopenharmony_ci======================
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci.. toctree::
16562306a36Sopenharmony_ci   :maxdepth: 2
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci   hdcp
16862306a36Sopenharmony_ci   nfc
169