diff --git a/drivers/wireless/ieee80211/bcm43xxx/Kconfig b/drivers/wireless/ieee80211/bcm43xxx/Kconfig index d57b0e3c4c1..a5a48d7be0a 100644 --- a/drivers/wireless/ieee80211/bcm43xxx/Kconfig +++ b/drivers/wireless/ieee80211/bcm43xxx/Kconfig @@ -125,11 +125,11 @@ config IEEE80211_BROADCOM_SCHED_PRIORITY This parameter should be set the bcmf daemon thread schedule priority -config IEEE80211_BROADCOM_SCAN_RESULT_SIZE - int "Broadcom BCMF escan result size" - default 1024 +config IEEE80211_BROADCOM_SCAN_RESULT_ENTRIES + int "Broadcom BCMF escan result entries" + default 10 ---help--- - This parameter should be set the bcmf escan result buffer size + This parameter should be set the bcmf escan result buffer entries if IEEE80211_BROADCOM_FULLMAC diff --git a/drivers/wireless/ieee80211/bcm43xxx/bcmf_driver.c b/drivers/wireless/ieee80211/bcm43xxx/bcmf_driver.c index c477cfc7a7a..721336b645b 100644 --- a/drivers/wireless/ieee80211/bcm43xxx/bcmf_driver.c +++ b/drivers/wireless/ieee80211/bcm43xxx/bcmf_driver.c @@ -53,10 +53,10 @@ * Pre-processor Definitions ****************************************************************************/ -#define DOT11_BSSTYPE_ANY 2 -#define BCMF_SCAN_TIMEOUT_TICK (5*CLOCKS_PER_SEC) -#define BCMF_AUTH_TIMEOUT_MS 20000 /* was 10000 */ -#define BCMF_SCAN_RESULT_SIZE CONFIG_IEEE80211_BROADCOM_SCAN_RESULT_SIZE +#define DOT11_BSSTYPE_ANY 2 +#define BCMF_SCAN_TIMEOUT_TICK (5*CLOCKS_PER_SEC) +#define BCMF_AUTH_TIMEOUT_MS 20000 /* was 10000 */ +#define BCMF_SCAN_RESULT_ENTRIES CONFIG_IEEE80211_BROADCOM_SCAN_RESULT_ENTRIES /* CLM file is cut into pieces of MAX_CHUNK_LEN. * It is relatively small because dongles (FW) have a small maximum size @@ -623,16 +623,19 @@ void bcmf_wl_auth_event_handler(FAR struct bcmf_dev_s *priv, */ void bcmf_wl_scan_event_handler(FAR struct bcmf_dev_s *priv, - struct bcmf_event_s *event, + FAR struct bcmf_event_s *event, unsigned int len) { - uint32_t status; - uint32_t event_len; - struct wl_escan_result *result; - struct wl_bss_info *bss; - unsigned int bss_info_len; + FAR struct wl_escan_result *result; unsigned int escan_result_len; - unsigned int bss_count = 0; + FAR struct wl_bss_info *curr; + FAR struct wl_bss_info *bss; + uint32_t event_len; + int16_t worst_rssi; + int worst_entry; + uint32_t status; + int i; + int j; event_len = len; @@ -666,7 +669,7 @@ void bcmf_wl_scan_event_handler(FAR struct bcmf_dev_s *priv, /* Process escan result payload */ - result = (struct wl_escan_result *)&event[1]; + result = (FAR struct wl_escan_result *)&event[1]; if (len < result->buflen || result->buflen < sizeof(struct wl_escan_result)) @@ -674,260 +677,78 @@ void bcmf_wl_scan_event_handler(FAR struct bcmf_dev_s *priv, goto exit_invalid_frame; } - /* wl_escan_result structure contains a wl_bss_info field */ - - len = result->buflen - sizeof(struct wl_escan_result) - + sizeof(struct wl_bss_info); - /* Process bss_infos */ bss = result->bss_info; - while (len > 0 && bss_count < result->bss_count) + for (i = 0; i < result->bss_count; i++) { - struct iw_event *iwe; - unsigned int result_size; - size_t essid_len; - size_t essid_len_aligned; - uint8_t *ie_buffer; - unsigned int ie_offset; - unsigned int check_offset; + worst_entry = -1; + worst_rssi = 0; - result_size = BCMF_SCAN_RESULT_SIZE - priv->scan_result_size; - bss_info_len = bss->length; + wlinfo("Scan result: <%.32s> " + "%02x:%02x:%02x:%02x:%02x:%02x " + "signal %d %d %d\n", + bss->SSID, + bss->BSSID.ether_addr_octet[0], + bss->BSSID.ether_addr_octet[1], + bss->BSSID.ether_addr_octet[2], + bss->BSSID.ether_addr_octet[3], + bss->BSSID.ether_addr_octet[4], + bss->BSSID.ether_addr_octet[5], + bss->RSSI, bss->phy_noise, bss->SNR); - if (len < bss_info_len) + for (j = 0; j < priv->scan_result_entries; j++) { - wlerr("bss_len error %d %d\n", len, bss_info_len); - goto exit_invalid_frame; + curr = &priv->scan_result[j]; + + /* Check if current bss AP is not already detected */ + + if (memcmp(&curr->BSSID, &bss[i].BSSID, + sizeof(curr->BSSID)) == 0) + { + /* Replace the duplicate entry if rssi is + * better than before + */ + + if (curr->RSSI < bss[i].RSSI) + { + memcpy(curr, bss, sizeof(*curr)); + } + + goto process_next_bss; + } + + /* Find worst rssi and mark the entry */ + + if (curr->RSSI < worst_rssi) + { + worst_entry = j; + worst_rssi = curr->RSSI; + } } - /* Append current bss_info to priv->scan_results - * FIXME protect this against race conditions - */ - - /* Check if current bss AP is not already detected */ - - check_offset = 0; - - while (priv->scan_result_size - check_offset - >= offsetof(struct iw_event, u)) + if (priv->scan_result_entries == BCMF_SCAN_RESULT_ENTRIES) { - iwe = (struct iw_event *)&priv->scan_result[check_offset]; + /* Entries full and replace the worst entry */ - if (iwe->cmd == SIOCGIWAP) + if (worst_entry >= 0) { - if (memcmp(&iwe->u.ap_addr.sa_data, - bss->BSSID.ether_addr_octet, - sizeof(bss->BSSID.ether_addr_octet)) == 0) + curr = &priv->scan_result[worst_entry]; + if (curr->RSSI < bss->RSSI) { - goto process_next_bss; + memcpy(curr, bss, sizeof(*curr)); } } - check_offset += iwe->len; +process_next_bss: + continue; } - wlinfo("Scan result: <%.32s> %02x:%02x:%02x:%02x:%02x:%02x\n", - bss->SSID, - bss->BSSID.ether_addr_octet[0], - bss->BSSID.ether_addr_octet[1], - bss->BSSID.ether_addr_octet[2], - bss->BSSID.ether_addr_octet[3], - bss->BSSID.ether_addr_octet[4], - bss->BSSID.ether_addr_octet[5]); + curr = &priv->scan_result[priv->scan_result_entries]; + memcpy(curr, bss, sizeof(*curr)); - /* Copy BSSID */ - - if (result_size < BCMF_IW_EVENT_SIZE(ap_addr)) - { - goto scan_result_full; - } - - iwe = (struct iw_event *)&priv->scan_result[priv->scan_result_size]; - iwe->len = BCMF_IW_EVENT_SIZE(ap_addr); - iwe->cmd = SIOCGIWAP; - memcpy(&iwe->u.ap_addr.sa_data, bss->BSSID.ether_addr_octet, - sizeof(bss->BSSID.ether_addr_octet)); - iwe->u.ap_addr.sa_family = ARPHRD_ETHER; - - priv->scan_result_size += BCMF_IW_EVENT_SIZE(ap_addr); - result_size -= BCMF_IW_EVENT_SIZE(ap_addr); - - /* Copy ESSID */ - - essid_len = min(strlen((const char *)bss->SSID), 32); - essid_len_aligned = (essid_len + 3) & -4; - - if (result_size < BCMF_IW_EVENT_SIZE(essid)+essid_len_aligned) - { - goto scan_result_full; - } - - iwe = (struct iw_event *)&priv->scan_result[priv->scan_result_size]; - iwe->len = BCMF_IW_EVENT_SIZE(essid)+essid_len_aligned; - iwe->cmd = SIOCGIWESSID; - iwe->u.essid.flags = 0; - iwe->u.essid.length = essid_len; - - /* Special processing for iw_point, set offset in pointer field */ - - iwe->u.essid.pointer = (FAR void *)sizeof(iwe->u.essid); - memcpy(&iwe->u.essid + 1, bss->SSID, essid_len); - - priv->scan_result_size += BCMF_IW_EVENT_SIZE(essid)+essid_len_aligned; - result_size -= BCMF_IW_EVENT_SIZE(essid)+essid_len_aligned; - - /* Copy link quality info */ - - if (result_size < BCMF_IW_EVENT_SIZE(qual)) - { - goto scan_result_full; - } - - iwe = (struct iw_event *)&priv->scan_result[priv->scan_result_size]; - iwe->len = BCMF_IW_EVENT_SIZE(qual); - iwe->cmd = IWEVQUAL; - iwe->u.qual.qual = bss->SNR; - wlinfo("signal %d %d %d\n", bss->RSSI, bss->phy_noise, bss->SNR); - iwe->u.qual.level = bss->RSSI; - iwe->u.qual.noise = bss->phy_noise; - iwe->u.qual.updated = IW_QUAL_DBM | IW_QUAL_ALL_UPDATED; - - priv->scan_result_size += BCMF_IW_EVENT_SIZE(qual); - result_size -= BCMF_IW_EVENT_SIZE(qual); - - /* Copy AP mode */ - - if (result_size < BCMF_IW_EVENT_SIZE(mode)) - { - goto scan_result_full; - } - - iwe = (struct iw_event *)&priv->scan_result[priv->scan_result_size]; - iwe->len = BCMF_IW_EVENT_SIZE(mode); - iwe->cmd = SIOCGIWMODE; - if (bss->capability & DOT11_CAP_ESS) - { - iwe->u.mode = IW_MODE_INFRA; - } - else if (bss->capability & DOT11_CAP_IBSS) - { - iwe->u.mode = IW_MODE_ADHOC; - } - else - { - iwe->u.mode = IW_MODE_AUTO; - } - - priv->scan_result_size += BCMF_IW_EVENT_SIZE(mode); - result_size -= BCMF_IW_EVENT_SIZE(mode); - - /* Copy AP encryption mode */ - - if (result_size < BCMF_IW_EVENT_SIZE(data)) - { - goto scan_result_full; - } - - iwe = (struct iw_event *)&priv->scan_result[priv->scan_result_size]; - iwe->len = BCMF_IW_EVENT_SIZE(data); - iwe->cmd = SIOCGIWENCODE; - iwe->u.data.flags = bss->capability & DOT11_CAP_PRIVACY ? - IW_ENCODE_ENABLED | IW_ENCODE_NOKEY : - IW_ENCODE_DISABLED; - iwe->u.data.length = 0; - iwe->u.essid.pointer = NULL; - - priv->scan_result_size += BCMF_IW_EVENT_SIZE(data); - result_size -= BCMF_IW_EVENT_SIZE(data); - - /* Copy relevant raw IE frame */ - - if (bss->ie_offset >= bss_info_len || - bss->ie_length > bss_info_len - bss->ie_offset) - { - goto process_next_bss; - } - - ie_offset = 0; - ie_buffer = (uint8_t *)bss + bss->ie_offset; - - while (1) - { - size_t ie_frame_size; - - if (bss->ie_length - ie_offset < 2) - { - /* Minimum Information element size is 2 bytes */ - - break; - } - - ie_frame_size = ie_buffer[ie_offset + 1] + 2; - - if (ie_frame_size > bss->ie_length - ie_offset) - { - /* Entry too big */ - - break; - } - - switch (ie_buffer[ie_offset]) - { - case IEEE80211_ELEMID_RSN: - { - size_t ie_frame_size_aligned; - ie_frame_size_aligned = (ie_frame_size + 3) & -4; - - wlinfo("found RSN\n"); - - if (result_size < BCMF_IW_EVENT_SIZE(data) + - ie_frame_size_aligned) - { - break; - } - - iwe = (struct iw_event *) - &priv->scan_result[priv->scan_result_size]; - - iwe->len = BCMF_IW_EVENT_SIZE(data)+ie_frame_size_aligned; - iwe->cmd = IWEVGENIE; - iwe->u.data.flags = 0; - iwe->u.data.length = ie_frame_size; - iwe->u.data.pointer = (FAR void *)sizeof(iwe->u.data); - memcpy(&iwe->u.data + 1, &ie_buffer[ie_offset], - ie_frame_size); - - priv->scan_result_size += BCMF_IW_EVENT_SIZE(essid) + - ie_frame_size_aligned; - result_size -= BCMF_IW_EVENT_SIZE(essid) + - ie_frame_size_aligned; - break; - } - - default: - break; - } - - ie_offset += ie_buffer[ie_offset + 1] + 2; - } - - goto process_next_bss; - - scan_result_full: - - /* Continue instead of break to log dropped AP results */ - - wlerr("No more space in scan_result buffer\n"); - - process_next_bss: - - /* Process next bss_info */ - - len -= bss_info_len; - bss = (struct wl_bss_info *)((uint8_t *)bss + bss_info_len); - bss_count += 1; + priv->scan_result_entries++; } wl_escan_result_processed: @@ -959,7 +780,124 @@ wl_escan_result_processed: exit_invalid_frame: wlerr("Invalid scan result event\n"); - bcmf_hexdump((uint8_t *)event, event_len, (unsigned long)event); + bcmf_hexdump((FAR uint8_t *)event, event_len, (unsigned long)event); +} + +static int bcmf_wl_scan_format_results(FAR struct bcmf_dev_s *priv, + FAR struct iwreq *iwr) +{ + FAR wl_bss_info_t *scan_result[BCMF_SCAN_RESULT_ENTRIES]; + FAR wl_bss_info_t *info; + FAR struct iw_event *iwe; + FAR char *pointer; + int len; + int i; + int j; + + if (priv->scan_result_entries == 0) + { + iwr->u.data.length = 0; + return OK; + } + + len = IW_EV_LEN(ap_addr) + IW_EV_LEN(qual) + + IW_EV_LEN(freq) + IW_EV_LEN(data) + + IW_EV_LEN(essid); + + len *= priv->scan_result_entries; + + for (i = 0; i < priv->scan_result_entries; i++) + { + scan_result[i] = &priv->scan_result[i]; + len += (min(strlen((FAR const char *)scan_result[i]->SSID), + 32) + 3) & ~3; + } + + if (iwr->u.data.pointer == NULL || iwr->u.data.length < len) + { + iwr->u.data.length = len; + return -E2BIG; + } + + /* Sort list by RSSI */ + + for (i = 0; i < priv->scan_result_entries; i++) + { + for (j = 0; j + 1 < priv->scan_result_entries - i; j++) + { + if (scan_result[j]->RSSI < scan_result[j + 1]->RSSI) + { + info = scan_result[j]; + scan_result[j] = scan_result[j + 1]; + scan_result[j + 1] = info; + } + } + } + + pointer = iwr->u.data.pointer; + + /* Copy scan result */ + + for (i = 0; i < priv->scan_result_entries; i++) + { + info = scan_result[i]; + + /* Copy BSSID */ + + iwe = (FAR struct iw_event *)pointer; + iwe->cmd = SIOCGIWAP; + iwe->u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(&iwe->u.ap_addr.sa_data, + info->BSSID.ether_addr_octet, IFHWADDRLEN); + iwe->len = IW_EV_LEN(ap_addr); + pointer += iwe->len; + + /* Copy ESSID */ + + iwe = (FAR struct iw_event *)pointer; + iwe->cmd = SIOCGIWESSID; + iwe->u.essid.flags = 0; + iwe->u.essid.length = min(strlen((FAR const char *)info->SSID), 32); + iwe->u.essid.pointer = (FAR void *)sizeof(iwe->u.essid); + memcpy(&iwe->u.essid + 1, info->SSID, iwe->u.essid.length); + iwe->len = IW_EV_LEN(essid) + ((iwe->u.essid.length + 3) & ~3); + pointer += iwe->len; + + /* Copy link quality info */ + + iwe = (FAR struct iw_event *)pointer; + iwe->cmd = IWEVQUAL; + iwe->u.qual.qual = info->SNR; + iwe->u.qual.level = info->RSSI; + iwe->u.qual.noise = info->phy_noise; + iwe->u.qual.updated = IW_QUAL_DBM | IW_QUAL_ALL_UPDATED; + iwe->len = IW_EV_LEN(qual); + pointer += iwe->len; + + /* Copy AP control channel */ + + iwe = (FAR struct iw_event *)pointer; + iwe->cmd = SIOCGIWFREQ; + iwe->u.freq.e = 0; + iwe->u.freq.m = info->ctl_ch; + iwe->len = IW_EV_LEN(freq); + pointer += iwe->len; + + /* Copy AP encryption mode */ + + iwe = (FAR struct iw_event *)pointer; + iwe->cmd = SIOCGIWENCODE; + iwe->u.data.flags = info->capability & DOT11_CAP_PRIVACY ? + IW_ENCODE_ENABLED | IW_ENCODE_NOKEY : + IW_ENCODE_DISABLED; + iwe->u.data.length = 0; + iwe->u.essid.pointer = NULL; + iwe->len = IW_EV_LEN(data); + pointer += iwe->len; + } + + iwr->u.data.length = pointer - (FAR char *)iwr->u.data.pointer; + return OK; } void bcmf_wl_scan_timeout(wdparm_t arg) @@ -1130,7 +1068,8 @@ int bcmf_wl_start_scan(FAR struct bcmf_dev_s *priv, struct iwreq *iwr) if (priv->scan_result == NULL) { - priv->scan_result = kmm_malloc(BCMF_SCAN_RESULT_SIZE); + priv->scan_result = kmm_malloc(sizeof(wl_bss_info_t) * + BCMF_SCAN_RESULT_ENTRIES); if (priv->scan_result == NULL) { wlerr("Cannot allocate result buffer\n"); @@ -1141,7 +1080,7 @@ int bcmf_wl_start_scan(FAR struct bcmf_dev_s *priv, struct iwreq *iwr) wlinfo("start scan\n"); - priv->scan_result_size = 0; + priv->scan_result_entries = 0; priv->scan_status = BCMF_SCAN_RUN; out_len = sizeof(scan_params); @@ -1202,41 +1141,16 @@ int bcmf_wl_get_scan_results(FAR struct bcmf_dev_s *priv, struct iwreq *iwr) goto exit_sem_post; } - if (iwr->u.data.pointer == NULL || - iwr->u.data.length < priv->scan_result_size) + ret = bcmf_wl_scan_format_results(priv, iwr); + if (ret == OK) { - /* Stat request, return scan_result_size */ + /* Free scan result buffer */ - ret = -E2BIG; - iwr->u.data.pointer = NULL; - iwr->u.data.length = priv->scan_result_size; - goto exit_sem_post; + kmm_free(priv->scan_result); + priv->scan_result = NULL; + priv->scan_result_entries = 0; } - if (priv->scan_result_size <= 0) - { - ret = OK; - iwr->u.data.length = 0; - goto exit_free_buffer; - } - - /* Copy result to user buffer */ - - if (iwr->u.data.length > priv->scan_result_size) - { - iwr->u.data.length = priv->scan_result_size; - } - - memcpy(iwr->u.data.pointer, priv->scan_result, iwr->u.data.length); - -exit_free_buffer: - - /* Free scan result buffer */ - - kmm_free(priv->scan_result); - priv->scan_result = NULL; - priv->scan_result_size = 0; - exit_sem_post: nxsem_post(&priv->control_mutex); diff --git a/drivers/wireless/ieee80211/bcm43xxx/bcmf_driver.h b/drivers/wireless/ieee80211/bcm43xxx/bcmf_driver.h index 3a5c96d63e1..1361912e1ad 100644 --- a/drivers/wireless/ieee80211/bcm43xxx/bcmf_driver.h +++ b/drivers/wireless/ieee80211/bcm43xxx/bcmf_driver.h @@ -89,8 +89,8 @@ struct bcmf_dev_s int scan_status; /* Current scan status */ struct wdog_s scan_timeout; /* Scan timeout timer */ - FAR uint8_t *scan_result; /* Temp buffer that holds results */ - unsigned int scan_result_size; /* Current size of temp buffer */ + FAR wl_bss_info_t *scan_result; /* Temp buffer that holds results */ + unsigned int scan_result_entries; /* Current entries of temp buffer */ sem_t auth_signal; /* Authentication notification signal */ int auth_status; /* Authentication status */