diff --git a/drivers/wireless/ieee80211/bcmf_driver.c b/drivers/wireless/ieee80211/bcmf_driver.c index 94a2c750c5..39fdcabd94 100644 --- a/drivers/wireless/ieee80211/bcmf_driver.c +++ b/drivers/wireless/ieee80211/bcmf_driver.c @@ -50,6 +50,8 @@ #include #include #include +#include +#include #include "bcmf_driver.h" #include "bcmf_cdc.h" @@ -65,6 +67,12 @@ #define DOT11_BSSTYPE_ANY 2 #define BCMF_SCAN_TIMEOUT_TICK (5*CLOCKS_PER_SEC) #define BCMF_AUTH_TIMEOUT_MS 10000 +#define BCMF_SCAN_RESULT_SIZE 1024 + +/* Helper to get iw_event size */ + +#define BCMF_IW_EVENT_SIZE(field) \ + (offsetof(struct iw_event, u)+sizeof(((union iwreq_data*)0)->field)) /**************************************************************************** * Private Types @@ -366,6 +374,9 @@ void bcmf_wl_auth_event_handler(FAR struct bcmf_dev_s *priv, } } +/* bcmf_wl_scan_event_handler must run at high priority else + * race condition may occur on priv->scan_result field + */ void bcmf_wl_scan_event_handler(FAR struct bcmf_dev_s *priv, struct bcmf_event_s *event, unsigned int len) { @@ -414,7 +425,7 @@ void bcmf_wl_scan_event_handler(FAR struct bcmf_dev_s *priv, goto exit_invalid_frame; } - /* wl_escan_result already cointains a wl_bss_info field */ + /* wl_escan_result structure cointains a wl_bss_info field */ len = result->buflen - sizeof(struct wl_escan_result) + sizeof(struct wl_bss_info); @@ -425,6 +436,15 @@ void bcmf_wl_scan_event_handler(FAR struct bcmf_dev_s *priv, while (len > 0 && bss_count < result->bss_count) { + 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; + + result_size = BCMF_SCAN_RESULT_SIZE - priv->scan_result_size; bss_info_len = bss->length; if (len < bss_info_len) @@ -433,11 +453,211 @@ void bcmf_wl_scan_event_handler(FAR struct bcmf_dev_s *priv, goto exit_invalid_frame; } + /* 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)) + { + iwe = (struct iw_event*)&priv->scan_result[check_offset]; + + if (iwe->cmd == SIOCGIWAP) + { + if (memcmp(&iwe->u.ap_addr.sa_data, bss->BSSID.ether_addr_octet, + sizeof(bss->BSSID.ether_addr_octet)) == 0) + { + goto process_next_bss; + } + } + + check_offset += iwe->len; + } + 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[3], bss->BSSID.ether_addr_octet[3], + bss->BSSID.ether_addr_octet[2], bss->BSSID.ether_addr_octet[3], bss->BSSID.ether_addr_octet[4], bss->BSSID.ether_addr_octet[5]); + /* 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: + // wlinfo("unhandled IE entry %d %d\n", ie_buffer[ie_offset], + // ie_buffer[ie_offset+1]); + 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; @@ -632,12 +852,26 @@ int bcmf_wl_start_scan(FAR struct bcmf_dev_s *priv, struct iwreq *iwr) /* Lock control_mutex semaphore */ if ((ret = sem_wait(&priv->control_mutex)) != OK) + { + goto exit_failed; + } + + /* Allocate buffer to store scan result */ + + if (priv->scan_result == NULL) { - goto exit_failed; + priv->scan_result = kmm_malloc(BCMF_SCAN_RESULT_SIZE); + if (priv->scan_result == NULL) + { + wlerr("Cannot allocate result buffer\n"); + ret = -ENOMEM; + goto exit_sem_post; + } } wlinfo("start scan\n"); + priv->scan_result_size = 0; priv->scan_status = BCMF_SCAN_RUN; out_len = sizeof(scan_params); @@ -658,8 +892,8 @@ int bcmf_wl_start_scan(FAR struct bcmf_dev_s *priv, struct iwreq *iwr) return OK; exit_sem_post: - sem_post(&priv->control_mutex); priv->scan_status = BCMF_SCAN_DISABLED; + sem_post(&priv->control_mutex); exit_failed: wlinfo("Failed\n"); return ret; @@ -667,19 +901,80 @@ exit_failed: int bcmf_wl_get_scan_results(FAR struct bcmf_dev_s *priv, struct iwreq *iwr) { - /* Not implemented yet, set len to zero */ - - iwr->u.data.length = 0; + int ret = OK; if (priv->scan_status == BCMF_SCAN_RUN) { - return -EAGAIN; + ret = -EAGAIN; + goto exit_failed; } - if (priv->scan_status == BCMF_SCAN_DONE) + + if (priv->scan_status != BCMF_SCAN_DONE) { - return OK; + ret = -EINVAL; + goto exit_failed; } - return -EINVAL; + + /* Lock control_mutex semaphore to avoid race condition */ + + if ((ret = sem_wait(&priv->control_mutex)) != OK) + { + ret = -EIO; + goto exit_failed; + } + + if (!priv->scan_result) + { + /* Result have already been requested */ + + ret = OK; + iwr->u.data.length = 0; + goto exit_sem_post; + } + + if (iwr->u.data.pointer == NULL || + iwr->u.data.length < priv->scan_result_size) + { + /* Stat request, return scan_result_size */ + + ret = -E2BIG; + iwr->u.data.pointer = NULL; + iwr->u.data.length = priv->scan_result_size; + goto exit_sem_post; + } + + 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: + sem_post(&priv->control_mutex); + +exit_failed: + if (ret) + { + iwr->u.data.length = 0; + } + return ret; } int bcmf_wl_set_auth_param(FAR struct bcmf_dev_s *priv, struct iwreq *iwr) diff --git a/drivers/wireless/ieee80211/bcmf_driver.h b/drivers/wireless/ieee80211/bcmf_driver.h index 937427a49b..a9d712011f 100644 --- a/drivers/wireless/ieee80211/bcmf_driver.h +++ b/drivers/wireless/ieee80211/bcmf_driver.h @@ -93,6 +93,8 @@ struct bcmf_dev_s int scan_status; /* Current scan status */ WDOG_ID 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 */ sem_t auth_signal; /* Authentication notification signal */ int auth_status; /* Authentication status */ diff --git a/drivers/wireless/ieee80211/bcmf_ioctl.h b/drivers/wireless/ieee80211/bcmf_ioctl.h index 7c4cc235f2..48d69bf184 100644 --- a/drivers/wireless/ieee80211/bcmf_ioctl.h +++ b/drivers/wireless/ieee80211/bcmf_ioctl.h @@ -106,6 +106,10 @@ typedef struct wl_bss_info /* variable length Information Elements */ } wl_bss_info_t; +#define DOT11_CAP_ESS 0x0001 +#define DOT11_CAP_IBSS 0x0002 +#define DOT11_CAP_PRIVACY 0x0010 + typedef struct wlc_ssid { uint32_t SSID_len; diff --git a/drivers/wireless/ieee80211/bcmf_utils.h b/drivers/wireless/ieee80211/bcmf_utils.h index 32fd4ca562..ede2098c2b 100644 --- a/drivers/wireless/ieee80211/bcmf_utils.h +++ b/drivers/wireless/ieee80211/bcmf_utils.h @@ -47,6 +47,14 @@ #define container_of(ptr, type, member) \ (type *)((uint8_t *)(ptr) - offsetof(type, member)) +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ diff --git a/include/nuttx/wireless/ieee80211/ieee80211.h b/include/nuttx/wireless/ieee80211/ieee80211.h index b109487d0b..62ea4491d3 100644 --- a/include/nuttx/wireless/ieee80211/ieee80211.h +++ b/include/nuttx/wireless/ieee80211/ieee80211.h @@ -504,7 +504,7 @@ begin_packed_struct struct ieee80211_qosframe uint8_t i_addr3[IEEE80211_ADDR_LEN]; uint8_t i_seq[2]; uint8_t i_qos[2]; -} end_packet_struct; +} end_packed_struct; begin_packed_struct struct ieee80211_htframe /* 11n */ { @@ -516,7 +516,7 @@ begin_packed_struct struct ieee80211_htframe /* 11n */ uint8_t i_seq[2]; uint8_t i_qos[2]; uint8_t i_ht[4]; -} end_packet_struct; +} end_packed_struct; begin_packed_struct struct ieee80211_frame_addr4 { @@ -527,7 +527,7 @@ begin_packed_struct struct ieee80211_frame_addr4 uint8_t i_addr3[IEEE80211_ADDR_LEN]; uint8_t i_seq[2]; uint8_t i_addr4[IEEE80211_ADDR_LEN]; -} end_packet_struct; +} end_packed_struct; begin_packed_struct struct ieee80211_qosframe_addr4 { @@ -539,7 +539,7 @@ begin_packed_struct struct ieee80211_qosframe_addr4 uint8_t i_seq[2]; uint8_t i_addr4[IEEE80211_ADDR_LEN]; uint8_t i_qos[2]; -} end_packet_struct; +} end_packed_struct; begin_packed_struct struct ieee80211_htframe_addr4 /* 11n */ { @@ -552,7 +552,7 @@ begin_packed_struct struct ieee80211_htframe_addr4 /* 11n */ uint8_t i_addr4[IEEE80211_ADDR_LEN]; uint8_t i_qos[2]; uint8_t i_ht[4]; -} end_packet_struct; +} end_packed_struct; /* Control frames. */ @@ -565,7 +565,7 @@ begin_packed_struct struct ieee80211_frame_min /* FCS */ -} end_packet_struct; +} end_packed_struct; begin_packed_struct struct ieee80211_frame_rts { @@ -576,7 +576,7 @@ begin_packed_struct struct ieee80211_frame_rts /* FCS */ -} end_packet_struct; +} end_packed_struct; struct ieee80211_frame_cts { @@ -586,7 +586,7 @@ struct ieee80211_frame_cts /* FCS */ -} end_packet_struct; +} end_packed_struct; struct ieee80211_frame_ack { @@ -596,7 +596,7 @@ struct ieee80211_frame_ack /* FCS */ -} end_packet_struct; +} end_packed_struct; struct ieee80211_frame_pspoll { @@ -607,7 +607,7 @@ struct ieee80211_frame_pspoll /* FCS */ -} end_packet_struct; +} end_packed_struct; struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */ @@ -618,7 +618,7 @@ struct ieee80211_frame_cfend /* FCS */ -} end_packet_struct; +} end_packed_struct; /* Information elements (see Table 7-26). */ @@ -818,7 +818,7 @@ struct ieee80211_eapol_key uint8_t reserved[8]; uint8_t mic[EAPOL_KEY_MIC_LEN]; uint8_t paylen[2]; -} end_packet_struct; +} end_packed_struct; /* Pairwise Transient Key (see 8.5.1.2) */ @@ -827,7 +827,7 @@ struct ieee80211_ptk uint8_t kck[16]; /* Key Confirmation Key */ uint8_t kek[16]; /* Key Encryption Key */ uint8_t tk[32]; /* Temporal Key */ -} end_packet_struct; +} end_packed_struct; /* Key Data Encapsulation (see Table 62) */ diff --git a/include/nuttx/wireless/wireless.h b/include/nuttx/wireless/wireless.h index 5455e14d4f..144e346fca 100644 --- a/include/nuttx/wireless/wireless.h +++ b/include/nuttx/wireless/wireless.h @@ -160,6 +160,47 @@ #define WL_NNETCMDS 0x0032 /* Number of network commands */ #define WL_USERFIRST (WL_NETFIRST + WL_NNETCMDS) +/* ----------------------- WIRELESS EVENTS ----------------------- */ +/* Those are *NOT* ioctls, do not issue request on them !!! */ +/* Most events use the same identifier as ioctl requests */ + +#define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */ +#define IWEVQUAL 0x8C01 /* Quality part of statistics (scan) */ +#define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */ +#define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */ +#define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */ +#define IWEVGENIE 0x8C05 /* Generic IE (WPA, RSN, WMM, ..) + * (scan results); This includes id and + * length fields. One IWEVGENIE may + * contain more than one IE. Scan + * results may contain one or more + * IWEVGENIE events. */ +#define IWEVMICHAELMICFAILURE 0x8C06 /* Michael MIC failure + * (struct iw_michaelmicfailure) + */ +#define IWEVASSOCREQIE 0x8C07 /* IEs used in (Re)Association Request. + * The data includes id and length + * fields and may contain more than one + * IE. This event is required in + * Managed mode if the driver + * generates its own WPA/RSN IE. This + * should be sent just before + * IWEVREGISTERED event for the + * association. */ +#define IWEVASSOCRESPIE 0x8C08 /* IEs used in (Re)Association + * Response. The data includes id and + * length fields and may contain more + * than one IE. This may be sent + * between IWEVASSOCREQIE and + * IWEVREGISTERED events for the + * association. */ +#define IWEVPMKIDCAND 0x8C09 /* PMKID candidate for RSN + * pre-authentication + * (struct iw_pmkid_cand) */ + +#define IWEVFIRST 0x8C00 +#define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST) + /* Other Common Wireless Definitions ***********************************************/ /* Maximum size of the ESSID and NICKN strings */ @@ -178,6 +219,31 @@ #define IW_MODE_MESH 7 /* Mesh (IEEE 802.11s) network */ #define IW_MODE_NFLAGS 8 +/* Statistics flags (bitmask in updated) */ + +#define IW_QUAL_QUAL_UPDATED 0x01 /* Value was updated since last read */ +#define IW_QUAL_LEVEL_UPDATED 0x02 +#define IW_QUAL_NOISE_UPDATED 0x04 +#define IW_QUAL_ALL_UPDATED 0x07 +#define IW_QUAL_DBM 0x08 /* Level + Noise are dBm */ +#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */ +#define IW_QUAL_LEVEL_INVALID 0x20 +#define IW_QUAL_NOISE_INVALID 0x40 +#define IW_QUAL_RCPI 0x80 /* Level + Noise are 802.11k RCPI */ +#define IW_QUAL_ALL_INVALID 0x70 + +/* Flags for encoding (along with the token) */ + +#define IW_ENCODE_INDEX 0x00FF /* Token index (if needed) */ +#define IW_ENCODE_FLAGS 0xFF00 /* Flags defined below */ +#define IW_ENCODE_MODE 0xF000 /* Modes defined below */ +#define IW_ENCODE_DISABLED 0x8000 /* Encoding disabled */ +#define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */ +#define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */ +#define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */ +#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */ +#define IW_ENCODE_TEMP 0x0400 /* Temporary key */ + /* Frequency flags */ #define IW_FREQ_AUTO 0 /* Let the driver decides */