<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                如上文所述,目前Linux平臺中用于替代wext框架的是nl80211和cfg80211。其中cfg80211供Kernel網卡驅動開發使用,而nl80211 API則供用戶空間進程使用。 相比wext,nl80211的使用難度較大,因為在nl80211框架中,用戶進程和Kernel通信的手段沒有使用wext中的ioctl,而是采用了netlink機制。所以,雖然nl80211.h僅是定義了一些枚舉值和有限的數據結構,但其操作卻比較復雜。netlink是Linux平臺上一種基于socket的IPC通信機 制,它支持: * 用戶空間進程和Kernel通信。 * 用戶空間中進程間的通信。 不過,相比其他IPC機制,netlink最常用之處還是用戶空間進程和kernel模塊間的通信。鑒于netlink的復雜性,開源世界提供了幾個較為完備的基于netlink編程的框架,其中最著名的就是libnl。而Android也充分發揚拿來主義,在其system/core/libnl_2目錄中移植并精簡了libnl項目的代碼,得到一個小巧的libnl_2工程。 本節首先介紹netlink基本知識,然后介紹libnl開源庫的內容,最后結合實例介紹nl80211的用法。 **1.netlink編程[41]** netlink是一種基于socket的IPC通信機制,所以它需要解決如下兩個問題。 * 尋址:即如何定位通信對象。 * 雙方通信的數據格式:socket編程中,通信雙方傳遞的數據格式是由應用程序自己決定的。那么netlink是否有其自定義的數據格式呢? (1)netlink socket創建及綁定 netlink使用前,需要通過socket調用創建一個socket句柄,而socket函數的原型如下。 ~~~ int socket(int domain,int type,int protocol); ~~~ 對于netlink編程來說,注意以下事項。 * domain:必須設置為AF_NETLINK,表示此socket句柄將用于netlink。 * type:netlink是基于消息的IPC通信,所以該值為SOCK_DGRAM。注意,對netlink編程來說,內核并不區分type的值是SOCK_DGRAM還是SOCK_RAW。 * protocol:該值可根據需要設為NETLINK_ROUTE、NETLINK_NETFILTER。不同的協議代表Kernel中不同的子系統。例如NETLINK_ROUTE代表通信對象將是Kernel中負責route的子系統,而NETLINK_NETFILTER代表通信對象將是Kernel中負責Netfilter的子系統,而第2章碰到的NETLINK_KOBJECT_UEVENT則代表應用程序希望接收來自Kernel中的uevent消息。 使用netlink通信時,如何保證確保通信雙方能正確定位對方呢?原來,在netlink中,通信的另一方地址由一個數據類型為sockaddr_nl的結構體來唯一標示。該結構體的原型如下。 **netlink.h** ~~~ struct sockaddr_nl { // nl_family取值必須為AF_NETLINK或PF_NETLINK sa_family_t nl_family; unsigned short nl_pad; // 該值暫時無用,必須設為0 /* nl_pid看起來是用于存儲進程pid的,但實際上它只是用于標示一個netlink socket。所以,用戶空間 只要保證進程內該值的唯一性即可。另外,如果該值為0,表示通信的目標是Kernel。 */ __u32 nl_pid; /* 每一個netlink協議都支持最多32個多播組,加入多播組的成員都能接收到對應的多播消息。 例如NETLINK_ROUTE協議就有RTMGRP_LINK和RTMGRP_IPV4_IFADDR等多個多播組。每個多播組對應 不同的消息。之所以采用多播的方式,是因為它能減少消息發送/接收的次數。 nl_groups為0,表示只處理單播消息。 */ __u32 nl_groups; }; ~~~ 設置好地址后,通過bind函數將該地址和socket句柄綁定,這樣通信的另一方就正式確定了。 關于socket創建以及bind的使用,讀者可參考下面的例子。 ~~~ struct sockaddr_nl sa; memset(&sa, 0, sizeof(sa)); sa.nl_family = AF_NETLINK; sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR; // sa.nl_pid已經通過memset函數置為0了,代表此次netlink通信的目標是Kernel fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); bind(fd, (struct sockaddr *) &sa, sizeof(sa)); ~~~ netlink中關于protocol和多播group的取值比較分散,而且Linux的man手冊更新趕不上代碼的更新速度。建議讀者通過netlink.h尋找protocol的取值。通過protocol對應的頭文件去尋找多播group的定義。例如rtnetlink.h就定義了RTMGRP_LINK等多播組。 (2)netlink消息類型和標志 netlink的消息及處理是netlink編程中較為復雜的一部分。netlink中,所有消息都有一個消息頭,該頭由結構體nlmsghdr表達,原型如下。 ~~~ struct nlmsghdr { __u32 nlmsg_len; // 整個消息的長度,包括消息頭 __u16 nlmsg_type; // 消息類型,詳情見后文 __u16 nlmsg_flags; // 附加標志 __u32 nlmsg_seq; // 消息序列號 __u32 nlmsg_pid; // 發送該消息的nl_pid。該值為0,表示數據來自內核 }; ~~~ netlink定義了三種消息類型(type),分別如下。 * NLMSG_NOOP:無操作,程序可直接丟棄這類消息。 * NLMSG_ERROR:代表錯誤消息。這類消息攜帶的信息由結構體struct nlmsgerr表達。 * NLMSG_DONE:如果某個信息的數據量較大,需要分成多個netlink消息發送的話, NLMSG_DONE表示這是此信息最后一個消息分片包。 說實話,筆者覺得單從type值來區分netlink的消息有些不太好理解。如果從C/S角度來看,netlink消息可分為另外三種類型。 * Request消息:代表客戶端向服務端發起的請求。這類消息必須為nlmsg_flags設置NLM_F_REQUEST標志位。同時,客戶端最好為nlmsg_seq設置一個獨一無二的值,以區分自己發送的不同請求。 * Response消息:該類消息作為服務端對客戶端請求的回應。對應的消息類型是NLMSG_ERROR。注意,如果服務器處理請求成功,也返回該值。只不過nlmsgerr的error變量值為0。 * Notification消息:用于服務端向客戶端發送通知。由于它不對應任何請求,故nlmsg_seq一般取值為0。 基于上述討論,讀者會發現對于Response消息,無論服務端處理請求是否成功,都會返回NLMSG_ERROR消息,其對應的數據類型是nlmsgerr,原型如下。 ~~~ struct nlmsgerr { int error; // 值為負代表錯誤碼,值為0表示請求處理成功 // 攜帶對應請求消息的消息頭 // 由于只返回消息頭,故客戶端必須根據消息頭中的nlmsg_seq找到具體的消息 struct nlmsghdr msg; }; ~~~ 下面來看參數nlmsg_flags,它比type復雜,常設的位值有(通過“|”運算符將不同值“或”在一起)。 * NLM_F_REQUEST:代表請求消息。 * NLM_F_MULTI:代表消息分片中的一個。理論上說,由于nlmsghdr中的nlmsg_len為32位,其最大消息長度可達4GB,但內核實現時,最大消息的長度只有一個頁面(一般為4KB)。如果有大于4KB的信息要傳遞,就需將其分成多個消息發送。除最后一個分片消息外,其余消息都需置NLM_F_MULTI位。當然,結合上文所述,最后一個分片消息需要設置nlmsg_type為NLMSG_DONE。 * NLM_F_ACK:該標志將強制服務端接收并處理完請求后回復ACK給客戶端。 * * * * * **提示** netlink中nlmsg_flags的取值還有很多,感興趣的讀者可利用man 7 netlink命令閱讀相關知識。 * * * * * (3)netlink消息的處理 對于socket編程來說,一般客戶端會分配一個buffer用于接收數據,而后續解析該buffer中的netlink消息其實也是一件比較麻煩的事情。另外,由于netlink對消息的大小有字節對齊的要求,所以應用程序在構造自己的netlink消息時,也比較麻煩。不過好在netlink為我們提供了一些幫助宏,利用這些宏,netlink消息就不再麻煩了。以下代碼為netlink消息處理時常用的一些 宏。 ~~~ NLMSG_ALIGN(len)// 獲取len按4字節補齊后的長度。例如,如果len為1,則該宏返回的值應是4 // 整個消息包的長度,包括消息頭和數據長度len。該值用于填充消息頭中的nlmsg_len參數 NLMSG_LENGTH(len) // 返回按4字節對齊后整個消息包的長度。它和NLMSG_LENGTH最大的不同是該宏將長度按4字節對齊 NLMSG_SPACE(len) // 該宏等于NLMSG_ALIGN(NLMSG_LENGTH(len)) NLMSG_DATA(nlh) // 獲取消息中的數據起始地址 // 用于分片消息的處理。可獲取下一條分片消息。其用法見下文例子 NLMSG_NEXT(nlh,len) NLMSG_OK(nlh,len) // 用于判斷接收到的數據是否包含一個完整的netlink消息 // 用于返回數據的長度。注意,由于netlink消息在創建時會按4字節補齊 // 所以其數據真正的長度不能通過nlms_len來判斷 NLMSG_PAYLOAD(nlh,len) ~~~ 下面來看一個netlink消息解析的例子,讀者以后有需要,可以把它作為參考。 ~~~ // 本例基于linux netlink手冊。可通過man 7 netlink查閱 int len; char buf[4096]; struct iovec iov = { buf, sizeof(buf) }; struct sockaddr_nl sa; struct msghdr msg; // 用于recvmsg系統調用,和netlink沒關系 struct nlmsghdr *nh; msg = { (void *)&sa, sizeof(sa), &iov, 1, NULL, 0, 0 }; len = recvmsg(fd, &msg, 0); // recvmsg返回,可能存儲了多條netlink消息 // 開始接受接收buffer,注意NLMSG_NEXT宏,其內部會對len長度進行修改,以調整到下一個消息的起始 for (nh = (struct nlmsghdr *) buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT (nh, len)) { /* The end of multipart message. */ if (nh->nlmsg_type == NLMSG_DONE){ return; // 分片消息處理 } if (nh->nlmsg_type == NLMSG_ERROR){ struct nlmsgerr* pError = (struct nlmsgerr*)NLMSG_DATA(nh); // 獲取nlmsgerr的內容 ...... /* 錯誤或ACK處理*/ } void* data = NLMSG_DATA(nh); // 獲取數據的起始地址 ......// 處理數據 } ~~~ netlink的消息收發和普通的socket消息收發一樣,這里不贅述。 (4)netlink編程小結 netlink強制要求每個netlink消息都包含消息頭,其實它還定義了一個名為nlattr的結構體,用于規范載荷(Payload)的數據類型。其目的是希望Payload以屬性(attribute)的方式來描述自己。struct nlattr結構非常簡單,如下所示。 ~~~ struct nlattr { __u16 nla_len; // 屬性長度 __u16 nla_type; // 屬性類型 }; ~~~ netlink雖然復用了socket編程以方便應用程序和內核通信,但因為其文檔很少,而且不同protocol往往還有自己特定的數據結構,所以實際使用過程中難度較大。 本書不討論nlattr及netlink其他的內容。建議讀者考慮下文介紹的文檔更加豐富的libnl開源庫。 **2.libnl開源庫** 根據前文介紹,netlink API用起來相對麻煩,建議讀者考慮采用libnl開源庫,其官網站為http://www.infradead.org/~tgr/libnl/ 。libnl的內容也不少,其架構如圖3-50所示。 :-: ![](https://box.kancloud.cn/35049c47aa5b4ffe3b3f84ad876261e6_680x631.jpg) 圖3-50 libnl架構 由圖3-50可知,以下三個庫都基于其核心庫libnl。 * libnl-route:用于和Kernel中的Routing子系統交互。 * libnl-nf:用于和Kernel中的Netfilter子系統交互。 * libnl-genl:用于和Kernel中的Generic Netlink模塊交互。 * * * * * 提示 從圖也可看出netlink使用的復雜性。 * * * * * Android平臺移植并精簡了libnl和libnl-genl中的部分內容,得到了libnl_2(2表示libnl工程的版本號,最新版為3),目錄在system/core/libnl_2文件夾下。 本節介紹libnl中的一些常用API。詳細內容還請讀者參考其官方網站中的文檔,地址為 http://www.infradead.org/~tgr/libnl/doc/core.html。 (1)nl_sock結構體的使用 libnl以面向對象的方式重新封裝了netlink原有的API。其使用時必須分配一個nl_sock結構體。下面展示了和它相關的一些API及使用方法。 ~~~ #include <netlink/socket.h> // 分配和釋放nl_sock結構體 struct nl_sock *nl_socket_alloc(void) void nl_socket_free(struct nl_sock *sk) // nl_connet內部將通過bind函數將netlink socket和protocol對應的模塊進行綁定 int nl_connect(struct nl_sock *sk, int protocol) ~~~ linbl還可為每個nl_sock設置消息處理函數,相關API如下。 ~~~ // 為nl_sock對象設置一個回調函數,當該socket上收到消息后,就會回調此函數進行處理 // 回調函數及參數封裝在結構體struct nl_cb中 void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb); // 獲取該nl_sock設置的回調函數信息 struct nl_cb *nl_socket_get_cb(const struct nl_sock *sk); ~~~ 注意,以上兩個函數沒有文檔說明。建議使用另外一個控制力度更為精細的API。 ~~~ /* 此API對消息接收及處理的力度更為精細,其中: type類型包括NL_CB_ACK、NL_CB_SEQ_CHECK、NL_CB_INVALID等,可用于處理底層不同netlink消息的情況。 例如,當收到的netlink消息無效時,將調用NL_CB_INVALIDE設置的回調函數進行處理。 nl_cb_kinds指定消息回調函數的類型,可選值有NL_CB_CUSTOM,代表用戶設置的回調函數,NL_CB_DEFAULT 代表默認的處理函數。 回調函數的返回值包括以下。 NL_OK:表示處理正常。 NL_SKIP:表示停止當前netlink消息分析,轉而去分析接收buffer中下一條netlink消息(消息分  片的情況)。 NL_STOP:表示停止此次接收buffer中的消息分析。 */ int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type, enum nl_cb_kind kind, nl_recvmsg_msg_cb_t func, void *arg); ~~~ 另外,netlink還可設置錯誤消息(即專門處理nlmsgerr數據)處理回調函數,相關API如下。 ~~~ #include <netlink/handlers.h> // 必須包含此頭文件 // 設置錯誤消息處理 int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, nl_recvmsg_err_cb_t func, void * arg); typedef int(* nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg); ~~~ (2)libnl中的消息處理 libnl定義了自己的消息結構體struct nl_msg。不過它也提供API直接處理netlink的消息。常用的API如下。 ~~~ #include <netlink/msg.h> // 必須包含這個頭文件 // 下面這兩個函數計算netlink消息體中對應部分的長度 int nlmsg_size(int payloadlen); // 請參考圖來理解這兩個函數返回值的意義 int nlmsg_total_size(int payloadlen); ~~~ 關于netlink消息的長度如圖3-51所示。 :-: ![](https://box.kancloud.cn/8d0b0732351983775b03301307ba3dd6_1286x244.jpg) 圖3-51 nlmsg消息長度的計算 其他可直接處理netlink消息的API如下。 ~~~ struct nlmsghdr *nlmsg_next(struct nlmsghdr *hdr, int *remaining); int nlmsg_ok(const struct nlmsghdr *hdr, int remaining); /*定義一個消息處理的for循環宏,其值等于 for (int rem = len, pos = head; nlmsg_ok(pos, rem);\ pos = nlmsg_next(pos, &rem)) */ #define nlmsg_for_each(pos,head,en) ~~~ 開發者也可以通過libnl定義的消息結構體nl_msg進行相關操作,和nl_msg有關的API如下。 ~~~ struct nl_msg *nlmsg_alloc(void); void nlmsg_free(struct nl_msg *msg); // nl_msg內部肯定會指向一個netlink消息頭實例,下面這個函數用于填充netlink消息頭 struct nlmsghdr *nlmsg_put(struct nl_msg *msg, uint32_t port, uint32_t seqnr, int nlmsg_type, int payload, int nlmsg_flags); ~~~ (3)libnl中的消息發送和接收 netlink直接利用系統調用(如send、recv、sendmsg、recvmsg等)進行數據收發,而libnl封裝了自己特有的數據收發API。其中和發送有關的幾個主要API如下。 ~~~ // 直接發送netlink消息 int nl_sendto (struct nl_sock *sk, void *buf, size_t size) // 發送nl_msg消息 int nl_send (struct nl_sock *sk, struct nl_msg *msg) int nl_send_simple(struct nl_sock *sk, int type, int flags,void *buf, size_t size); ~~~ 常用的數據接收API如下。 ~~~ // 核心接收函數。nla參數用于存儲發送端的地址信息。creds用于存儲權限相關的信息 int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, unsigned char **buf, struct ucred **creds) // 內部通過nl_recv接收消息,然后通過cb回調結構體中的回調函數傳給接收者 int nl_recvmsgs (struct nl_sock *sk, struct nl_cb *cb) ~~~ (4)libnl-genl API介紹[41] 由圖3-50可知,libnl-genl封裝了對generic netlink模塊的處理,它基于libnl。Linux中關于generic netlink的說明幾乎沒有,建議大家參考libnl中的說明。一條genl消息的結構如圖3-52所示。 :-: ![](https://box.kancloud.cn/a75da960cdda3c55f55dd8b5c2518293_539x251.jpg) 圖3-52 genl消息結構 其中,genlmsghdr的原型如下。 ~~~ struct genlmsghdr { __u8 cmd; // cmd和version都和具體的案例有關 __u8 version; __u16 reserved; // 保留 }; ~~~ genl常用的API如下。 ~~~ // 和libnl的nl_connect類型,只不過協議類型為GENERIC_NETLINK int genl_connect (struct nl_sock *sk) // genlmsg_put用于填充圖中的nlmsghdr、genlmsghder和用戶自定義的消息頭。詳細內容見下文 void* genlmsg_put (struct nl_msg *msg, uint32_t port, uint32_t seq, int family, int hdrlen, int flags, uint8_t cmd, uint8_t version) // 用于獲取genl消息中攜帶的nlattr內容 struct nlattr* genlmsg_attrdata(const struct genlmsghdr *gnlh,int hdrlen) ~~~ 另外,genl還有幾個比較重要的API,它們和genl機制的內核實現有關,這里僅簡單介紹其中幾點內容。為實現genl機制,內核創建了一個虛擬的Generic Netlink Bus。所有genl的使用者(包含內核模塊或用戶空間進程)都會注冊到此Bus上。這些使用者注冊時,都需要填充一個名為genl_family的數據結構,該結構是一種身份標示。所以某一方只要設置好genlmsg_put中的 family參數,數據就能傳遞到對應的模塊。 family是一個整型,可讀性較差,所以genl使用者往往會指定一個字符串作為family name。而family name和family的對應關系則由genl中另外一個重要模塊去處理。這個模塊就是genl中的Controller,它也是Generic Bus使用者。其family name為"nlctrl",只不過它的family是固定的,目前取值為16(一般為它定義一個NETLINK_GENERIC宏)。Controller的一個重要作用就是為其他注冊者建立family name和family之間關系,也就是動態為其他注冊者分配family編號。另外,Controller也支持查詢,即返回當前Kernel中注冊的所有genl模塊的family name和family的值。 對用戶空間程序來說,只要知道family的值,就可和指定模塊進行通信了。libnl-genl封裝了上述操作,并提供了幾個常用的API。 ~~~ // 根據family name字符串去查詢family,該函數內部實現將發送查詢消息給Controller int genl_ctrl_resolve (struct nl_sock *sk, const char *name) /* 如果每次都向Controller去查詢family編號將嚴重影響效率,所以libnl-genl會把查詢到的信息 緩存起來。 下面這個函數將分配一個nl_cache列表,其內容存儲了當前注冊到Generic Netlink Bus上所有注 冊者的信息。 */ int genl_ctrl_alloc_cache (struct nl_sock *sk, struct nl_cache **result) // 根據family name從緩存中獲取對應的genl_family信息 struct genl_family * genl_ctrl_search_by_name (struct nl_cache *cache, const char *name) ~~~ * * * * * **提示** 相比直接使用netlink API,libnl對開發者更加友好,即使libnl封裝得再好,netlink編程依然不是一件輕松的事情。目前為止,筆者還沒有找到一篇文檔能全面描述netlink中的protocol及對應的多播組、genl中Controller模塊所支持的命令等至關重要的知識點。當年在Windows平臺做開發時,微軟為開發者提供的編程文檔中不僅有原理性說明,還有很多編程技巧。這些內容對開發者而言都是無價之寶。不過,指望Linux重新修訂、增補文檔無疑是一件異想天開的事情。在此筆者也只能希望讀者們在學習過程中注意收集資料并和大家一起分享了 * * * * * **3.nl80211實例** 了解netlink和libnl之后,現在來看nl80211。簡單來說,nl80211的核心就是通過netlink機制向Kernel中的無線網卡驅動發送特定的消息。只不過這些消息的類型、參數等都由nl80211.h定義。此處通過一個案例,學習如何通過nl80211觸發網卡進行無線網絡掃描。 **driver_nl80211.c::wpa_driver_nl80211_scan** ~~~ static int wpa_driver_nl80211_scan(void *priv, struct wpa_driver_scan_params *params) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; int ret = -1, timeout; struct nl_msg *msg, *rates = NULL; // 定義兩個nl_msg對象,rates和P2P有關,讀者可忽略它 drv->scan_for_auth = 0; // 創建nl80211消息,其中NL80211_CMD_TRIGGER_SCAN是Nl80211定義的命令,用于觸發網絡掃描 msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params); ......// P2P 處理 // 發送netlink消息 ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) goto nla_put_failure; ......// wpa_supplicant其他處理 ......// 錯誤處理 return ret; } ~~~ 上面代碼中構造無線網絡掃描nl_msg的重要函數nl80211_scan_common代碼如下所示。 **driver_nl80211.c::nl80211_scan_common** ~~~ static struct nl_msg * nl80211_scan_common (struct wpa_driver_nl80211_data *drv, u8 cmd, struct wpa_driver_scan_params *params) { struct nl_msg *msg; int err; size_t i; // 分配一個nl_msg對象 msg = nlmsg_alloc(); /* 調用nl80211_cmd函數填充nl_msg中的信息,其內部代碼如下。 static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg, int flags, uint8_t cmd){ return genlmsg_put(msg, 0, 0, drv->global->nl80211_id,0, flags, cmd, 0); } */ nl80211_cmd(drv, msg, 0, cmd); /* nl80211消息的參數通過netlink中的nlattr來存儲。NL80211_ATTR_IFINDEX代表 此次操作所指定的網絡設備編號。 */ nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (params->num_ssids) { struct nl_msg *ssids = nlmsg_alloc(); for (i = 0; i < params->num_ssids; i++) { nla_put(ssids, i + 1, params->ssids[i].ssid_len,params->ssids[i].ssid); ...... } // netlink支持消息嵌套,即屬性中攜帶的數據可以是另外一個nl_msg消息 err = nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); nlmsg_free(ssids); ...... } ......// 其他處理 return msg; ......// 錯誤處理 } ~~~ 由上面的例子可知,nl80211其實就是利用netlink機制將一些802.11相關的命令和參數發送給驅動去執行。這些命令和參數信息可通過nl80211頭文件查詢。 * * * * * **提示** 本書后續章節將分析wpa_supplicant8,讀者可參考external/wpa_supplicant8/wpa_supplicant/src/drivers/nl80211_copy.h。 * * * * * 首先,nl80211_copy.h定義其支持的命令,如下所示。 **nl80211_copy.h** ~~~ enum nl80211_commands { NL80211_CMD_UNSPEC, NL80211_CMD_GET_WIPHY, NL80211_CMD_SET_WIPHY, ...... NL80211_CMD_GET_INTERFACE, NL80211_CMD_SET_INTERFACE, ...... NL80211_CMD_SET_BSS, NL80211_CMD_SET_REG, ......// 一共定義了94條命令 } ~~~ 然后定義屬性的取值,如下所示。 ~~~ enum nl80211_attrs { NL80211_ATTR_UNSPEC, NL80211_ATTR_WIPHY, NL80211_ATTR_WIPHY_NAME, NL80211_ATTR_IFINDEX, NL80211_ATTR_IFNAME, NL80211_ATTR_IFTYPE, NL80211_ATTR_MAC, ......// 一共定義了155條屬性 } ~~~ 頭文件中對命令、屬性等信息的注釋都非常詳細。本節不贅述,請讀者自行閱讀該文件。 * * * * * **提示** 相比wext而言,nl80211的使用難度明顯要復雜,其中重要原因是它是基于netlink編程的。而且,如果沒有libnl的支持,相信使用難度會更大。但從Wi-Fi角度來看,nl80211和wext到沒有太大區別,二者都是緊緊圍繞MAC層service來設計數據結構的。 * * * * *
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看