Merge remote-tracking branch 'spiriou/wapi_scan'

This commit is contained in:
Gregory Nutt 2017-05-21 09:25:02 -06:00
commit 679a08e371
6 changed files with 399 additions and 24 deletions

View File

@ -50,6 +50,8 @@
#include <nuttx/kmalloc.h>
#include <nuttx/wdog.h>
#include <nuttx/sdio.h>
#include <nuttx/net/arp.h>
#include <nuttx/wireless/ieee80211/ieee80211.h>
#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)

View File

@ -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 */

View File

@ -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;

View File

@ -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
****************************************************************************/

View File

@ -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) */

View File

@ -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 */