Intel(R) Management Engine (ME) 客戶端匯流排 API¶
基本原理¶
對於專用應用程式,MEI 字元裝置可用於從使用者空間向 Intel ME 中的許多 FW 裝置傳送和接收資料。但是,對於某些 ME 功能,利用現有軟體堆疊並透過現有核心子系統公開它們是有意義的。
為了無縫地插入核心裝置驅動程式模型,我們在 MEI 驅動程式之上添加了核心虛擬匯流排抽象。這允許為各種 MEI 功能實現 Linux 核心驅動程式,作為其各自子系統中獨立的實體。透過向現有程式碼新增 MEI CL 匯流排層,甚至可以潛在地重新使用現有裝置驅動程式。
MEI CL 匯流排 API¶
MEI 客戶端的驅動程式實現與任何其他基於現有匯流排的裝置驅動程式非常相似。驅動程式透過 struct mei_cl_driver 結構(定義於 include/linux/mei_cl_bus.c)將自身註冊為 MEI CL 匯流排驅動程式。
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);
};
定義於 include/linux/mod_devicetable.h 中的 mei_cl_device_id 結構允許驅動程式將其自身繫結到裝置名稱。
struct mei_cl_device_id {
char name[MEI_CL_NAME_SIZE];
uuid_le uuid;
__u8 version;
kernel_ulong_t driver_info;
};
要實際在 ME 客戶端總線上註冊驅動程式,必須呼叫 mei_cl_add_driver() API。這通常在模組初始化時呼叫。
一旦驅動程式註冊並繫結到裝置,驅動程式通常會嘗試在此總線上執行一些 I/O,這應透過 mei_cl_send() 和 mei_cl_recv() 函式完成。更多詳細資訊請參見 API: 部分。
為了使驅動程式收到有關待處理流量或事件的通知,驅動程式應分別透過 mei_cl_devev_register_rx_cb() 和 mei_cldev_register_notify_cb() 函式註冊回撥。
API:¶
-
ssize_t mei_cldev_send_vtag(struct mei_cl_device *cldev, const u8 *buf, size_t length, u8 vtag)¶
具有 vtag (write) 的 me 裝置傳送
引數
struct mei_cl_device *cldevme 客戶端裝置
const u8 *buf要傳送的緩衝區
size_t length緩衝區長度
u8 vtag虛擬標籤
返回
以位元組為單位寫入的大小
< 0 出錯時
-
ssize_t mei_cldev_send_vtag_timeout(struct mei_cl_device *cldev, const u8 *buf, size_t length, u8 vtag, unsigned long timeout)¶
具有 vtag 和超時 (write) 的 me 裝置傳送
引數
struct mei_cl_device *cldevme 客戶端裝置
const u8 *buf要傳送的緩衝區
size_t length緩衝區長度
u8 vtag虛擬標籤
unsigned long timeout傳送超時(以毫秒為單位),0 表示無限超時
返回
以位元組為單位寫入的大小
< 0 出錯時
-
ssize_t mei_cldev_recv_vtag(struct mei_cl_device *cldev, u8 *buf, size_t length, u8 *vtag)¶
客戶端接收 vtag (read)
引數
struct mei_cl_device *cldevme 客戶端裝置
u8 *buf要接收的緩衝區
size_t length緩衝區長度
u8 *vtag虛擬標籤
返回
以位元組為單位讀取的大小
< 0 出錯時
-
ssize_t mei_cldev_recv_timeout(struct mei_cl_device *cldev, u8 *buf, size_t length, unsigned long timeout)¶
客戶端接收超時 (read)
引數
struct mei_cl_device *cldevme 客戶端裝置
u8 *buf要接收的緩衝區
size_t length緩衝區長度
unsigned long timeout傳送超時(以毫秒為單位),0 表示無限超時
返回
以位元組為單位讀取的大小
< 0 出錯時
-
ssize_t mei_cldev_recv_vtag_timeout(struct mei_cl_device *cldev, u8 *buf, size_t length, u8 *vtag, unsigned long timeout)¶
客戶端接收 vtag (read)
引數
struct mei_cl_device *cldevme 客戶端裝置
u8 *buf要接收的緩衝區
size_t length緩衝區長度
u8 *vtag虛擬標籤
unsigned long timeout接收超時(以毫秒為單位),0 表示無限超時
返回
以位元組為單位讀取的大小
< 0 出錯時
-
ssize_t mei_cldev_send(struct mei_cl_device *cldev, const u8 *buf, size_t length)¶
me 裝置傳送 (write)
引數
struct mei_cl_device *cldevme 客戶端裝置
const u8 *buf要傳送的緩衝區
size_t length緩衝區長度
返回
以位元組為單位寫入的大小
< 0 出錯時
-
ssize_t mei_cldev_send_timeout(struct mei_cl_device *cldev, const u8 *buf, size_t length, unsigned long timeout)¶
具有超時 (write) 的 me 裝置傳送
引數
struct mei_cl_device *cldevme 客戶端裝置
const u8 *buf要傳送的緩衝區
size_t length緩衝區長度
unsigned long timeout傳送超時(以毫秒為單位),0 表示無限超時
返回
以位元組為單位寫入的大小
< 0 出錯時
-
ssize_t mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length)¶
客戶端接收 (read)
引數
struct mei_cl_device *cldevme 客戶端裝置
u8 *buf要接收的緩衝區
size_t length緩衝區長度
返回
以位元組為單位讀取的大小,< 0 表示出錯
-
int mei_cldev_register_rx_cb(struct mei_cl_device *cldev, mei_cldev_cb_t rx_cb)¶
註冊 Rx 事件回撥
引數
struct mei_cl_device *cldevme 客戶端裝置
mei_cldev_cb_t rx_cb回撥函式
返回
- 0 表示成功
-EALREADY 如果已註冊回撥 <0 表示其他錯誤
-
int mei_cldev_register_notif_cb(struct mei_cl_device *cldev, mei_cldev_cb_t notif_cb)¶
註冊 FW 通知事件回撥
引數
struct mei_cl_device *cldevme 客戶端裝置
mei_cldev_cb_t notif_cb回撥函式
返回
- 0 表示成功
-EALREADY 如果已註冊回撥 <0 表示其他錯誤
-
void *mei_cldev_get_drvdata(const struct mei_cl_device *cldev)¶
驅動程式資料 getter
引數
const struct mei_cl_device *cldevmei 客戶端裝置
返回
驅動程式私有資料
-
void mei_cldev_set_drvdata(struct mei_cl_device *cldev, void *data)¶
驅動程式資料 setter
引數
struct mei_cl_device *cldevmei 客戶端裝置
void *data要儲存的資料
-
u8 mei_cldev_ver(const struct mei_cl_device *cldev)¶
返回底層 me 客戶端的協議版本
引數
const struct mei_cl_device *cldevmei 客戶端裝置
返回
me 客戶端協議版本
-
bool mei_cldev_enabled(const struct mei_cl_device *cldev)¶
檢查裝置是否已啟用
引數
const struct mei_cl_device *cldevmei 客戶端裝置
返回
如果 me 客戶端已初始化並連線,則為 true
-
int mei_cldev_enable(struct mei_cl_device *cldev)¶
啟用 me 客戶端裝置,建立與 me 客戶端的連線
引數
struct mei_cl_device *cldevme 客戶端裝置
返回
0 表示成功,< 0 表示出錯
-
int mei_cldev_disable(struct mei_cl_device *cldev)¶
停用 me 客戶端裝置,從 me 客戶端斷開連線
引數
struct mei_cl_device *cldevme 客戶端裝置
返回
0 表示成功,< 0 表示出錯
-
ssize_t mei_cldev_send_gsc_command(struct mei_cl_device *cldev, u8 client_id, u32 fence_id, struct scatterlist *sg_in, size_t total_in_len, struct scatterlist *sg_out)¶
透過向 gsc 傳送 gsl mei 訊息並接收 gsc 的回覆來發送 gsc 命令
引數
struct mei_cl_device *cldevme 客戶端裝置
u8 client_id要向其傳送命令的客戶端 ID
u32 fence_id要向其傳送命令的 fence ID
struct scatterlist *sg_in包含 rx 訊息緩衝區地址的散點收集列表
size_t total_in_len“in” sg 中資料的總長度,可以小於緩衝區大小的總和
struct scatterlist *sg_out包含 tx 訊息緩衝區地址的散點收集列表
返回
以位元組為單位寫入的大小
< 0 出錯時
示例¶
作為一個理論示例,讓我們假設 ME 配備了“聯絡人”NFC IP。此裝置的驅動程式 init 和 exit 例程將如下所示
#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);
驅動程式的簡化探測例程將如下所示
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;
}
在探測例程中,驅動程式首先啟用 MEI 裝置,然後註冊一個 rx 處理程式,這與註冊一個執行緒 IRQ 處理程式儘可能接近。處理程式實現通常會呼叫 mei_cldev_recv(),然後處理接收到的資料。
#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);
}