• 沒有找到結果。

第五章 WinME在Linux作業系統下的實作

5.3 事件通知機制的實作

5.3.5 其它的事件

關於其它事件,在核心中的實作方式就是在改變狀態的核心程式碼中增加一 個 rtnetlink 的觸發點即可。

可是有一點要注意的,就是當改變狀態的核心程式碼是在中斷環境 (Interrupt Context) 下處理時,我們是沒有辦法直接傳送 rtnetlink 的訊息給使用者空間的,

因為在中斷環境下是不能處理會進入睡眠模式的或是進行 block I/O 動作的程式 碼的。可是通常和驅動程式有關的狀態大部分都是在中斷環境中所改變的,因為 當硬體上的資訊改變時通常都是藉由中斷來通知系統。所以這個時候就沒辦法靠 增加 rtnetlink 的觸發點來傳送訊息。

要解決以上的問題,Linux 在 2.4 版的核心中有提供 task queue 的下半部 (BottomHalf) 處理方式,而其中的 scheduler queue 中的 task 則可以在程序環境 (Process Context) 下所執行的。

所以我們在中斷環境下新增一個名稱為 win_task_queue 的 scheduler queue,

把我們要執行的傳送 rtnetlink 訊息給使用者空間的函式加入 win_task_queue 中,

然後呼叫核心中提供的 schedule_task 函式將 win_task_queue 為參數傳入,這樣就 可以在程序環境中執行我們的函式。schedule_task 會喚醒核心執行緒 keventd 來 處理 win_task_queue 中所佇列的函式。

以 LINK_UP和LINK_DOWN的事件為例,當網路卡驅動程式發現這是一個 LINK_UP或是LINK_DOWN的中斷時,驅動程式會去執行netif_carrier_on來將核 心中維護網路卡狀態的carry bit 開啟,或是執行 netif_carrier_off 將 carry bit 清 掉,不過這些都是在中斷環境中所執行的,因此我們沒辦法直接在

netif_carrier_xx的函式中傳送LINK_UP或LINK_DOWN的訊息給使用者空間。所 以這邊我們就以task queue的方式解決。相關程式碼可參考 Table 5-6。

Table 5-6 送 LINK_UP、LINK_DOWN 事件的核心程式碼

/* use rtnetlink socket to notify user space LINK events */

static void win_notifier_taskq(void* dummy) {

/* if nocarry bit is zero, send NETDEV_LINKDOWN event */

if(test_bit(__LINK_STATE_NOCARRIER, &win_dev->state)) {

win_rtmsg_ifinfo(NETDEV_LINKDOWN, RTM_DRIVER_EVENT,…);

} else {

win_rtmsg_ifinfo(NETDEV_LINKUP, RTM_DRIVER_EVENT, ..);

} }

/* declare a scheduler queue */

static struct tq_struct linkwatch_queue;

static int linkwatch_fire_event(struct net_device *dev) {

<-- some initial code -->

/* call schedule_task to process functions in linkwatch_queue */

schedule_task(&linkwatch_queue);

}

/* when interface card connect POA, it’s driver will call this routine*/

void netif_carrier_on(struct net_device *dev) {

if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) linkwatch_fire_event(dev);

if (netif_running(dev))

__netdev_watchdog_up(dev);

}

/* when interface card disconnect POA, it’s driver will call this routine*/

void netif_carrier_off(struct net_device *dev) {

if (!test_and_set_bit(__LINK_STATE_NOCARRIER, &dev->state)) linkwatch_fire_event(dev);

}

第 6 章 成果及貢獻

Table 6-1 Linux 下使用 rtnetlink socket 新增一個資料到路由表中的程式碼片段

int main(int argc, char *argv[]) {

fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);

/* setup local address & bind using this address */

bzero(&la, sizeof(la));

la.nl_family = AF_NETLINK;

la.nl_pid = getpid();

bind(fd, (struct sockaddr*) &la, sizeof(la));

/* initialize RTNETLINK request buffer */

bzero(&req, sizeof(req));

/* add first attrib: */

/* set destination IP addr and increment the RTNETLINK buffer size*/

rtap = (struct rtattr *) req.buf;

rtap->rta_type = RTA_DST;

rtap->rta_len = sizeof(struct rtattr) + 4;

inet_pton(AF_INET, dsts, ((char *)rtap) + sizeof(struct rtattr));

rtl += rtap->rta_len;

/* add second attrib: */

/* set ifc index and increment the size */

rtap = (struct rtattr *) (((char *)rtap) + rtap->rta_len);

rtap->rta_type = RTA_OIF;

rtap->rta_len = sizeof(struct rtattr) + 4;

memcpy(((char *)rtap) + sizeof(struct rtattr), &ifcn, 4);

rtl += rtap->rta_len;

/* setup the NETLINK header */

req.nl.nlmsg_len = NLMSG_LENGTH(rtl);

req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;

req.nl.nlmsg_type = RTM_NEWROUTE;

/* setup the service header (struct rtmsg) */

req.rt.rtm_family = AF_INET;

req.rt.rtm_table = RT_TABLE_MAIN;

req.rt.rtm_protocol = RTPROT_STATIC;

req.rt.rtm_scope = RT_SCOPE_UNIVERSE;

req.rt.rtm_type = RTN_UNICAST;

/* set the network prefix size */

req.rt.rtm_dst_len = pn;

/* create the remote address to communicate */

bzero(&pa, sizeof(pa));

pa.nl_family = AF_NETLINK;

/* initialize & create the struct msghdr supplied to the sendmsg() function */

bzero(&msg, sizeof(msg));

msg.msg_name = (void *) &pa;

msg.msg_namelen = sizeof(pa);

/* place the pointer & size of the RTNETLINK message in the struct msghdr */

iov.iov_base = (void *) &req.nl;

iov.iov_len = req.nl.nlmsg_len;

msg.msg_iov = &iov;

msg.msg_iovlen = 1;

/* send the RTNETLINK message to kernel */

rtn = sendmsg(fd, &msg, 0);

/* close socket */

close(fd);

}

Table 6-2 使用 WinME 所提供的程式界面新增一個資料到路由表中的程式碼片段

struct set_routing_table_param { UINT8 act;

int main(int argc, char* argv[]) {

/* routing table’s attribute, dst means destination address * ifcn = 2 means out network interface’s Link ID is 2

struct set_routing_table_param *srtp;

cmd.i8_cmd = SET_ ROUTING_TABLE;

cmd.pv_param = (VOID*)malloc(sizeof(struct set_routing_table_param));

srtp = (struct set_routing_table_param *)cmd.pv_param;

/* fill param field */

srtp->act = 1;

inet_pton(AF_INET, dsts, ((char *)srtp->dst);

srtp->gateway = 0;

inet_pton(AF_INET, netmask, ((char *)srtp->netmask);

srtp->link_id = ifcn;