From bd5d6ce98648636d1171cd15a068c227de05b5c2 Mon Sep 17 00:00:00 2001 From: Simon Piriou Date: Tue, 2 May 2017 08:48:13 -0600 Subject: [PATCH] bcmf: implement basic wext interface for authentication --- drivers/wireless/ieee80211/bcmf_driver.c | 297 +++++++++++++++++++++-- drivers/wireless/ieee80211/bcmf_driver.h | 15 +- drivers/wireless/ieee80211/bcmf_netdev.c | 17 +- drivers/wireless/ieee80211/bcmf_utils.c | 5 +- 4 files changed, 311 insertions(+), 23 deletions(-) diff --git a/drivers/wireless/ieee80211/bcmf_driver.c b/drivers/wireless/ieee80211/bcmf_driver.c index 8c623b75ad..791a8394ba 100644 --- a/drivers/wireless/ieee80211/bcmf_driver.c +++ b/drivers/wireless/ieee80211/bcmf_driver.c @@ -66,6 +66,7 @@ #define DOT11_BSSTYPE_ANY 2 #define BCMF_SCAN_TIMEOUT_TICK (5*CLOCKS_PER_SEC) +#define BCMF_AUTH_TIMEOUT_MS 10000 /**************************************************************************** * Private Types @@ -103,6 +104,9 @@ static void bcmf_wl_scan_event_handler(FAR struct bcmf_dev_s *priv, static void bcmf_wl_auth_event_handler(FAR struct bcmf_dev_s *priv, struct bcmf_event_s *event, unsigned int len); +static int bcmf_wl_get_interface(FAR struct bcmf_dev_s *priv, + struct iwreq *iwr); + /**************************************************************************** * Private Data ****************************************************************************/ @@ -212,12 +216,13 @@ int bcmf_driver_initialize(FAR struct bcmf_dev_s *priv) uint32_t out_len; uint32_t value; uint8_t tmp_buf[64]; + int interface = CHIP_STA_INTERFACE; /* Disable TX Gloming feature */ out_len = 4; *(uint32_t*)tmp_buf = 0; - ret = bcmf_cdc_iovar_request(priv, CHIP_STA_INTERFACE, false, + ret = bcmf_cdc_iovar_request(priv, interface, false, IOVAR_STR_TX_GLOM, tmp_buf, &out_len); if (ret != OK) @@ -229,8 +234,8 @@ int bcmf_driver_initialize(FAR struct bcmf_dev_s *priv) out_len = 4; value = 0; - ret = bcmf_cdc_ioctl(priv, CHIP_STA_INTERFACE, true, - WLC_SET_PM, (uint8_t*)&value, &out_len); + ret = bcmf_cdc_ioctl(priv, interface, true, WLC_SET_PM, + (uint8_t*)&value, &out_len); if (ret != OK) { return ret; @@ -240,8 +245,8 @@ int bcmf_driver_initialize(FAR struct bcmf_dev_s *priv) out_len = 4; value = GMODE_AUTO; - ret = bcmf_cdc_ioctl(priv, CHIP_STA_INTERFACE, true, - WLC_SET_GMODE, (uint8_t*)&value, &out_len); + ret = bcmf_cdc_ioctl(priv, interface, true, WLC_SET_GMODE, + (uint8_t*)&value, &out_len); if (ret != OK) { return ret; @@ -251,14 +256,27 @@ int bcmf_driver_initialize(FAR struct bcmf_dev_s *priv) out_len = 4; value = 1; - ret = bcmf_cdc_iovar_request(priv, CHIP_STA_INTERFACE, true, - IOVAR_STR_ROAM_OFF, (uint8_t*)&value, - &out_len); - + ret = bcmf_cdc_iovar_request(priv, interface, true, IOVAR_STR_ROAM_OFF, + (uint8_t*)&value, + &out_len); + + /* TODO configure EAPOL version to default */ + + out_len = 8; + ((uint32_t*)tmp_buf)[0] = interface; + ((uint32_t*)tmp_buf)[1] = (uint32_t)-1; + + if (bcmf_cdc_iovar_request(priv, interface, true, + "bsscfg:"IOVAR_STR_SUP_WPA2_EAPVER, tmp_buf, + &out_len)) + { + return -EIO; + } + /* Query firmware version string */ out_len = sizeof(tmp_buf); - ret = bcmf_cdc_iovar_request(priv, CHIP_STA_INTERFACE, false, + ret = bcmf_cdc_iovar_request(priv, interface, false, IOVAR_STR_VERSION, tmp_buf, &out_len); if (ret != OK) @@ -293,9 +311,18 @@ int bcmf_driver_initialize(FAR struct bcmf_dev_s *priv) bcmf_event_register(priv, bcmf_wl_scan_event_handler, WLC_E_ESCAN_RESULT); - /* Register SET_SSID event */ + /* Register authentication related events */ + bcmf_event_register(priv, bcmf_wl_auth_event_handler, WLC_E_ASSOC_IND_NDIS); + bcmf_event_register(priv, bcmf_wl_auth_event_handler, WLC_E_AUTH); + bcmf_event_register(priv, bcmf_wl_auth_event_handler, WLC_E_ASSOC); + bcmf_event_register(priv, bcmf_wl_auth_event_handler, WLC_E_LINK); + bcmf_event_register(priv, bcmf_wl_auth_event_handler, WLC_E_PSK_SUP); + bcmf_event_register(priv, bcmf_wl_auth_event_handler, WLC_E_JOIN); bcmf_event_register(priv, bcmf_wl_auth_event_handler, WLC_E_SET_SSID); + bcmf_event_register(priv, bcmf_wl_auth_event_handler, WLC_E_DEAUTH_IND); + bcmf_event_register(priv, bcmf_wl_auth_event_handler, WLC_E_DISASSOC); + bcmf_event_register(priv, bcmf_wl_auth_event_handler, WLC_E_DISASSOC_IND); if (bcmf_event_push_config(priv)) { @@ -325,15 +352,21 @@ void bcmf_wl_auth_event_handler(FAR struct bcmf_dev_s *priv, struct bcmf_event_s *event, unsigned int len) { uint32_t type; + uint32_t status; type = bcmf_getle32(&event->type); + status = bcmf_getle32(&event->status); wlinfo("Got auth event %d from <%s>\n", type, event->src_name); - if (type == WLC_E_SET_SSID) + bcmf_hexdump((uint8_t*)event, len, (unsigned long)event); + + if (type == WLC_E_SET_SSID && status == WLC_E_STATUS_SUCCESS) { /* Auth complete */ + priv->auth_status = OK; + sem_post(&priv->auth_signal); } } @@ -397,7 +430,7 @@ void bcmf_wl_scan_event_handler(FAR struct bcmf_dev_s *priv, bss = result->bss_info; - do + while (len > 0 && bss_count < result->bss_count) { bss_info_len = bss->length; @@ -418,7 +451,6 @@ void bcmf_wl_scan_event_handler(FAR struct bcmf_dev_s *priv, bss = (struct wl_bss_info*)((uint8_t*)bss + bss_info_len); bss_count += 1; } - while (len > 0 && bss_count < result->bss_count); wl_escan_result_processed: @@ -479,6 +511,12 @@ void bcmf_wl_scan_timeout(int argc, wdparm_t arg1, ...) sem_post(&priv->control_mutex); } +int bcmf_wl_get_interface(FAR struct bcmf_dev_s *priv, struct iwreq *iwr) +{ + // TODO resolve interface using iwr->ifr_name + return CHIP_STA_INTERFACE; +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -629,4 +667,233 @@ int bcmf_wl_is_scan_done(FAR struct bcmf_dev_s *priv) return OK; } return -EINVAL; -} \ No newline at end of file +} + +int bcmf_wl_set_auth_param(FAR struct bcmf_dev_s *priv, struct iwreq *iwr) +{ + int ret = -ENOSYS; + int interface; + uint32_t out_len; + + interface = bcmf_wl_get_interface(priv, iwr); + + if (interface < 0) + { + return -EINVAL; + } + + switch (iwr->u.param.flags & IW_AUTH_INDEX) + { + case IW_AUTH_WPA_VERSION: + { + uint32_t wpa_version[2]; + uint32_t auth_mode; + + switch (iwr->u.param.value) + { + case IW_AUTH_WPA_VERSION_DISABLED: + wpa_version[1] = 0; + auth_mode = WPA_AUTH_DISABLED; + break; + case IW_AUTH_WPA_VERSION_WPA: + wpa_version[1] = 1; + auth_mode = WPA_AUTH_PSK; + break; + case IW_AUTH_WPA_VERSION_WPA2: + wpa_version[1] = 1; + auth_mode = WPA2_AUTH_PSK; + break; + default: + wlerr("Invalid wpa version %d\n", iwr->u.param.value); + return -EINVAL; + } + + out_len = 8; + wpa_version[0] = interface; + + if (bcmf_cdc_iovar_request(priv, interface, true, + "bsscfg:"IOVAR_STR_SUP_WPA, + (uint8_t*)wpa_version, + &out_len)) + { + return -EIO; + } + + out_len = 4; + if(bcmf_cdc_ioctl(priv, interface, true, WLC_SET_WPA_AUTH, + (uint8_t*)&auth_mode, &out_len)) + { + return -EIO; + } + } + return OK; + + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + { + uint32_t cipher_mode; + uint32_t wep_auth = 0; + + switch (iwr->u.param.value) + { + case IW_AUTH_CIPHER_WEP40: + case IW_AUTH_CIPHER_WEP104: + cipher_mode = WEP_ENABLED; + wep_auth = 1; + break; + case IW_AUTH_CIPHER_TKIP: + cipher_mode = TKIP_ENABLED; + break; + case IW_AUTH_CIPHER_CCMP: + cipher_mode = AES_ENABLED; + break; + default: + wlerr("Invalid cipher mode %d\n", iwr->u.param.value); + return -EINVAL; + } + + out_len = 4; + if(bcmf_cdc_ioctl(priv, interface, true, + WLC_SET_WSEC, (uint8_t*)&cipher_mode, &out_len)) + { + return -EIO; + } + + /* Set authentication mode */ + + out_len = 4; + if(bcmf_cdc_ioctl(priv, interface, true, + WLC_SET_AUTH, (uint8_t*)&wep_auth, &out_len)) + { + return -EIO; + } + } + return OK; + + case IW_AUTH_KEY_MGMT: + case IW_AUTH_TKIP_COUNTERMEASURES: + case IW_AUTH_DROP_UNENCRYPTED: + case IW_AUTH_80211_AUTH_ALG: + case IW_AUTH_WPA_ENABLED: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_ROAMING_CONTROL: + case IW_AUTH_PRIVACY_INVOKED: + default: + wlerr("Unknown cmd %d\n", iwr->u.param.flags); + break; + } + + return ret; +} + +int bcmf_wl_set_mode(FAR struct bcmf_dev_s *priv, struct iwreq *iwr) +{ + int interface; + uint32_t out_len; + uint32_t value; + + interface = bcmf_wl_get_interface(priv, iwr); + + if (interface < 0) + { + return -EINVAL; + } + + out_len = 4; + value = iwr->u.mode == IW_MODE_INFRA ? 1 : 0; + if(bcmf_cdc_ioctl(priv, interface, true, + WLC_SET_INFRA, (uint8_t*)&value, &out_len)) + { + return -EIO; + } + + return OK; +} + +int bcmf_wl_set_encode_ext(FAR struct bcmf_dev_s *priv, struct iwreq *iwr) +{ + int interface; + struct iw_encode_ext *ext; + uint32_t out_len; + wsec_pmk_t psk; + + interface = bcmf_wl_get_interface(priv, iwr); + + if (interface < 0) + { + return -EINVAL; + } + + ext = (struct iw_encode_ext*)iwr->u.encoding.pointer; + + switch (ext->alg) + { + case IW_ENCODE_ALG_TKIP: + break; + case IW_ENCODE_ALG_CCMP: + break; + case IW_ENCODE_ALG_NONE: + case IW_ENCODE_ALG_WEP: + default: + wlerr("Unknown algo %d\n", ext->alg); + return -EINVAL; + } + + memset(&psk, 0, sizeof(wsec_pmk_t)); + memcpy(psk.key, &ext->key, ext->key_len); + psk.key_len = ext->key_len; + psk.flags = WSEC_PASSPHRASE; + + out_len = sizeof(psk); + return bcmf_cdc_ioctl(priv, interface, true, + WLC_SET_WSEC_PMK, (uint8_t*)&psk, &out_len); +} + +int bcmf_wl_set_ssid(FAR struct bcmf_dev_s *priv, struct iwreq *iwr) +{ + int ret; + int interface; + uint32_t out_len; + wlc_ssid_t ssid; + + interface = bcmf_wl_get_interface(priv, iwr); + + if (interface < 0) + { + return -EINVAL; + } + + ssid.SSID_len = iwr->u.essid.length; + memcpy(ssid.SSID, iwr->u.essid.pointer, iwr->u.essid.length); + + /* Configure AP SSID and trig authentication request */ + + out_len = sizeof(ssid); + if(bcmf_cdc_ioctl(priv, interface, true, + WLC_SET_SSID, (uint8_t*)&ssid, &out_len)) + { + return -EIO; + } + + ret = bcmf_sem_wait(&priv->auth_signal, BCMF_AUTH_TIMEOUT_MS); + + wlinfo("semwait done ! %d\n", ret); + + if (ret) + { + wlerr("Associate request timeout\n"); + return -EINVAL; + } + + switch (priv->auth_status) + { + case OK: + wlinfo("AP Join ok\n"); + break; + + default: + wlerr("AP join failed %d\n", priv->auth_status); + return -EINVAL; + } + return OK; + } diff --git a/drivers/wireless/ieee80211/bcmf_driver.h b/drivers/wireless/ieee80211/bcmf_driver.h index c264ca5da9..27b2e51a40 100644 --- a/drivers/wireless/ieee80211/bcmf_driver.h +++ b/drivers/wireless/ieee80211/bcmf_driver.h @@ -94,6 +94,7 @@ struct bcmf_dev_s struct wl_escan_params *scan_params; /* Current scan parameters */ sem_t auth_signal; /* Authentication notification signal */ + int auth_status; /* Authentication status */ }; /* Default bus interface structure */ @@ -126,14 +127,26 @@ struct bcmf_frame_s uint16_t len; /* Frame buffer size */ }; +/* IOCTLs network interface implementation */ + int bcmf_wl_set_mac_address(FAR struct bcmf_dev_s *priv, struct ifreq *req); int bcmf_wl_enable(FAR struct bcmf_dev_s *priv, bool enable); +/* IOCTLs AP scan interface implementation */ + int bcmf_wl_start_scan(FAR struct bcmf_dev_s *priv); int bcmf_wl_is_scan_done(FAR struct bcmf_dev_s *priv); -int bcmf_wl_associate(FAR struct bcmf_dev_s *priv); +/* IOCTLs authentication interface implementation */ + +int bcmf_wl_set_auth_param(FAR struct bcmf_dev_s *priv, struct iwreq *iwr); + +int bcmf_wl_set_encode_ext(FAR struct bcmf_dev_s *priv, struct iwreq *iwr); + +int bcmf_wl_set_mode(FAR struct bcmf_dev_s *priv, struct iwreq *iwr); + +int bcmf_wl_set_ssid(FAR struct bcmf_dev_s *priv, struct iwreq *iwr); #endif /* __DRIVERS_WIRELESS_IEEE80211_BCMF_DRIVER_H */ diff --git a/drivers/wireless/ieee80211/bcmf_netdev.c b/drivers/wireless/ieee80211/bcmf_netdev.c index 32dd54176c..7422991f49 100644 --- a/drivers/wireless/ieee80211/bcmf_netdev.c +++ b/drivers/wireless/ieee80211/bcmf_netdev.c @@ -1010,11 +1010,18 @@ static int bcmf_ioctl(FAR struct net_driver_s *dev, int cmd, ret = bcmf_wl_is_scan_done(priv); break; - case SIOCSIFHWADDR: - /* Update device MAC address */ + case SIOCSIFHWADDR: /* Set device MAC address */ ret = bcmf_wl_set_mac_address(priv, (struct ifreq*)arg); break; + case SIOCSIWAUTH: + ret = bcmf_wl_set_auth_param(priv, (struct iwreq*)arg); + break; + + case SIOCSIWENCODEEXT: + ret = bcmf_wl_set_encode_ext(priv, (struct iwreq*)arg); + break; + case SIOCSIWFREQ: /* Set channel/frequency (Hz) */ wlwarn("WARNING: SIOCSIWFREQ not implemented\n"); ret = -ENOSYS; @@ -1026,8 +1033,7 @@ static int bcmf_ioctl(FAR struct net_driver_s *dev, int cmd, break; case SIOCSIWMODE: /* Set operation mode */ - wlwarn("WARNING: SIOCSIWMODE not implemented\n"); - ret = -ENOSYS; + ret = bcmf_wl_set_mode(priv, (struct iwreq*)arg); break; case SIOCGIWMODE: /* Get operation mode */ @@ -1046,8 +1052,7 @@ static int bcmf_ioctl(FAR struct net_driver_s *dev, int cmd, break; case SIOCSIWESSID: /* Set ESSID (network name) */ - wlwarn("WARNING: SIOCSIWESSID not implemented\n"); - ret = -ENOSYS; + ret = bcmf_wl_set_ssid(priv, (struct iwreq*)arg); break; case SIOCGIWESSID: /* Get ESSID */ diff --git a/drivers/wireless/ieee80211/bcmf_utils.c b/drivers/wireless/ieee80211/bcmf_utils.c index 4fb126356e..b45f649f1e 100644 --- a/drivers/wireless/ieee80211/bcmf_utils.c +++ b/drivers/wireless/ieee80211/bcmf_utils.c @@ -101,12 +101,15 @@ void bcmf_hexdump(uint8_t *data, unsigned int len, unsigned long offset) int bcmf_sem_wait(sem_t *sem, unsigned int timeout_ms) { struct timespec abstime; + unsigned int timeout_sec; /* Get the current time */ (void)clock_gettime(CLOCK_REALTIME, &abstime); - abstime.tv_nsec += 1000 * 1000* timeout_ms; + timeout_sec = timeout_ms/1000; + abstime.tv_sec += timeout_sec; + abstime.tv_nsec += 1000 * 1000* (timeout_ms % 1000); if (abstime.tv_nsec >= 1000 * 1000 * 1000) {