SylixOS熱插拔概述(一)
-
1. 熱插拔系統簡介
-
1.1 熱插拔系統
熱插拔裝置指支援帶電操作的一類裝置,允許使用者不關閉系統、不切斷電源情況下取出或更換裝置。熱插拔系統用於管理、監控系統中所有熱插拔裝置的插入、拔出狀態,從而能夠讓系統內部自動完成此類裝置的建立、刪除而無需使用者手動處理。同時,熱插拔系統會收集熱插拔相關資訊,供應用程式使用。
圖 1-1熱插拔系統結構
如圖 1-1所示,SylixOS 中有一個名稱為"t_hotplug"的核心執行緒,裝置的熱插拔狀態通過事件的方式報告給該執行緒。系統中還有一個名為"/dev/hotplug"的虛擬裝置,它負責收集相關熱插拔訊息,應用程式可通過讀取"/dev/hotplug"裝置,獲得自己關心的熱插拔訊息。
-
1.2 實現原理
在 SylixOS 中,可以使用如下兩種方法獲得熱插拔事件:
1.中斷產生,例如"mini2440"開發板上SD卡熱插拔操作,當SD卡插入或者拔出時會觸發引腳中斷,中斷服務程式中會根據讀取的引腳狀態,產生相應的熱插拔事件,將需要處理的事件加入到熱插拔工作處理佇列,等待核心執行緒處理。
2. 輪詢檢測,當有些熱插拔裝置不產生中斷(沒有插拔中斷功能的裝置),則需要輪詢檢測某些事件標誌。裝置驅動程式需要將檢測函式和引數註冊到"hotplug"迴圈檢測連結串列中,"t_hotplug"核心執行緒會定時呼叫檢測函式,輪詢檢測函式會產生相應事件,等待核心執行緒處理。
如圖 1-1所示,當裝置熱插拔操作結束時,會產生一條熱插拔訊息存入快取區,應用層程式可以通過讀取虛擬裝置"/dev/hotplug"(熱插拔裝置驅動建立),從快取區中獲取熱插拔訊息。
-
2 讀取熱插拔訊息
前文提到,熱插拔事件產生後會產生熱插拔訊息,存放在"/dev/hotplug"裝置的快取區中,則應用層可以對"/dev/hotplug"裝置進行讀取,獲得應用層需要的熱插拔訊息。由於"/dev/hotplug"裝置是字元裝置,所以應用層可以對裝置進行open、read、write、ioctl、close等操作,獲得應用層所需的熱插拔訊息。
2.1 獲取熱插拔訊息例項
SylixOS 中定義了當前常見的熱插拔裝置訊息,如 USB、SD卡、PCI等,使用者也可以自定義新增。此外,還有網絡卡的連線與斷開等與熱插拔行為相似的訊息。
下面舉例說明如何獲取網絡卡熱插拔訊息(本例程式是在mini2440開發板上測試執行),測試程式碼程式碼清單2-1所示。
#include <stdio.h>
#include <string.h>
#define MSG_LEN_MAX (534)
int main (int argc, char *argv[])
{
UINT8 pucMsgBuff[MSG_LEN_MAX];
INT iFd;
INT32 iMsgType;
BOOL bInsert;
ssize_t sstReadLen;
CHAR *pcDevName = NULL;
UINT8 *pucArg = NULL;
UINT8 *pucTemp = NULL;
iFd = open("/dev/hotplug", O_RDONLY); /* 開啟hotplug虛擬裝置 */
if (iFd < 0) {
fprintf(stderr, "open /dev/hotplug failed.\n");
return (-1);
}
ioctl(iFd, LW_HOTPLUG_FIOSETMSG, LW_HOTPLUG_MSG_NETLINK_CHANGE); /* ioctl 設定關心網絡卡熱插拔事件 */
while (1) {
sstReadLen = read(iFd, pucMsgBuff, MSG_LEN_MAX); /* 讀取熱插拔訊息 */
if (sstReadLen < 0) {
fprintf(stderr, "read hotplug message error.\n");
close(iFd);
return (-1);
}
if (sstReadLen < 5) {
continue;
}
/*
* 解析熱插拔訊息
*/
pucTemp = pucMsgBuff;
iMsgType = (pucTemp[0] << 24) | (pucTemp[1] << 16) | (pucTemp[2] << 8) | (pucTemp[3]);
pucTemp += 4;
bInsert = *pucTemp ? TRUE : FALSE;
pucTemp += 1;
pcDevName = (CHAR *) pucTemp;
pucArg = pucTemp + strlen(pcDevName) + 1;
printf("get new hotplug message >>\n" /* 列印熱插拔訊息 */
" message type: %d\n"
"device status: %s\n"
" device name: %s\n"
" arg0: 0x%01x%01x%01x%01x\n"
" arg1: 0x%01x%01x%01x%01x\n"
" arg2: 0x%01x%01x%01x%01x\n"
" arg3: 0x%01x%01x%01x%01x\n", iMsgType,
bInsert ? "insert" : "remove", pcDevName, pucArg[0], pucArg[1],
pucArg[2], pucArg[3], pucArg[4], pucArg[5], pucArg[6],
pucArg[7], pucArg[8], pucArg[9], pucArg[10], pucArg[11],
pucArg[12], pucArg[13], pucArg[14], pucArg[15]);
}
close(iFd);
return (0);
}
程式清單1.1示例程式碼分析:
- 開啟“/dev/hotplug”熱插拔裝置;
- 迴圈讀取該裝置中的熱插拔訊息;
- 解析熱插拔訊息並輸出列印。
應用程式在解析訊息型別時,需要按照大端資料儲存格式進行解析,即低地址的位元組代表的是高位元組資料。訊息的額外引數的起始地址即為裝置名稱起始地址加上其長度和結束字元的地址。程式執行後,插入或拔出SD儲存卡,會列印如下的資訊:
插入SD儲存卡:
get new hotplug message >>
message type: 346
device status: insert
device name: /media/sdcard0
arg0: 0x0000
arg1: 0x0000
arg2: 0x0000
arg3: 0x0000
拔出SD儲存卡:
get new hotplug message >>
message type: 346
device status: remove
device name: /media/sdcard0
arg0: 0x0000
arg1: 0x0000
arg2: 0x0000
arg3: 0x0000
通過分析程式碼清單2-1所示程式碼,使用者在讀取裝置熱插拔訊息時應注意以下幾點:
1.以只讀方式開啟"/dev/hotplug"裝置,SylixOS中熱插拔訊息在熱插拔裝置建立時產生,並且寫入到裝置中快取區中。
2.程式碼清單2-1中程式通過ioctl函式實現單獨監聽網絡卡熱插拔訊息的功能,應用程式可以根據需要設定ioctl函式中的引數來獲取對應的訊息。預設情況下是讀取所有型別熱插拔訊息。
3.程式碼清單2-1中read函式實現讀取網絡卡熱插拔訊息的功能,讀取訊息後對獲得的熱插拔訊息進行解析,然後輸出列印。根據程式圖 2-1輸出結果可知,SylixOS中對熱插拔訊息格式進行特殊規定,格式分析參照2.2節。
-
2.2 熱插拔訊息格式
由2.1節中讀取網絡卡熱插拔訊息例項可知,在SylixOS中熱插拔訊息有規定的格式。下面對SylixOS熱插拔訊息格式進行分析,如圖 2-2所示。
圖 2-2熱插拔訊息格式
參照圖 2-2可知,訊息的前4個位元組標識了訊息的型別。SylixOS中已經定義了USB鍵盤、USB滑鼠、SD儲存卡、SDIO無線網絡卡等熱插拔型別。在實際的硬體平臺上,裝置驅動也可以定義自己的熱插拔訊息型別。
第5個位元組為裝置狀態,0表示拔出,1表示插入。
從第 6 個位元組開始,表示裝置的名稱,其內容為一個以'\0'結束的字串,應用程式應該以此為結束符得到完整的名稱。該名稱為一個裝置的完整路徑名稱, 如"/dev/ttyUSB0"、"/media/sdcard0"等。由於SylixOS中,一個完整路徑名稱的最大長度為512,加上結束字元'\0',因此,dev name欄位的最大長度為513。
緊跟著裝置名稱('\0'字元結尾)的是 4 個可用於靈活擴充套件的引數,均為4位元組長度。這4個引數可適應不同裝置訊息的特殊處理。SylixOS未規定每個引數的具體用法和儲存格式(大端或小端),完全由裝置驅動定義。
綜上論述,一個熱插拔訊息的最大長度為:4 + 1 + 513 + 4 + 4 + 4 + 4 = 534位元組。
-
3 熱插拔訊息產生
-
3.1 模擬熱插拔實現
下面通過訊號模擬熱插拔事件,用訊號SIGALRM模擬裝置插入,用訊號SIGUSR1模擬裝置拔出。示例程式碼清單3-1所示:
程式碼清單3-1#define __SYLIXOS_KERNEL #include <SylixOS.h> #include <stdio.h> #include <string.h> #include <signal.h> #include <pthread.h> #include <unistd.h> static char *msg = "Dev"; void send_event (void *arg) { int signum = (int)arg; if (signum == SIGALRM) { API_HotplugEventMessage(LW_HOTPLUG_MSG_ALL, 1, msg, 0, 0, 0, 0); } else if (signum == SIGUSR1){ API_HotplugEventMessage(LW_HOTPLUG_MSG_ALL, 0, msg, 0, 0, 0, 0); } } void pullout_handler (int signum) { API_HotplugEvent((VOIDFUNCPTR)send_event, (void *)signum, 0, 0, 0, 0, 0); } void insert_handler (int signum) { API_HotplugEvent((VOIDFUNCPTR)send_event, (void *)signum, 0, 0, 0, 0, 0); } int main (int argc, char *argv[]) { int i; if (signal(SIGALRM, insert_handler) == SIG_ERR) { fprintf(stderr, "Install signal handler failed.\n"); return -1; } if (signal(SIGUSR1, pullout_handler) == SIG_ERR) { fprintf(stderr, "Install signal handler failed.\n"); return -1; } for (i = 0; i < 8; ++i) { alarm(2); pause(); kill(getpid(), SIGUSR1); } return 0; }
本例利用訊號模擬熱插拔事件,執行2.1節中程式(註釋掉ioctl函式,獲取所有型別的熱插拔訊息),再執行程式碼清單3-1所示程式,會得到圖 3-2所示結果。
圖 3-2模擬熱插拔模擬結果
-
4 小結
本文件介紹了SylixOS下熱插拔系統實現原理以及應用程式如何獲取熱插拔訊息。最後通過訊號模擬熱插拔事件,打印出模擬的熱插拔訊息,下面會繼續介紹熱插拔的原理以及實現。