Broadcom WLAN 드라이브 분석
147199 단어 wifi
1. scan command를 WLAN firmware에 어떻게 보내는지 살펴보자.
안드로이드 플랫폼을 예로 들면 안드로이드 프레임워크의 코드부터 살펴보겠습니다.
1. WifiStateMachine.java에는 다음과 같은 함수가 있습니다
public void startScan(boolean forceActive) {
sendMessage(obtainMessage(CMD_START_SCAN, forceActive ?
SCAN_ACTIVE : SCAN_PASSIVE, 0));
}
2. processMessage() 함수에서 CMD 처리START_SCAN
@Override
public boolean processMessage(Message message) {
if (DBG) log(getName() + message.toString() + "
");
boolean eventLoggingEnabled = true;
switch(message.what) {
case CMD_SET_SCAN_TYPE:
if (message.arg1 == SCAN_ACTIVE) {
WifiNative.setScanModeCommand(true);
} else {
WifiNative.setScanModeCommand(false);
}
break;
// CMD_START_SCAN
case CMD_START_SCAN:
eventLoggingEnabled = false;
WifiNative.scanCommand(message.arg1 == SCAN_ACTIVE);
mScanResultIsPending = true;
break;
...
default:
return NOT_HANDLED;
}
if (eventLoggingEnabled) {
EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what);
}
return HANDLED;
}
3. 이제 JNI 함수에
static jboolean android_net_wifi_scanCommand(JNIEnv* env, jobject, jboolean forceActive)
{
jboolean result;
// Ignore any error from setting the scan mode.
// The scan will still work.
if (forceActive && !sScanModeActive)
doSetScanMode(true);
// ,"SCAN" result = doBooleanCommand("OK", "SCAN");
if (forceActive && !sScanModeActive)
doSetScanMode(sScanModeActive);
return result;
}
4. DoBoolean Command 계속 따라가기()
static jboolean doBooleanCommand(const char* expect, const char* fmt, ...)
{
char buf[BUF_SIZE];
va_list args;
va_start(args, fmt);
int byteCount = vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
if (byteCount < 0 || byteCount >= BUF_SIZE) {
return JNI_FALSE;
}
char reply[BUF_SIZE];
if (doCommand(buf, reply, sizeof(reply)) != 0) {
return JNI_FALSE;
}
return (strcmp(reply, expect) == 0);
}
5. DoCommand 호출()
static int doCommand(const char *cmd, char *replybuf, int replybuflen)
{
size_t reply_len = replybuflen - 1;
// Android HAL API
if (::wifi_command(cmd, replybuf, &reply_len) != 0)
return -1;
else {
// Strip off trailing newline
if (reply_len > 0 && replybuf[reply_len-1] == '
')
replybuf[reply_len-1] = '\0';
else
replybuf[reply_len] = '\0';
return 0;
}
}
6. 와이파이가 실행되었습니다.c의 와이파이command()
int wifi_command(const char *command, char *reply, size_t *reply_len)
{
// ctrl_conn
return wifi_send_command(ctrl_conn, command, reply, reply_len);
}
int wifi_send_command(struct wpa_ctrl *ctrl, const char *cmd, char *reply, size_t *reply_len) { int ret; if (ctrl_conn == NULL) { LOGV("Not connected to wpa_supplicant - \"%s\" command dropped.
", cmd); return -1; } // , wpa_supplicant ! cmd "SCAN" ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), reply, reply_len, NULL); if (ret == -2) { LOGD("'%s' command timed out.
", cmd); /* unblocks the monitor receive socket for termination */ write(exit_sockets[0], "T", 1); return -2; } else if (ret < 0 || strncmp(reply, "FAIL", 4) == 0) { return -1; } if (strncmp(cmd, "PING", 4) == 0) { reply[*reply_len] = '\0'; } return 0; }
7. wpa_ctrl_request가command를 성공적으로 보내려면 wpa 를 호출해야 합니다ctrl_open (), 그래서 와이파이를 보도록 하겠습니다.connect_to_supplicant():int wifi_connect_to_supplicant() { char ifname[256]; char supp_status[PROPERTY_VALUE_MAX] = {'\0'}; /* Make sure supplicant is running */ if (!property_get(SUPP_PROP_NAME, supp_status, NULL) || strcmp(supp_status, "running") != 0) { LOGE("Supplicant not running, cannot connect"); return -1; } if (access(IFACE_DIR, F_OK) == 0) { snprintf(ifname, sizeof(ifname), "%s/%s", IFACE_DIR, iface); } else { strlcpy(ifname, iface, sizeof(ifname)); } // ctrl_conn ctrl_conn = wpa_ctrl_open(ifname); if (ctrl_conn == NULL) { LOGE("Unable to open connection to supplicant on \"%s\": %s", ifname, strerror(errno)); return -1; } // monitor_conn monitor_conn = wpa_ctrl_open(ifname); if (monitor_conn == NULL) { wpa_ctrl_close(ctrl_conn); ctrl_conn = NULL; return -1; } if (wpa_ctrl_attach(monitor_conn) != 0) { wpa_ctrl_close(monitor_conn); wpa_ctrl_close(ctrl_conn); ctrl_conn = monitor_conn = NULL; return -1; } if (socketpair(AF_UNIX, SOCK_STREAM, 0, exit_sockets) == -1) { wpa_ctrl_close(monitor_conn); wpa_ctrl_close(ctrl_conn); ctrl_conn = monitor_conn = NULL; return -1; } return 0; }
여기의 iface는property에서 꺼낸 것입니다:
property_get("wifi.interface", iface, WIFI_TEST_INTERFACE);
이 값은 device/samsung/tuna/device.mk에 다음 코드가 있습니다.
wifi.interface=wlan0
8. 그럼 다음은 wpasupplicant에 걸렸습니다. 이 "SCAN"command를 처리하러 가야 합니다.command를 어디서 처리하는지 알기 위해서는 wpa를 봐야 한다supplicant 초기화 프로세스:
8.1main()부터 보기int main(int argc, char *argv[]) { int c, i; struct wpa_interface *ifaces, *iface; int iface_count, exitcode = -1; struct wpa_params params; struct wpa_global *global; if (os_program_init()) return -1; os_memset(¶ms, 0, sizeof(params)); params.wpa_debug_level = MSG_INFO; iface = ifaces = os_zalloc(sizeof(struct wpa_interface)); if (ifaces == NULL) return -1; iface_count = 1; wpa_supplicant_fd_workaround(); ... exitcode = 0; global = wpa_supplicant_init(¶ms); if (global == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize wpa_supplicant"); exitcode = -1; goto out; } for (i = 0; exitcode == 0 && i < iface_count; i++) { if ((ifaces[i].confname == NULL && ifaces[i].ctrl_interface == NULL) || ifaces[i].ifname == NULL) { if (iface_count == 1 && (params.ctrl_interface || params.dbus_ctrl_interface)) break; usage(); exitcode = -1; break; } // if (wpa_supplicant_add_iface(global, &ifaces[i]) == NULL) exitcode = -1; } ... return exitcode; }
8.2 wpa 호출supplicant_add_iface()struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, struct wpa_interface *iface) { struct wpa_supplicant *wpa_s; struct wpa_interface t_iface; struct wpa_ssid *ssid; if (global == NULL || iface == NULL) return NULL; // ! wpa_s wpa_s = wpa_supplicant_alloc(); if (wpa_s == NULL) return NULL; wpa_s->global = global; t_iface = *iface; ... // ,wpa_s wpa_supplicant_init_iface() if (wpa_supplicant_init_iface(wpa_s, &t_iface)) { wpa_printf(MSG_DEBUG, "Failed to add interface %s", iface->ifname); wpa_supplicant_deinit_iface(wpa_s, 0); os_free(wpa_s); return NULL; } ... return wpa_s; }
8.3 wpa 호출supplicant_init_iface()static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, struct wpa_interface *iface) { const char *ifname, *driver; struct wpa_driver_capa capa; ... // main() ,iface->confname -c if (iface->confname) { #ifdef CONFIG_BACKEND_FILE wpa_s->confname = os_rel2abs_path(iface->confname); if (wpa_s->confname == NULL) { wpa_printf(MSG_ERROR, "Failed to get absolute path " "for configuration file '%s'.", iface->confname); return -1; } wpa_printf(MSG_DEBUG, "Configuration file '%s' -> '%s'", iface->confname, wpa_s->confname); #else /* CONFIG_BACKEND_FILE */ wpa_s->confname = os_strdup(iface->confname); #endif /* CONFIG_BACKEND_FILE */ // wpa_s->conf iface->confname // wpa_supplicant -c wpa_s->conf = wpa_config_read(wpa_s->confname); if (wpa_s->conf == NULL) { wpa_printf(MSG_ERROR, "Failed to read or parse " "configuration '%s'.", wpa_s->confname); return -1; } /* * Override ctrl_interface and driver_param if set on command * line. */ if (iface->ctrl_interface) { os_free(wpa_s->conf->ctrl_interface); wpa_s->conf->ctrl_interface = os_strdup(iface->ctrl_interface); } if (iface->driver_param) { os_free(wpa_s->conf->driver_param); wpa_s->conf->driver_param = os_strdup(iface->driver_param); } } else wpa_s->conf = wpa_config_alloc_empty(iface->ctrl_interface, iface->driver_param); ... if (wpa_supplicant_driver_init(wpa_s) < 0) return -1; if (wpa_s->conf->country[0] && wpa_s->conf->country[1] && wpa_drv_set_country(wpa_s, wpa_s->conf->country)) { wpa_printf(MSG_DEBUG, "Failed to set country"); return -1; } wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); if (wpas_wps_init(wpa_s)) return -1; if (wpa_supplicant_init_eapol(wpa_s) < 0) return -1; wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); // , wpa_s wpa_supplicant_ctrl_iface_init() wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s); ... if (wpa_bss_init(wpa_s) < 0) return -1; return 0; }
8.4 wpa 호출supplicant_ctrl_iface_init()struct ctrl_iface_priv * wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) { ... // handler eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv); wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); os_free(buf); return priv; ... }
8.5 분명히 모든command는 wpasupplicant_ctrl_iface_수신static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { // wpa_s struct wpa_supplicant *wpa_s = eloop_ctx; struct ctrl_iface_priv *priv = sock_ctx; char buf[256]; int res; struct sockaddr_un from; socklen_t fromlen = sizeof(from); char *reply = NULL; size_t reply_len = 0; int new_attached = 0; res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { perror("recvfrom(ctrl_iface)"); return; } buf[res] = '\0'; if (os_strcmp(buf, "ATTACH") == 0) { if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen)) reply_len = 1; else { new_attached = 1; reply_len = 2; } } else if (os_strcmp(buf, "DETACH") == 0) { if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen)) reply_len = 1; else reply_len = 2; } else if (os_strncmp(buf, "LEVEL ", 6) == 0) { if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen, buf + 6)) reply_len = 1; else reply_len = 2; } else { // command command, reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf, &reply_len); } if (reply) { sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); os_free(reply); } else if (reply_len == 1) { sendto(sock, "FAIL
", 5, 0, (struct sockaddr *) &from, fromlen); } else if (reply_len == 2) { sendto(sock, "OK
", 3, 0, (struct sockaddr *) &from, fromlen); } if (new_attached) eapol_sm_notify_ctrl_attached(wpa_s->eapol); }
9.wpa에서supplicant_ctrl_iface_process()에서 "SCAN"command 처리char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, char *buf, size_t *resp_len) { char *reply; const int reply_size = 2048; int ctrl_rsp = 0; int reply_len; ... reply = os_malloc(reply_size); if (reply == NULL) { *resp_len = 1; return NULL; } os_memcpy(reply, "OK
", 3); reply_len = 3; if (os_strcmp(buf, "PING") == 0) { os_memcpy(reply, "PONG
", 5); reply_len = 5; } ... else if (os_strcmp(buf, "SCAN") == 0) { wpa_s->scan_req = 2; // wpa_supplicant_req_scan(wpa_s, 0, 0); } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) { reply_len = wpa_supplicant_ctrl_iface_scan_results( wpa_s, reply, reply_size); } ... else { os_memcpy(reply, "UNKNOWN COMMAND
", 16); reply_len = 16; } if (reply_len < 0) { os_memcpy(reply, "FAIL
", 5); reply_len = 5; } if (ctrl_rsp) eapol_sm_notify_ctrl_response(wpa_s->eapol); *resp_len = reply_len; return reply; }
10. wpa 호출supplicant_req_scan()void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) { ... if (eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL) && wpa_s->conf->ap_scan == 1) { // ssid wpa_s->conf // Android framework , UI AP/SSID , // Settings App WifiManager addNetwork() // set wpa_s->conf->ssid // WifiManager saveNetwork() wpa_supplicant // wpa_s->conf iface->confname // struct wpa_ssid *ssid = wpa_s->conf->ssid; while (ssid) { if (!ssid->disabled && ssid->scan_ssid) break; ssid = ssid->next; } if (ssid) { wpa_msg(wpa_s, MSG_DEBUG, "Not rescheduling scan to " "ensure that specific SSID scans occur"); return; } } wpa_msg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec", sec, usec); eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL); // handler eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); }
timer를 통한handler가 wpa를 촉발할 예정입니다supplicant_scan().
11. wpa 호출supplicant_scan()static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; struct wpa_ssid *ssid; int scan_req = 0, ret; struct wpabuf *wps_ie = NULL; ... params.filter_ssids = wpa_supplicant_build_filter_ssids( wpa_s->conf, ¶ms.num_filter_ssids); // ret = wpa_supplicant_trigger_scan(wpa_s, ¶ms); wpabuf_free(wps_ie); os_free(params.freqs); os_free(params.filter_ssids); if (ret) { wpa_printf(MSG_WARNING, "Failed to initiate AP scan."); if (prev_state != wpa_s->wpa_state) wpa_supplicant_set_state(wpa_s, prev_state); wpa_supplicant_req_scan(wpa_s, 1, 0); } }
12. wpa 호출supplicant_trigger_scan()int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { int ret; wpa_supplicant_notify_scanning(wpa_s, 1); if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) ret = ieee80211_sta_req_scan(wpa_s, params); else // ret = wpa_drv_scan(wpa_s, params); if (ret) { wpa_supplicant_notify_scanning(wpa_s, 0); wpas_notify_scan_done(wpa_s, 0); } else wpa_s->scan_runs++; return ret; }
13. wpa 호출drv_scan()static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { if (wpa_s->driver->scan2) return wpa_s->driver->scan2(wpa_s->drv_priv, params); return -1; }
14. 여기서 우리는 nl80211 인터페이스를 사용했다고 가정하면drivernl80211.c의 wpadriver_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 = 0, timeout; struct nl_msg *msg, *ssids, *freqs; size_t i; msg = nlmsg_alloc(); ssids = nlmsg_alloc(); freqs = nlmsg_alloc(); if (!msg || !ssids || !freqs) { nlmsg_free(msg); nlmsg_free(ssids); nlmsg_free(freqs); return -1; } os_free(drv->filter_ssids); drv->filter_ssids = params->filter_ssids; params->filter_ssids = NULL; drv->num_filter_ssids = params->num_filter_ssids; // NL80211_CMD_TRIGGER_SCAN genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, NL80211_CMD_TRIGGER_SCAN, 0); NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); for (i = 0; i < params->num_ssids; i++) { wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", params->ssids[i].ssid, params->ssids[i].ssid_len); NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len, params->ssids[i].ssid); } if (params->num_ssids) nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); if (params->extra_ies) { wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan extra IEs", params->extra_ies, params->extra_ies_len); NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len, params->extra_ies); } if (params->freqs) { for (i = 0; params->freqs[i]; i++) { wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " "MHz", params->freqs[i]); NLA_PUT_U32(freqs, i + 1, params->freqs[i]); } nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs); } ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; ... }
15. scan 메시지는 넷링크에서 Linux 커널로 들어갑니다.
nl80211.c의 nl80211ops의 정의는 대응하는command handler:{ .cmd = NL80211_CMD_TRIGGER_SCAN, .doit = nl80211_trigger_scan, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, },
16. nl80211 호출trigger_scan()static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) { ... request->wdev = wdev; request->wiphy = &rdev->wiphy; request->scan_start = jiffies; rdev->scan_req = request; // : err = rdev_scan(rdev, request); if (!err) { nl80211_send_scan_start(rdev, wdev); if (wdev->netdev) dev_hold(wdev->netdev); } else { out_free: rdev->scan_req = NULL; kfree(request); } return err; }
17. rdev 호출scan()static inline int rdev_scan(struct cfg80211_registered_device *rdev, struct cfg80211_scan_request *request) { int ret; trace_rdev_scan(&rdev->wiphy, request); ret = rdev->ops->scan(&rdev->wiphy, request); trace_rdev_return_int(&rdev->wiphy, ret); return ret; }
18. 이전 절의 분석에 의하면 brcmfcfg80211_scan () 호출됨static s32 brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) { struct net_device *ndev = request->wdev->netdev; s32 err = 0; brcmf_dbg(TRACE, "Enter
"); if (!check_vif_up(container_of(request->wdev, struct brcmf_cfg80211_vif, wdev))) return -EIO; err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL); if (err) brcmf_err("scan error (%d)
", err); brcmf_dbg(TRACE, "Exit
"); return err; }
19. Brcmf 호출cfg80211_escan()static s32 brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_scan_request *request, struct cfg80211_ssid *this_ssid) { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev); struct cfg80211_ssid *ssids; struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int; u32 passive_scan; bool escan_req; bool spec_scan; s32 err; u32 SSID_len; ... escan_req = false; if (request) { /* scan bss */ ssids = request->ssids; escan_req = true; } else { /* scan in ibss */ /* we don't do escan in ibss */ ssids = this_ssid; } cfg->scan_request = request; set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); if (escan_req) { // run cfg->escan_info.run = brcmf_run_escan; err = brcmf_p2p_scan_prep(wiphy, request, ifp->vif); if (err) goto scan_out; // brcmf_do_escan err = brcmf_do_escan(cfg, wiphy, ndev, request); if (err) goto scan_out; } ... return 0; ... }
20. Brcmf 호출do_escan()static s32 brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_scan_request *request) { s32 err; u32 passive_scan; struct brcmf_scan_results *results; struct escan_info *escan = &cfg->escan_info; brcmf_dbg(SCAN, "Enter
"); escan->ndev = ndev; escan->wiphy = wiphy; escan->escan_state = WL_ESCAN_STATE_SCANNING; passive_scan = cfg->active_scan ? 0 : 1; err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_SET_PASSIVE_SCAN, passive_scan); if (err) { brcmf_err("error (%d)
", err); return err; } brcmf_set_mpc(ndev, 0); results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; results->version = 0; results->count = 0; results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE; // run err = escan->run(cfg, ndev, request, WL_ESCAN_ACTION_START); if (err) brcmf_set_mpc(ndev, 1); return err; }
21. Brcmf 호출run_escan()static s32 brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, struct cfg80211_scan_request *request, u16 action) { s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE + offsetof(struct brcmf_escan_params_le, params_le); struct brcmf_escan_params_le *params; s32 err = 0; brcmf_dbg(SCAN, "E-SCAN START
"); if (request != NULL) { /* Allocate space for populating ssids in struct */ params_size += sizeof(u32) * ((request->n_channels + 1) / 2); /* Allocate space for populating ssids in struct */ params_size += sizeof(struct brcmf_ssid) * request->n_ssids; } params = kzalloc(params_size, GFP_KERNEL); if (!params) { err = -ENOMEM; goto exit; } BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN); // scan brcmf_escan_prep(¶ms->params_le, request); params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION); params->action = cpu_to_le16(action); params->sync_id = cpu_to_le16(0x1234); // firmware "escan" err = brcmf_fil_iovar_data_set(netdev_priv(ndev), "escan", params, params_size); if (err) { if (err == -EBUSY) brcmf_dbg(INFO, "system busy : escan canceled
"); else brcmf_err("error (%d)
", err); } kfree(params); exit: return err; }
여기 Brcmf에 들어갈 필요가 있습니다run_escan () 보세요.scan 매개 변수의 설정이 관련되어 있기 때문입니다.static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le, struct cfg80211_scan_request *request) { u32 n_ssids; u32 n_channels; s32 i; s32 offset; u16 chanspec; char *ptr; struct brcmf_ssid_le ssid_le; ... if (n_ssids > 0) { // active scan? offset = offsetof(struct brcmf_scan_params_le, channel_list) + n_channels * sizeof(u16); offset = roundup(offset, sizeof(u32)); ptr = (char *)params_le + offset; for (i = 0; i < n_ssids; i++) { memset(&ssid_le, 0, sizeof(ssid_le)); ssid_le.SSID_len = cpu_to_le32(request->ssids[i].ssid_len); memcpy(ssid_le.SSID, request->ssids[i].ssid, request->ssids[i].ssid_len); if (!ssid_le.SSID_len) brcmf_dbg(SCAN, "%d: Broadcast scan
", i); else brcmf_dbg(SCAN, "%d: scan for %s size =%d
", i, ssid_le.SSID, ssid_le.SSID_len); memcpy(ptr, &ssid_le, sizeof(ssid_le)); ptr += sizeof(ssid_le); } } else { // n_ssids 0 passive scan? brcmf_dbg(SCAN, "Broadcast scan %p
", request->ssids); if ((request->ssids) && request->ssids->ssid_len) { brcmf_dbg(SCAN, "SSID %s len=%d
", params_le->ssid_le.SSID, request->ssids->ssid_len); params_le->ssid_le.SSID_len = cpu_to_le32(request->ssids->ssid_len); memcpy(¶ms_le->ssid_le.SSID, request->ssids->ssid, request->ssids->ssid_len); } } /* Adding mask to channel numbers */ params_le->channel_num = cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) | (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK)); }
여기는 active scan/passive scan에 대한 아직 제 추측입니다. 근거는 wpasupplicant에서 wpa에 대해driver_scan_params에 대한 몇 가지 참고 사항:/** * struct wpa_driver_scan_params - Scan parameters * Data for struct wpa_driver_ops::scan2(). */ struct wpa_driver_scan_params { /** * ssids - SSIDs to scan for */ struct wpa_driver_scan_ssid { /** * ssid - specific SSID to scan for (ProbeReq) * %NULL or zero-length SSID is used to indicate active scan * with wildcard SSID. */ const u8 *ssid; /** * ssid_len: Length of the SSID in octets */ size_t ssid_len; } ssids[WPAS_MAX_SCAN_SSIDS]; /** * num_ssids - Number of entries in ssids array * Zero indicates a request for a passive scan. */ size_t num_ssids; /** * extra_ies - Extra IE(s) to add into Probe Request or %NULL */ const u8 *extra_ies; /** * extra_ies_len - Length of extra_ies in octets */ size_t extra_ies_len; /** * freqs - Array of frequencies to scan or %NULL for all frequencies * * The frequency is set in MHz. The array is zero-terminated. */ int *freqs; /** * filter_ssids - Filter for reporting SSIDs * * This optional parameter can be used to request the driver wrapper to * filter scan results to include only the specified SSIDs. %NULL * indicates that no filtering is to be done. This can be used to * reduce memory needs for scan results in environments that have large * number of APs with different SSIDs. * * The driver wrapper is allowed to take this allocated buffer into its * own use by setting the pointer to %NULL. In that case, the driver * wrapper is responsible for freeing the buffer with os_free() once it * is not needed anymore. */ struct wpa_driver_scan_filter { u8 ssid[32]; size_t ssid_len; } *filter_ssids; /** * num_filter_ssids - Number of entries in filter_ssids array */ size_t num_filter_ssids; };
22. Brcmf 호출fil_iovar_data_set()s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data, u32 len) { struct brcmf_pub *drvr = ifp->drvr; s32 err; u32 buflen; mutex_lock(&drvr->proto_block); brcmf_dbg(FIL, "name=%s, len=%d
", name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, sizeof(drvr->proto_buf)); if (buflen) { // err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, buflen, true); } else { err = -EPERM; brcmf_err("Creating iovar failed
"); } mutex_unlock(&drvr->proto_block); return err; }
23. Brcmf 호출fil_cmd_data()static s32 brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) { struct brcmf_pub *drvr = ifp->drvr; s32 err; if (drvr->bus_if->state != BRCMF_BUS_DATA) { brcmf_err("bus is down. we have nothing to do.
"); return -EIO; } if (data != NULL) len = min_t(uint, len, BRCMF_DCMD_MAXLEN); if (set) err = brcmf_proto_cdc_set_dcmd(drvr, ifp->ifidx, cmd, data, len); else err = brcmf_proto_cdc_query_dcmd(drvr, ifp->ifidx, cmd, data, len); if (err >= 0) err = 0; else brcmf_err("Failed err=%d
", err); return err; }
24. Brcmf 호출proto_cdc_set_dcmd()int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len) { struct brcmf_proto *prot = drvr->prot; struct brcmf_proto_cdc_dcmd *msg = &prot->msg; int ret = 0; u32 flags, id; brcmf_dbg(CDC, "Enter, cmd %d len %d
", cmd, len); memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd)); msg->cmd = cpu_to_le32(cmd); msg->len = cpu_to_le32(len); flags = (++prot->reqid << CDC_DCMD_ID_SHIFT) | CDC_DCMD_SET; flags = (flags & ~CDC_DCMD_IF_MASK) | (ifidx << CDC_DCMD_IF_SHIFT); msg->flags = cpu_to_le32(flags); if (buf) memcpy(prot->buf, buf, len); // ret = brcmf_proto_cdc_msg(drvr); if (ret < 0) goto done; ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len); if (ret < 0) goto done; ... done: return ret; }
25. Brcmf 호출proto_cdc_msg()static int brcmf_proto_cdc_msg(struct brcmf_pub *drvr) { struct brcmf_proto *prot = drvr->prot; int len = le32_to_cpu(prot->msg.len) + sizeof(struct brcmf_proto_cdc_dcmd); brcmf_dbg(CDC, "Enter
"); /* NOTE : cdc->msg.len holds the desired length of the buffer to be * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area * is actually sent to the dongle */ if (len > CDC_MAX_MSG_SIZE) len = CDC_MAX_MSG_SIZE; /* Send request */ return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&prot->msg, len); }
26. Brcmf를 통해bus_txctl () 리퀘스트 보내기static inline int brcmf_bus_txctl(struct brcmf_bus *bus, unsigned char *msg, uint len) { return bus->ops->txctl(bus->dev, msg, len); }
지난 절에서 분석한 바와 같이 여기bus->ops->txctl는 Brcmf 를 호출합니다sdbrcm_bus_txctl()static int brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) { u8 *frame; u16 len; u32 swheader; uint retries = 0; u8 doff = 0; int ret = -1; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; unsigned long flags; ... if (ret == -1) { brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), frame, len, "Tx Frame:
"); brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && BRCMF_HDRS_ON(), frame, min_t(u16, len, 16), "TxHdr:
"); do { sdio_claim_host(bus->sdiodev->func[1]); // brcmf_tx_frame ret = brcmf_tx_frame(bus, frame, len); sdio_release_host(bus->sdiodev->func[1]); } while (ret < 0 && retries++ < TXRETRIES); } ... }
이로써 scan command와 대응하는 파라미터를 WLAN Firmware에 보냈습니다.다음은 scan result를 기다리는 것입니다.
2. WLAN firmware에서 scan result 수신
우리가 지난 절에서 분석한 Brcmfrx_frames () 보기 시작합니다.
1. Brcmf 시전rx_frames()void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) { ... skb_queue_walk_safe(skb_list, skb, pnext) { skb_unlink(skb, skb_list); ... /* Process special event packets and then discard them */ // scan result special event packets // brcmf_fweh_process_skb(drvr, skb, &ifidx); if (drvr->iflist[ifidx]) { ifp = drvr->iflist[ifidx]; ifp->ndev->last_rx = jiffies; } if (!(ifp->ndev->flags & IFF_UP)) { brcmu_pkt_buf_free_skb(skb); continue; } ifp->stats.rx_bytes += skb->len; ifp->stats.rx_packets++; if (in_interrupt()) netif_rx(skb); else /* If the receive is not processed inside an ISR, * the softirqd must be woken explicitly to service * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled * by netif_rx_ni(), but in earlier kernels, we need * to do it manually. */ netif_rx_ni(skb); } }
2.brcmf로 입장fweh_process_skb()static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr, struct sk_buff *skb, u8 *ifidx) { struct brcmf_event *event_packet; u8 *data; u16 usr_stype; /* only process events when protocol matches */ if (skb->protocol != cpu_to_be16(ETH_P_LINK_CTL)) return; /* check for BRCM oui match */ event_packet = (struct brcmf_event *)skb_mac_header(skb); data = (u8 *)event_packet; data += BRCMF_EVENT_OUI_OFFSET; if (memcmp(BRCM_OUI, data, DOT11_OUI_LEN)) return; /* final match on usr_subtype */ data += DOT11_OUI_LEN; usr_stype = get_unaligned_be16(data); if (usr_stype != BCMILCP_BCM_SUBTYPE_EVENT) return; // brcmf_fweh_process_event(drvr, event_packet, ifidx); }
3. Brcmf 호출fweh_process_event()void brcmf_fweh_process_event(struct brcmf_pub *drvr, struct brcmf_event *event_packet, u8 *ifidx) { ... event = kzalloc(sizeof(*event) + datalen, alloc_flag); if (!event) return; event->code = code; event->ifidx = *ifidx; /* use memcpy to get aligned event message */ memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg)); memcpy(event->data, data, datalen); memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN); // brcmf_fweh_queue_event(fweh, event); }
4. Brcmf 호출fweh_queue_event()static void brcmf_fweh_queue_event(struct brcmf_fweh_info *fweh, struct brcmf_fweh_queue_item *event) { ulong flags; spin_lock_irqsave(&fweh->evt_q_lock, flags); list_add_tail(&event->q, &fweh->event_q); spin_unlock_irqrestore(&fweh->evt_q_lock, flags); schedule_work(&fweh->event_work); // work queue }
여기 이벤트work()는 brcmffweh_attach()에서 초기화(호출 순서: brcmf sdbrcm probe()-> brcmfattach() -> brcmf_fweh_attach())void brcmf_fweh_attach(struct brcmf_pub *drvr) { struct brcmf_fweh_info *fweh = &drvr->fweh; INIT_WORK(&fweh->event_work, brcmf_fweh_event_worker); spin_lock_init(&fweh->evt_q_lock); INIT_LIST_HEAD(&fweh->event_q); }
5.분명히 brcmffweh_event_worker () 가 호출될 것입니다static void brcmf_fweh_event_worker(struct work_struct *work) { ... while ((event = brcmf_fweh_dequeue_event(fweh))) { ... /* special handling of interface event */ if (event->code == BRCMF_E_IF) { brcmf_fweh_handle_if_event(drvr, &emsg, event->data); goto event_free; } ifp = drvr->iflist[emsg.bsscfgidx]; // brcmf_fweh_call_event_handler() err = brcmf_fweh_call_event_handler(ifp, event->code, &emsg, event->data); if (err) { brcmf_err("event handler failed (%d)
", event->code); err = 0; } event_free: kfree(event); }
6.brcmf로 입장fweh_call_event_handler()static int brcmf_fweh_call_event_handler(struct brcmf_if *ifp, enum brcmf_fweh_event_code code, struct brcmf_event_msg *emsg, void *data) { struct brcmf_fweh_info *fweh; int err = -EINVAL; if (ifp) { fweh = &ifp->drvr->fweh; /* handle the event if valid interface and handler */ if (ifp->ndev && fweh->evt_handler[code]) err = fweh->evt_handler[code](ifp, emsg, data); else brcmf_err("unhandled event %d ignored
", code); } else { brcmf_err("no interface object
"); } return err; }
코드가 분명히 여기 퍼블릭 웨어의 이벤트는 fweh->evthandler가 처리했어.그럼 fweh->evt 를 알아야 돼요.handler는 어떻게 초기화되었습니까?
이전 절에서 Brcmf 를 분석한 적이 있습니다cfg80211_attach (), 이 함수는wl 를 호출합니다init_priv():static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg) { s32 err = 0; cfg->scan_request = NULL; cfg->pwr_save = true; cfg->roam_on = true; /* roam on & off switch. we enable roam per default */ cfg->active_scan = true; /* we do active scan for specific scan per default */ cfg->dongle_up = false; /* dongle is not up yet */ err = brcmf_init_priv_mem(cfg); if (err) return err; brcmf_register_event_handlers(cfg); mutex_init(&cfg->usr_sync); brcmf_init_escan(cfg); // scan brcmf_init_conf(cfg->conf); init_completion(&cfg->vif_disabled); return err; }
계속 Brcmf 따라가기init_ecan():static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg) { // handler! brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT, brcmf_cfg80211_escan_handler); cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; /* Init scan_timeout timer */ init_timer(&cfg->escan_timeout); cfg->escan_timeout.data = (unsigned long) cfg; cfg->escan_timeout.function = brcmf_escan_timeout; INIT_WORK(&cfg->escan_timeout_work, brcmf_cfg80211_escan_timeout_worker); }
분명히 퍼블릭 웨어에 올라온 BRCMFE_ESCAN_RESULT 이벤트, fweh->evthandler는 Brcmfcfg80211_escan_handler().
7.brcmf 호출cfg80211_escan_handler()static s32 brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, const struct brcmf_event_msg *e, void *data) { ... if (status == BRCMF_E_STATUS_PARTIAL) { ... } else { cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; if (brcmf_p2p_scan_finding_common_channel(cfg, NULL)) goto exit; if (cfg->scan_request) { cfg->bss_list = (struct brcmf_scan_results *) cfg->escan_info.escan_buf; brcmf_inform_bss(cfg); aborted = status != BRCMF_E_STATUS_SUCCESS; // brcmf_notify_escan_complete(cfg, ndev, aborted, false); } else brcmf_dbg(SCAN, "Ignored scan complete result 0x%x
", status); } exit: return err;
8.brcmf 호출notify_escan_complete()s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, bool aborted, bool fw_abort) { ... /* * e-scan can be initiated by scheduled scan * which takes precedence. */ if (cfg->sched_escan) { brcmf_dbg(SCAN, "scheduled scan completed
"); cfg->sched_escan = false; if (!aborted) cfg80211_sched_scan_results(cfg_to_wiphy(cfg)); brcmf_set_mpc(ndev, 1); } else if (scan_request) { brcmf_dbg(SCAN, "ESCAN Completed scan: %s
", aborted ? "Aborted" : "Done"); // cfg80211_scan_done(scan_request, aborted); brcmf_set_mpc(ndev, 1); } if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) brcmf_dbg(SCAN, "Scan complete, probably P2P scan
"); return err; }
9.cfg80211 호출scan_done()void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) { trace_cfg80211_scan_done(request, aborted); WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); request->aborted = aborted; queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk); // work queue }
여기 work queue는 Wiphy에서...new () 초기화:INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
10. 그럼 다음은 호출cfg80211_scan_done()void __cfg80211_scan_done(struct work_struct *wk) { struct cfg80211_registered_device *rdev; rdev = container_of(wk, struct cfg80211_registered_device, scan_done_wk); cfg80211_lock_rdev(rdev); ___cfg80211_scan_done(rdev, false); cfg80211_unlock_rdev(rdev); }
11. 호출cfg80211_scan_done()void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) { ... /* * This must be before sending the other events! * Otherwise, wpa_supplicant gets completely confused with * wext events. */ if (wdev->netdev) cfg80211_sme_scan_done(wdev->netdev); if (request->aborted) { nl80211_send_scan_aborted(rdev, wdev); } else { if (request->flags & NL80211_SCAN_FLAG_FLUSH) { /* flush entries from previous scans */ spin_lock_bh(&rdev->bss_lock); __cfg80211_bss_expire(rdev, request->scan_start); spin_unlock_bh(&rdev->bss_lock); } // nl80211 user space nl80211_send_scan_done(rdev, wdev); } ... }
12. nl80211 호출send_scan_done()void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) { struct sk_buff *msg; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) return; // if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0, NL80211_CMD_NEW_SCAN_RESULTS) < 0) { nlmsg_free(msg); return; } genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); }
13. 다음 wpasupplicant에서 NL80211 수신CMD_NEW_SCAN_RESULTSstatic int process_event(struct nl_msg *msg, void *arg) { struct wpa_driver_nl80211_data *drv = arg; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; union wpa_event_data data; ... switch (gnlh->cmd) { ... case NL80211_CMD_NEW_SCAN_RESULTS: wpa_printf(MSG_DEBUG, "nl80211: New scan results available"); drv->scan_complete_events = 1; eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); send_scan_event(drv, 0, tb); break; ... default: wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", gnlh->cmd); break; } return NL_SKIP; }
14. send 호출scan_event()static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, struct nlattr *tb[]) { union wpa_event_data event; struct nlattr *nl; int rem; struct scan_info *info; #define MAX_REPORT_FREQS 50 int freqs[MAX_REPORT_FREQS]; int num_freqs = 0; os_memset(&event, 0, sizeof(event)); info = &event.scan_info; info->aborted = aborted; ... // EVENT_SCAN_RESULTS wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); }
15. wpa 입장supplicant_event()void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { struct wpa_supplicant *wpa_s = ctx; u16 reason_code = 0; switch (event) { ... #ifndef CONFIG_NO_SCAN_PROCESSING case EVENT_SCAN_RESULTS: wpa_supplicant_event_scan_results(wpa_s, data); break; #endif /* CONFIG_NO_SCAN_PROCESSING */ ... default: wpa_printf(MSG_INFO, "Unknown event %d", event); break; } }
16. wpa 호출supplicant_event_scan_results()static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { struct wpa_bss *selected; struct wpa_ssid *ssid = NULL; struct wpa_scan_results *scan_res; int ap = 0; #ifdef CONFIG_AP if (wpa_s->ap_iface) ap = 1; #endif /* CONFIG_AP */ wpa_supplicant_notify_scanning(wpa_s, 0); // driver scan result scan_res = wpa_supplicant_get_scan_results(wpa_s, data ? &data->scan_info : NULL, 1); if (scan_res == NULL) { if (wpa_s->conf->ap_scan == 2 || ap) return; wpa_printf(MSG_DEBUG, "Failed to get scan results - try " "scanning again"); wpa_supplicant_req_new_scan(wpa_s, 1, 0); return; } if (wpa_s->scan_res_handler) { wpa_s->scan_res_handler(wpa_s, scan_res); wpa_s->scan_res_handler = NULL; wpa_scan_results_free(scan_res); return; } if (ap) { wpa_printf(MSG_DEBUG, "Ignore scan results in AP mode"); wpa_scan_results_free(scan_res); return; } wpa_printf(MSG_DEBUG, "New scan results available"); // "CTRL-EVENT-SCAN-RESULTS " // Android framework WifiMonitor wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); wpas_notify_scan_results(wpa_s); wpas_notify_scan_done(wpa_s, 1); ... }
여기 wpa가 필요해supplicant_get_scan_results (), 아주 중요합니다.struct wpa_scan_results * wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, struct scan_info *info, int new_scan) { struct wpa_scan_results *scan_res; size_t i; if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) scan_res = ieee80211_sta_get_scan_results(wpa_s); else // wpa_driver_nl80211_get_scan_results(), // driver NL80211_CMD_GET_SCAN , , // driver scan result dump 。 scan_res = wpa_drv_get_scan_results2(wpa_s); if (scan_res == NULL) { wpa_printf(MSG_DEBUG, "Failed to get scan results"); return NULL; } // qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *), wpa_scan_result_compar); wpa_bss_update_start(wpa_s); for (i = 0; i < scan_res->num; i++) // update wpa_s , wpa_bss_update_scan_res(wpa_s, scan_res->res[i]); wpa_bss_update_end(wpa_s, info, new_scan); return scan_res; }
17. 앞에서 안드로이드 프레임워크의 와이파이 모니터를 언급했다.이제 Wifi Monitor에 들어가서 이벤트'Ctrl-EVENT-SCAN-RESULTS'를 어떻게 처리하는지 분석해 보도록 하겠습니다.class MonitorThread extends Thread { public MonitorThread() { super("WifiMonitor"); } public void run() { if (connectToSupplicant()) { // Send a message indicating that it is now possible to send commands // to the supplicant mStateMachine.sendMessage(SUP_CONNECTION_EVENT); } else { mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT); return; } //noinspection InfiniteLoopStatement for (;;) { String eventStr = WifiNative.waitForEvent(); ... /* * Map event name into event enum */ int event; if (eventName.equals(CONNECTED_STR)) event = CONNECTED; ... // scan result else if (eventName.equals(SCAN_RESULTS_STR)) event = SCAN_RESULTS; ... else event = UNKNOWN; ... if (event == STATE_CHANGE) { handleSupplicantStateChange(eventData); } ... else { handleEvent(event, eventData); } mRecvErrors = 0; } } ... /** * Handle all supplicant events except STATE-CHANGE * @param event the event type * @param remainder the rest of the string following the * event name and " — " */ void handleEvent(int event, String remainder) { switch (event) { ... // scan result case SCAN_RESULTS: mStateMachine.sendMessage(SCAN_RESULTS_EVENT); break; case UNKNOWN: break; } } ... }
18. WifiStateMachine에서 SCAN 을 더 처리할 것이 분명하다RESULT_EVENTclass SupplicantStartedState extends State { ... @Override public boolean processMessage(Message message) { if (DBG) log(getName() + message.toString() + "
"); WifiConfiguration config; boolean eventLoggingEnabled = true; switch(message.what) { ... case WifiMonitor.SCAN_RESULTS_EVENT: eventLoggingEnabled = false; setScanResults(WifiNative.scanResultsCommand()); sendScanResultsAvailableBroadcast(); mScanResultIsPending = false; break; ... default: return NOT_HANDLED; } if (eventLoggingEnabled) { EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); } return HANDLED; } ... }
19. 여기서는 WifiNative를 먼저 호출합니다.scanResultsCommand () 는 검색 결과를 가져와 setScanResult () 에 전달합니다.JNI 함수로 들어가 보도록 하겠습니다.static jstring android_net_wifi_scanResultsCommand(JNIEnv* env, jobject) { return doStringCommand(env, "SCAN_RESULTS"); }
20. "SCAN RESULT"라는 명령을 wpa 에게 또 보냈다supplicant
wpa_supplicant_ctrl_iface_process () 호출 wpasupplicant_ctrl_iface_scan_명령을 처리하려면 results를 사용하십시오.static int wpa_supplicant_ctrl_iface_scan_results( struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { char *pos, *end; struct wpa_bss *bss; int ret; pos = buf; end = buf + buflen; ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / " "flags / ssid
"); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) { // ,wpa_supplicant_get_scan_results() update wpa_s // , wpa_s->bss_id bss ret = wpa_supplicant_ctrl_iface_scan_result(bss, pos, end - pos); if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; } return pos - buf; }
21. wpa 호출supplicant_ctrl_iface_scan_result () 문자열 포맷하기/* Format one result on one text line into a buffer. */ static int wpa_supplicant_ctrl_iface_scan_result( const struct wpa_bss *bss, char *buf, size_t buflen) { ... }
결국 안드로이드 프레임워크는 포맷된 스캔 결과를 얻었다.
22. setScanResults 호출()/** * scanResults input format * 00:bb:cc:dd:cc:ee 2427 166 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net1 * 00:bb:cc:dd:cc:ff 2412 165 [WPA-EAP-TKIP][WPA2-EAP-CCMP] Net2 */ private void setScanResults(String scanResults) { if (scanResults == null) { return; } List<ScanResult> scanList = new ArrayList<ScanResult>(); int lineCount = 0; int scanResultsLen = scanResults.length(); // Parse the result string, keeping in mind that the last line does // not end with a newline. for (int lineBeg = 0, lineEnd = 0; lineEnd <= scanResultsLen; ++lineEnd) { if (lineEnd == scanResultsLen || scanResults.charAt(lineEnd) == '
') { ++lineCount; if (lineCount == 1) { lineBeg = lineEnd + 1; continue; } if (lineEnd > lineBeg) { String line = scanResults.substring(lineBeg, lineEnd); ScanResult scanResult = parseScanResult(line); if (scanResult != null) { scanList.add(scanResult); } else { //TODO: hidden network handling } } lineBeg = lineEnd + 1; } } mScanResults = scanList; }
이 함수의 주석은 wpa 에서supplicant에서 얻은 스캔 결과의 형식입니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
IOS가 현재 네트워크 상태를 판단하는 세 가지 방법을 자세히 설명합니다.프로젝트에서 좋은 사용자 체험을 위해 일부 장면은 반드시 네트워크 상태를 라인으로 판단한 후에야 무엇을 해야 할지 결정할 수 있다.예를 들어 영상 재생은 와이파이인지 4G인지 선이 판단해야 한다. 와이파이가 직접 재...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.