#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wl_internal.h" #include "wl_ioctl.h" #include "wl_debug.h" #ifndef MAX #define MAX(a, b) (((a) >= (b)) ? (a) : (b)) #endif #define DHD_IOCTL_MAGIC (0x00444944) #define DHD_GET_VAR 2 #define DHD_SET_VAR 3 #define WL_BSSCFG_PREX_STR "bsscfg:" #define WL_EVENT_LIST_SIZE (5) /* Timeout in second for event from driver */ #define WL_EVENT_TIMEOUT 10 #define WL_EVENT_BUF_SIZE 2048 #define WL_REG_EVENT_ONCE_MAX (16) #define THREAD_CLOSE_EVENT "CLOSE" /* Broadcom OUI (Organizationally Unique Identifier): Used in the proprietary(221) IE (Information Element) in all Broadcom devices */ #define BRCM_OUI "\x00\x10\x18" /* Length in bytes of 802.11 OUI*/ #define DOT11_OUI_LEN 3 #define JOIN_ASSOCIATED (uint32_t)(1 << 0) #define JOIN_AUTHENTICATED (uint32_t)(1 << 1) #define JOIN_LINK_READY (uint32_t)(1 << 2) #define JOIN_SECURITY_COMPLETE (uint32_t)(1 << 3) #define JOIN_SSID_SET (uint32_t)(1 << 4) #define JOIN_NO_NETWORKS (uint32_t)(1 << 5) #define JOIN_EAPOL_KEY_M1_TIMEOUT (uint32_t)(1 << 6) #define JOIN_EAPOL_KEY_M3_TIMEOUT (uint32_t)(1 << 7) #define JOIN_EAPOL_KEY_G1_TIMEOUT (uint32_t)(1 << 8) #define JOIN_EAPOL_KEY_FAILURE (uint32_t)(1 << 9) #define JOIN_COMPLETED (JOIN_AUTHENTICATED | JOIN_LINK_READY | JOIN_SSID_SET | JOIN_SECURITY_COMPLETE) /* Linux network driver ioctl encoding */ typedef struct { uint32_t cmd; /**< common ioctl definition */ void *buf; /**< pointer to user buffer */ uint32_t len; /**< length of user buffer */ uint8_t set; /**< 1=set IOCTL; 0=query IOCTL */ uint32_t used; /**< bytes read or written (optional) */ uint32_t needed; /**< bytes needed (optional) */ uint32_t driver; /**< to identify target driver */ } wl_dhd_ioctl_t; typedef struct { uint8_t event_mask[WL_EVENTING_MASK_LEN]; #ifdef WL_CONFIG_CHECK_EVENT uint8_t reg_event_mask[WL_EVENTING_MASK_LEN]; uint32_t is_event_set; #endif /* WL_CONFIG_CHECK_EVENT */ wl_event_cb_t callback; void* user_data; } wl_event_item_t; typedef enum { WL_CONNECT_STATUS_DISCONNECTED = 0, WL_CONNECT_STATUS_DISCONNECTING, WL_CONNECT_STATUS_CONNECTED, WL_CONNECT_STATUS_CONNECTING, } wl_connect_status_t; static int wl_ioctl_init(const char* ifname); static int wl_ioctl_deinit(void); static int wl_event_init(void); static int wl_event_deinit(void); static void* wl_event_thread_func(void *args); static void wl_connect_event_handler(wl_event_msg_t* msg, void* user_data); pthread_mutex_t wl_dbg_mutex; static int wl_ioctl_sockfd = -1; static struct ifreq wl_ifr; static bool wl_is_init; static sem_t wl_connect_sem; static pthread_mutex_t wl_mutex; static pthread_t wl_event_thread; static int wl_event_pipe[2]; static wl_event_item_t wl_event_list[WL_EVENT_LIST_SIZE]; static uint32_t wl_event_num; static const uint32_t wl_connect_status_event[] = { WLC_E_SET_SSID, WLC_E_JOIN, WLC_E_LINK, WLC_E_AUTH, WLC_E_DEAUTH_IND, WLC_E_DISASSOC_IND, WLC_E_PSK_SUP }; static uint32_t wl_connect_status; static uint32_t wl_join_status; static wl_connect_status_cb_t wl_connect_status_callback; static void *wl_connect_status_userdata; static int wl_mkiovar( const char* iovar_name, void* param, int32_t paramlen, int8_t* iovar_buf, int32_t buflen) { int32_t len; len = strlen(iovar_name) + 1; if ((len + paramlen) > buflen) { return WL_E_BUFTOOSHORT; } strncpy((char*)iovar_buf, iovar_name, buflen); if ((param != NULL) && (paramlen > 0)) { /* append data onto the end of the name string */ memcpy(&iovar_buf[len], param, paramlen); len += paramlen; } return len; } static int wl_mkiovar_bsscfg( const char* iovar_name, void* param, int32_t paramlen, int8_t* iovar_buf, int32_t buflen, int32_t bssidx) { const char *prefix = WL_BSSCFG_PREX_STR; int8_t *p; uint32_t prefixlen; uint32_t namelen; uint32_t iolen; if ((iovar_buf == NULL) || (buflen == 0)) { return WL_E_BADARG; } memset(iovar_buf, 0, buflen); if (bssidx == 0) { return wl_mkiovar(iovar_name, param, paramlen, iovar_buf, buflen); } prefixlen = strlen(prefix); namelen = strlen(iovar_name) + 1; iolen = prefixlen + namelen + sizeof(uint32_t) + paramlen; if (buflen < 0 || iolen > (uint32_t)buflen) { return WL_E_BADARG; } p = iovar_buf; /* copy prefix, no null */ memcpy(p, prefix, prefixlen); p += prefixlen; /* copy iovar name including null */ memcpy(p, iovar_name, namelen); p += namelen; /* bss config index as first param */ memcpy(p, &bssidx, sizeof(uint32_t)); p += sizeof(uint32_t); /* parameter buffer follows */ if (paramlen) { memcpy(p, param, paramlen); } return iolen; } int wl_ioctl(uint32_t cmd, void* buf, uint32_t len, bool set) { int ret = 0; wl_ioctl_t ioc; #if WL_LOG_LEVEL >= WL_LOG_DEBUG if (cmd == WLC_SET_VAR) { WL_DBG("SET IOVAR %s\n", (char*)buf); } else if (cmd == WLC_GET_VAR) { WL_DBG("GET IOVAR %s\n", (char*)buf); } else { WL_DBG("cmd = %d\n", cmd); } #endif /* WL_LOG_LEVEL >= WL_LOG_DEBUG */ WL_DUMP(buf, len); if (wl_ioctl_sockfd < 0) { WL_ERR("wl is not init\n"); return WL_E_IOCTL_ERROR; } ioc.cmd = cmd; ioc.buf = buf; ioc.len = len; ioc.set = set; wl_ifr.ifr_data = (caddr_t)&ioc; ret = ioctl(wl_ioctl_sockfd, SIOCDEVPRIVATE, &wl_ifr); if (ret < 0) { #if WL_LOG_LEVEL >= WL_LOG_DEBUG if ((cmd != WLC_GET_VAR) || (strncmp(buf, "bcmerrorstr", strlen("bcmerrorstr") != 0))) { char buf[128]; memset(buf, 0, sizeof(buf)); wl_iovar_get("bcmerrorstr", buf, sizeof(buf)); WL_ERR("ioctl err = %d, bcmerror = %s\n", ret, buf); } #else /* WL_LOG_LEVEL >= WL_LOG_DEBUG */ WL_ERR("ioctl err = %d\n", ret); #endif /* WL_LOG_LEVEL >= WL_LOG_DEBUG */ return WL_E_IOCTL_ERROR; } return WL_E_OK; } int wl_iovar_getbuf( const char* iovar_name, void* param, int32_t paramlen, int8_t* iovar_buf, int32_t buflen) { int ret; ret = wl_mkiovar( iovar_name, param, paramlen, iovar_buf, buflen); if (ret > 0) { ret = wl_ioctl(WLC_GET_VAR, iovar_buf, buflen, false); } return ret; } int wl_iovar_setbuf( const char* iovar_name, void* param, int32_t paramlen, int8_t* iovar_buf, int32_t buflen) { int ret; ret = wl_mkiovar( iovar_name, param, paramlen, iovar_buf, buflen); if (ret > 0) { ret = wl_ioctl(WLC_SET_VAR, iovar_buf, ret, true); } return ret; } int wl_iovar_get(const char* iovar_name, void* outbuf, int32_t len) { int ret; int8_t smbuf[WLC_IOCTL_SMLEN]; /* use the return buffer if it is bigger than what we have on the stack */ if (len > WLC_IOCTL_SMLEN) { ret = wl_iovar_getbuf(iovar_name, NULL, 0, outbuf, len); } else { memset(smbuf, 0, sizeof(smbuf)); ret = wl_iovar_getbuf( iovar_name, NULL, 0, smbuf, sizeof(smbuf)); if (ret == 0) { memcpy(outbuf, smbuf, len); } } return ret; } int wl_iovar_set(const char* iovar_name, void* param, int32_t paramlen) { int8_t smbuf[WLC_IOCTL_SMLEN * 2]; memset(smbuf, 0, sizeof(smbuf)); return wl_iovar_setbuf( iovar_name, param, paramlen, smbuf, sizeof(smbuf)); } int wl_iovar_getint(const char* iovar_name, int32_t* pval) { return wl_iovar_get(iovar_name, pval, sizeof(int32_t)); } int wl_iovar_setint(const char* iovar_name, int32_t val) { return wl_iovar_set(iovar_name, &val, sizeof(int32_t)); } int wl_iovar_bsscfg_getbuf( const char* iovar_name, void* param, int32_t paramlen, void* iovar_buf, int32_t buflen, int32_t bsscfg_idx) { int ret = 0; wl_mkiovar_bsscfg(iovar_name, param, paramlen, iovar_buf, buflen, bsscfg_idx); ret = wl_ioctl(WLC_GET_VAR, iovar_buf, buflen, false); return ret; } int wl_iovar_bsscfg_setbuf( const char*iovar_name, void* param, int32_t paramlen, void* iovar_buf, int32_t buflen, int32_t bsscfg_idx) { int ret = 0; int32_t iovar_len; iovar_len = wl_mkiovar_bsscfg(iovar_name, param, paramlen, iovar_buf, buflen, bsscfg_idx); if (iovar_len > 0) ret = wl_ioctl(WLC_SET_VAR, iovar_buf, iovar_len, true); else { ret = WL_E_BUFTOOSHORT; } return ret; } int wl_iovar_bsscfg_get( const char* iovar_name, void* outbuf, int32_t len, int32_t bsscfg_idx) { int ret; int8_t smbuf[WLC_IOCTL_SMLEN]; /* use the return buffer if it is bigger than what we have on the stack */ if (len > WLC_IOCTL_SMLEN) { ret = wl_iovar_bsscfg_getbuf(iovar_name, NULL, 0, outbuf, len, bsscfg_idx); } else { memset(smbuf, 0, sizeof(smbuf)); ret = wl_iovar_bsscfg_getbuf( iovar_name, NULL, 0, smbuf, sizeof(smbuf), bsscfg_idx); if (ret == 0) { memcpy(outbuf, smbuf, len); } } return ret; } int wl_iovar_bsscfg_set( const char* iovar_name, void* param, int32_t paramlen, int32_t bsscfg_idx) { int8_t smbuf[WLC_IOCTL_SMLEN * 2]; memset(smbuf, 0, sizeof(smbuf)); return wl_iovar_bsscfg_setbuf( iovar_name, param, paramlen, smbuf, sizeof(smbuf), bsscfg_idx); } int wl_iovar_bsscfg_getint( const char*iovar_name, int32_t* pval, int32_t bsscfg_idx) { return wl_iovar_bsscfg_get(iovar_name, pval, sizeof(int32_t), bsscfg_idx); } int wl_iovar_bsscfg_setint( const char* iovar_name, int32_t val, int32_t bsscfg_idx) { return wl_iovar_bsscfg_set(iovar_name, &val, sizeof(int32_t), bsscfg_idx); } int wl_dhd_iovar_setint(const char* iovar_name, int32_t val) { int ret = 0; wl_dhd_ioctl_t ioc; int8_t iovar_buf[WLC_IOCTL_SMLEN]; int32_t len; if (wl_ioctl_sockfd < 0) { WL_ERR("wl is not init\n"); return WL_E_IOCTL_ERROR; } WL_DBG("IOVAR %s\n", (char*)iovar_name); memset(iovar_buf, 0, WLC_IOCTL_SMLEN); len = strlen(iovar_name) + 1; if ((len + sizeof(int32_t)) > WLC_IOCTL_SMLEN) { return WL_E_BUFTOOSHORT; } strncpy((char*)iovar_buf, iovar_name, WLC_IOCTL_SMLEN); memcpy(&iovar_buf[len], &val, sizeof(int32_t)); len += sizeof(int32_t); ioc.cmd = DHD_SET_VAR; ioc.buf = iovar_buf; ioc.len = len; ioc.set = true; ioc.driver = DHD_IOCTL_MAGIC; wl_ifr.ifr_data = (caddr_t)&ioc; ret = ioctl(wl_ioctl_sockfd, SIOCDEVPRIVATE, &wl_ifr); if (ret < 0) { return WL_E_IOCTL_ERROR; } return WL_E_OK; } int wl_init(const char* ifname) { int ret; wl_ether_addr_t bssid; pthread_mutex_init(&wl_dbg_mutex, NULL); WL_DBG("ifname = %s\n", ifname); if (wl_is_init) { WL_INFO("already init\n"); return WL_E_OK; } ret = wl_ioctl_init(ifname); if (ret != WL_E_OK) { return ret; } ret = wl_get_bssid(&bssid); if (ret == 0) { #ifdef WL_FIX_CONNECT_STATUS_OPTIONS uint32_t wakeind = 0; ret = wl_wowl_get_wakeind(&wakeind); if ((ret == WL_E_OK) && (wakeind & WL_WOWL_DIS)) { wl_connect_status = WL_CONNECT_STATUS_DISCONNECTED; } else { wl_connect_status = WL_CONNECT_STATUS_CONNECTED; wl_join_status = JOIN_COMPLETED; } #else /* WL_FIX_CONNECT_STATUS_OPTIONS */ wl_connect_status = WL_CONNECT_STATUS_CONNECTED; wl_join_status = JOIN_COMPLETED; #endif /* WL_FIX_CONNECT_STATUS_OPTIONS */ } else { wl_connect_status = WL_CONNECT_STATUS_DISCONNECTED; } ret = wl_event_init(); if (ret != WL_E_OK) { wl_ioctl_deinit(); } pthread_mutex_init(&wl_mutex, NULL); sem_init(&wl_connect_sem, 0, 0); wl_is_init = true; return ret; } int wl_deinit(void) { WL_DBG("enter\n"); wl_event_deinit(); wl_ioctl_deinit(); pthread_mutex_destroy(&wl_mutex); sem_destroy(&wl_connect_sem); pthread_mutex_destroy(&wl_dbg_mutex); wl_is_init = false; return 0; } bool wl_is_associated(void) { return (wl_connect_status == WL_CONNECT_STATUS_CONNECTED); } int wl_error(void) { int ret; int32_t error; ret = wl_iovar_getint("bcmerror", &error); if (ret == 0) { return error; } else { return WL_E_OK; } } static int wl_ioctl_init(const char* ifname) { int ret; struct ifreq ifr; WL_DBG("ifname = %s\n", ifname); ret = socket(AF_INET, SOCK_DGRAM, 0); if (ret < 0) { WL_ERR("create socket error = %d\n", ret); return WL_E_IOCTL_ERROR; } wl_ioctl_sockfd = ret; memset(&wl_ifr, 0, sizeof(struct ifreq)); strncpy(wl_ifr.ifr_name, ifname, IFNAMSIZ); memcpy(&ifr, &wl_ifr, sizeof(struct ifreq)); ret = ioctl(wl_ioctl_sockfd, SIOCGIFINDEX, &ifr); if (ret >= 0) { if (ifr.ifr_ifindex <= 0) { WL_ERR("invalid device %s\n", ifname); ret = WL_E_NODEVICE; } } if (ret < 0) { WL_ERR("ioctl error = %d\n", ret); close(wl_ioctl_sockfd); wl_ioctl_sockfd = -1; return WL_E_IOCTL_ERROR; } return WL_E_OK; } static int wl_ioctl_deinit(void) { WL_DBG("enter\n"); if (wl_ioctl_sockfd >= 0) { close(wl_ioctl_sockfd); wl_ioctl_sockfd = -1; } return WL_E_OK; } static int wl_event_init(void) { int ret; WL_DBG("enter\n"); ret = pthread_create( &wl_event_thread, NULL, wl_event_thread_func, NULL); if (ret < 0) { WL_ERR("create thread failed.\n"); return WL_E_ERROR; } ret = wl_event_register( wl_connect_status_event, sizeof(wl_connect_status_event) / sizeof(uint32_t), wl_connect_event_handler, NULL); return ret; } static int wl_event_deinit(void) { WL_DBG("enter\n"); wl_event_deregister(wl_connect_event_handler, NULL); WL_DBG("send close event to pipe\n"); write(wl_event_pipe[1], THREAD_CLOSE_EVENT, sizeof(THREAD_CLOSE_EVENT)); WL_DBG("waiting event thread close\n"); pthread_join(wl_event_thread, NULL); WL_DBG("exit\n"); return WL_E_OK; } static void *wl_event_thread_func(void *args) { int ret; int sockfd; // struct sockaddr_ll sll; char *buf = NULL; fd_set rfds; int fdsize; WL_DBG("enter\n"); sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETHER_TYPE_BRCM)); if (sockfd < 0) { WL_ERR("create socket err = %d\n", sockfd); return NULL; } FD_ZERO(&rfds); ret = pipe(wl_event_pipe); if (ret < 0) { WL_ERR("create pipe err = %d\n", ret); goto exit; } fdsize = MAX(sockfd, wl_event_pipe[0]); #if (0) /* bind the socket first before starting escan so we won't miss any event */ memset(&sll, 0, sizeof(sll)); sll.sll_family = AF_PACKET; sll.sll_protocol = htons(ETHER_TYPE_BRCM); sll.sll_ifindex = wl_ifr.ifr_ifindex; ret = bind(fd, (struct sockaddr *)&sll, sizeof(sll)); if (ret < 0) { WL_ERR("bind socket error = %d\n", ret); goto exit; } #endif buf = (char*)malloc(WL_EVENT_BUF_SIZE); if (buf == NULL) { WL_ERR("alloc event buf failed.\n"); goto pipeexit; } while (1) { int size; bcm_event_t *msg; WL_DBG("waiting event\n"); FD_SET(sockfd, &rfds); FD_SET(wl_event_pipe[0], &rfds); ret = select(fdsize + 1, &rfds, NULL, NULL, NULL); if (ret < 0) { WL_ERR("select err = %d\n", ret); break; } if (FD_ISSET(wl_event_pipe[0], &rfds)) { WL_ERR("Receive thread close event from pipe\n"); break; } if (!FD_ISSET(sockfd, &rfds)) { WL_ERR("unknown fd\n"); break; } size = recv(sockfd, buf, WL_EVENT_BUF_SIZE, 0); WL_DBG("recv buf size = %d\n", size); if (size >= sizeof(bcm_event_t)) { int i; msg = (bcm_event_t*)buf; if (memcmp(BRCM_OUI, &msg->bcm_hdr.oui[0], DOT11_OUI_LEN) != 0) { WL_INFO("Event OUI mismatch\n"); continue; } msg->event.flags = NTOH16(msg->event.flags); msg->event.event_type = NTOH32(msg->event.event_type); msg->event.status = NTOH32(msg->event.status); msg->event.reason = NTOH32(msg->event.reason); msg->event.auth_type = NTOH32(msg->event.auth_type); msg->event.datalen = NTOH32(msg->event.datalen); WL_DBG("recv event = %d\n", msg->event.event_type); if (msg->event.event_type > WLC_E_LAST) { WL_INFO("Invalid event\n"); continue; } for (i = 0; i < wl_event_num; i++) { wl_event_item_t *item = &wl_event_list[i]; if (ISSET(item->event_mask, msg->event.event_type)) { item->callback(&msg->event, item->user_data); } } } } pipeexit: close(wl_event_pipe[0]); close(wl_event_pipe[1]); exit: if (buf != NULL) { free(buf); } close(sockfd); pthread_exit(NULL); return NULL; } static int wl_connect_status_update(bool is_connected) { WL_DBG("is_connected = %d\n", is_connected); if (wl_connect_status_callback != NULL) { wl_connect_status_callback(is_connected, wl_connect_status_userdata); } } static void wl_connecting_ap_handler(wl_event_msg_t* msg, void* user_data) { bool is_join_completed = false; WL_DBG("Before event handler, join state = 0x%08x\n", wl_join_status); switch(msg->event_type) { case WLC_E_PSK_SUP: WL_DBG("WLC_E_PSK_SUP, status = %d\n", msg->status); /* Ignore WLC_E_PSK_SUP event if link is not up */ if (wl_join_status & JOIN_LINK_READY) { if (msg->status == WLC_SUP_KEYED) { /* Successful WPA key exchange */ wl_join_status |= JOIN_SECURITY_COMPLETE; } else { /* join has completed with an error */ is_join_completed = true; if ((msg->status == WLC_SUP_KEYXCHANGE_WAIT_M1) && (msg->reason == WLC_E_SUP_WPA_PSK_TMO)) { /* A timeout waiting for M1 may occur at the edge of the cell or if the AP is particularly slow. */ WL_DBG("Supplicant M1 timeout event\n"); wl_join_status |= JOIN_EAPOL_KEY_M1_TIMEOUT; } else if ((msg->status == WLC_SUP_KEYXCHANGE_WAIT_M3) && ( msg->reason == WLC_E_SUP_WPA_PSK_TMO)) { /* A timeout waiting for M3 is an indicator that the passphrase may be incorrect. */ WL_DBG("Supplicant M3 timeout event\n"); wl_join_status |= JOIN_EAPOL_KEY_M3_TIMEOUT; } else if ((msg->status == WLC_SUP_KEYXCHANGE_WAIT_G1) && (msg->reason == WLC_E_SUP_WPA_PSK_TMO)) { /* A timeout waiting for G1 (group key) may occur at the edge of the cell. */ WL_DBG("Supplicant G1 timeout event\n"); wl_join_status |= JOIN_EAPOL_KEY_G1_TIMEOUT; } else { WL_DBG("Unsuccessful supplicant event; status=0x%x\n", msg->status); /* Unknown failure during EAPOL key handshake */ wl_join_status |= JOIN_EAPOL_KEY_FAILURE; } } } break; case WLC_E_JOIN: case WLC_E_SET_SSID: WL_DBG("WLC_E_SET_SSID, status = %d\n", msg->status); if (msg->status == WLC_E_STATUS_SUCCESS) { /* SSID has been successfully set. */ wl_join_status |= JOIN_SSID_SET; } else if ((msg->status & 0xFF) == WLC_E_STATUS_NO_NETWORKS) { wl_join_status |= JOIN_NO_NETWORKS; is_join_completed = true; } else { is_join_completed = true; } break; case WLC_E_LINK: WL_DBG("WLC_E_LINK, flags = 0x%08x\n", msg->flags); if (msg->flags & WLC_EVENT_MSG_LINK) { wl_join_status |= JOIN_LINK_READY; } else { wl_join_status &= ~JOIN_LINK_READY; } break; case WLC_E_DEAUTH_IND: case WLC_E_DISASSOC_IND: WL_DBG("Disconnect\n"); wl_join_status &= ~(JOIN_AUTHENTICATED | JOIN_LINK_READY); break; case WLC_E_AUTH: WL_DBG("WLC_E_AUTH, status = %d\n", msg->status); if (msg->status == WLC_E_STATUS_SUCCESS) { wl_join_status |= JOIN_AUTHENTICATED; } else if (msg->status == WLC_E_STATUS_UNSOLICITED) { WL_DBG("Ignore UNSOLICITED pkt event\n"); } else { /* We cannot authenticate. Perhaps we're blocked or at the edge of a cell. */ is_join_completed = true; } break; default: break; } WL_DBG("After event handler, join state = 0x%08x, connect status = %d\n", wl_join_status, wl_connect_status); if (wl_join_status == JOIN_COMPLETED) { is_join_completed = true; } WL_DBG("is_join_completed = %d\n", is_join_completed); if (is_join_completed) { if (wl_join_status == JOIN_COMPLETED) { wl_connect_status = WL_CONNECT_STATUS_CONNECTED; } else { wl_connect_status = WL_CONNECT_STATUS_DISCONNECTED; } wl_join_wake(); if (wl_connect_status == WL_CONNECT_STATUS_CONNECTED) { wl_connect_status_update(true); } } } static void wl_connect_status_change_handler(wl_event_msg_t* msg, void* user_data) { bool is_join_completed = false; uint32_t old_status = wl_connect_status; WL_DBG("connect status = %d\n", wl_connect_status); switch(msg->event_type) { case WLC_E_LINK: WL_DBG("WLC_E_LINK, flags = 0x%08x\n", msg->flags); if (msg->flags & WLC_EVENT_MSG_LINK) { wl_connect_status = WL_CONNECT_STATUS_CONNECTED; } else { wl_connect_status = WL_CONNECT_STATUS_DISCONNECTED; } break; case WLC_E_DEAUTH_IND: case WLC_E_DISASSOC_IND: WL_DBG("Disconnect\n"); wl_connect_status = WL_CONNECT_STATUS_DISCONNECTED; break; default: break; } if (old_status != wl_connect_status) { bool is_connected = (wl_connect_status == WL_CONNECT_STATUS_CONNECTED); wl_connect_status_update(is_connected); } } static void wl_connect_event_handler(wl_event_msg_t* msg, void* user_data) { if (wl_connect_status == WL_CONNECT_STATUS_CONNECTING) { wl_connecting_ap_handler(msg, user_data); } else { wl_connect_status_change_handler(msg, user_data); } } int wl_join_init(uint32_t security) { WL_DBG("security = 0x%08x\n", security); if ((security == WL_SECURITY_OPEN) || (security == WL_SECURITY_WPS_OPEN) || (security == WL_SECURITY_WPS_SECURE)) { wl_join_status = JOIN_SECURITY_COMPLETE; } else { wl_join_status = 0; } wl_connect_status = WL_CONNECT_STATUS_CONNECTING; return 0; } int wl_join_wait(uint32_t timeout) { WL_DBG("timeout = %d\n", timeout); return wl_wait(&wl_connect_sem, timeout); } int wl_join_wake(void) { WL_DBG("enter\n"); sem_post(&wl_connect_sem); return 0; } int wl_join_error(void) { if (wl_join_status == JOIN_COMPLETED) { return WL_E_OK; } else if (wl_join_status & JOIN_NO_NETWORKS) { return WL_E_NO_NETWORK; } else { return WL_E_ERROR; } } int wl_wait(sem_t* sem, uint32_t timeout) { struct timespec ts; int ret; clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += timeout / 1000; ts.tv_nsec += (timeout % 1000) * 1000; ret = sem_timedwait(sem, &ts); if (ret == -ETIMEDOUT) { WL_DBG("semaphore timeout\n"); return WL_E_TIMEOUT; } else { return WL_E_OK; } } int wl_wake(sem_t* sem) { return sem_post(sem); } int wl_event_register( const uint32_t *event_type, uint32_t event_num, wl_event_cb_t callback, void* user_data) { int ret = WL_E_OK; #ifdef WL_CONFIG_CHECK_EVENT uint8_t event_mask[WL_EVENTING_MASK_LEN]; uint32_t count = 0; #endif /* WL_CONFIG_CHECK_EVENT */ wl_event_item_t *item; int i; WL_DBG("callback = 0x%08x, user_data = 0x%08x, wl_event_num = %d\n", (int)callback, (int)user_data, wl_event_num); pthread_mutex_lock(&wl_mutex); if (wl_event_num == WL_EVENT_LIST_SIZE) { return -1; } #ifdef WL_CONFIG_CHECK_EVENT memset(event_mask, 0, WL_EVENTING_MASK_LEN); ret = wl_iovar_get("event_msgs", event_mask, WL_EVENTING_MASK_LEN); if (ret < 0) { return ret; } #endif /* WL_CONFIG_CHECK_EVENT */ item = &wl_event_list[wl_event_num]; for (i = 0; i < event_num; i++) { if (event_type[i] > WL_EVENT_TYPE_MAX) { return -1; } #ifdef WL_CONFIG_CHECK_EVENT if (!ISSET(event_mask, event_type[i])) { SETBIT(event_mask, event_type[i]); SETBIT(item->reg_event_mask, event_type[i]); count++; } #endif /* WL_CONFIG_CHECK_EVENT */ SETBIT(item->event_mask, event_type[i]); } #ifdef WL_CONFIG_CHECK_EVENT if (count > 0) { item->is_event_set = true; ret = wl_iovar_set("event_msgs", &event_mask, WL_EVENTING_MASK_LEN); if (ret < 0) { return ret; } } #endif /* WL_CONFIG_CHECK_EVENT */ item->callback = callback; item->user_data = user_data; wl_event_num++; pthread_mutex_unlock(&wl_mutex); return ret; } int wl_event_deregister(wl_event_cb_t callback, void* user_data) { int i; wl_event_item_t *item = NULL; WL_DBG("callback = 0x%08x, user_data = 0x%08x, wl_event_num = %d\n", (int)callback, (int)user_data, wl_event_num); pthread_mutex_lock(&wl_mutex); for (i = 0; i < wl_event_num; i++) { if ((wl_event_list[i].callback == callback) && (wl_event_list[i].user_data == user_data)) { item = &wl_event_list[i]; break; } } if (item == NULL) { return 0; } #ifdef WL_CONFIG_CHECK_EVENT if (item->is_event_set) { int ret; uint8_t event_mask[WL_EVENTING_MASK_LEN]; ret = wl_iovar_get("event_msgs", event_mask, WL_EVENTING_MASK_LEN); if (ret == 0) { for (int j = 0; j < WL_EVENTING_MASK_LEN; i++) { event_mask[j] &= (~item->reg_event_mask[j]); } wl_iovar_set("event_msgs", &event_mask, WL_EVENTING_MASK_LEN); } } #endif /* WL_CONFIG_CHECK_EVENT */ if (i < (wl_event_num - 1)) { memcpy( item, &wl_event_list[i + 1], sizeof(wl_event_item_t) * (wl_event_num - i - 1)); } wl_event_num--; memset(&wl_event_list[wl_event_num], 0, sizeof(wl_event_item_t)); pthread_mutex_unlock(&wl_mutex); return 0; } int wl_connect_status_register(wl_connect_status_cb_t callback, void* user_data) { wl_connect_status_callback = callback; wl_connect_status_userdata = user_data; } int wl_connect_status_deregister(wl_connect_status_cb_t callback) { wl_connect_status_callback = NULL; wl_connect_status_userdata = NULL; }