return 0;
}
-static int ath6kl_wlan_parse_beacon(u8 *buf, int frame_len,
- struct ath6kl_common_ie *cie)
-{
- u8 *frm, *efrm;
- u8 elemid_ssid = false;
-
- frm = buf;
- efrm = (u8 *) (frm + frame_len);
-
- /*
- * beacon/probe response frame format
- * [8] time stamp
- * [2] beacon interval
- * [2] capability information
- * [tlv] ssid
- * [tlv] supported rates
- * [tlv] country information
- * [tlv] parameter set (FH/DS)
- * [tlv] erp information
- * [tlv] extended supported rates
- * [tlv] WMM
- * [tlv] WPA or RSN
- * [tlv] Atheros Advanced Capabilities
- */
- if ((efrm - frm) < 12)
- return -EINVAL;
-
- memset(cie, 0, sizeof(*cie));
-
- cie->ie_tstamp = frm;
- frm += 8;
- cie->ie_beaconInt = *(u16 *) frm;
- frm += 2;
- cie->ie_capInfo = *(u16 *) frm;
- frm += 2;
- cie->ie_chan = 0;
-
- while (frm < efrm) {
- switch (*frm) {
- case WLAN_EID_SSID:
- if (!elemid_ssid) {
- cie->ie_ssid = frm;
- elemid_ssid = true;
- }
- break;
- case WLAN_EID_SUPP_RATES:
- cie->ie_rates = frm;
- break;
- case WLAN_EID_COUNTRY:
- cie->ie_country = frm;
- break;
- case WLAN_EID_FH_PARAMS:
- break;
- case WLAN_EID_DS_PARAMS:
- cie->ie_chan = frm[2];
- break;
- case WLAN_EID_TIM:
- cie->ie_tim = frm;
- break;
- case WLAN_EID_IBSS_PARAMS:
- break;
- case WLAN_EID_EXT_SUPP_RATES:
- cie->ie_xrates = frm;
- break;
- case WLAN_EID_ERP_INFO:
- if (frm[1] != 1)
- return -EINVAL;
-
- cie->ie_erp = frm[2];
- break;
- case WLAN_EID_RSN:
- cie->ie_rsn = frm;
- break;
- case WLAN_EID_HT_CAPABILITY:
- cie->ie_htcap = frm;
- break;
- case WLAN_EID_HT_INFORMATION:
- cie->ie_htop = frm;
- break;
- case WLAN_EID_VENDOR_SPECIFIC:
- if (frm[1] > 3 && frm[2] == 0x00 && frm[3] == 0x50 &&
- frm[4] == 0xf2) {
- /* OUT Type (00:50:F2) */
-
- if (frm[5] == WPA_OUI_TYPE) {
- /* WPA OUT */
- cie->ie_wpa = frm;
- } else if (frm[5] == WMM_OUI_TYPE) {
- /* WMM OUT */
- cie->ie_wmm = frm;
- } else if (frm[5] == WSC_OUT_TYPE) {
- /* WSC OUT */
- cie->ie_wsc = frm;
- }
-
- } else if (frm[1] > 3 && frm[2] == 0x00
- && frm[3] == 0x03 && frm[4] == 0x7f
- && frm[5] == ATH_OUI_TYPE) {
- /* Atheros OUI (00:03:7f) */
- cie->ie_ath = frm;
- }
- break;
- default:
- break;
- }
- frm += frm[1] + 2;
- }
-
- if ((cie->ie_rates == NULL)
- || (cie->ie_rates[1] > ATH6KL_RATE_MAXSIZE))
- return -EINVAL;
-
- if ((cie->ie_ssid == NULL)
- || (cie->ie_ssid[1] > IEEE80211_MAX_SSID_LEN))
- return -EINVAL;
-
- return 0;
-}
-
static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len)
{
- struct bss *bss = NULL;
struct wmi_bss_info_hdr *bih;
- u8 cached_ssid_len = 0;
- u8 cached_ssid[IEEE80211_MAX_SSID_LEN] = { 0 };
- u8 beacon_ssid_len = 0;
- u8 *buf, *ie_ssid;
- u8 *ni_buf;
- int buf_len;
-
- int ret;
+ u8 *buf;
+ struct ieee80211_channel *channel;
+ struct ath6kl *ar = wmi->parent_dev;
+ struct ieee80211_mgmt *mgmt;
+ struct cfg80211_bss *bss;
if (len <= sizeof(struct wmi_bss_info_hdr))
return -EINVAL;
bih = (struct wmi_bss_info_hdr *) datap;
- bss = wlan_find_node(&wmi->parent_dev->scan_table, bih->bssid);
-
- if (a_sle16_to_cpu(bih->rssi) > 0) {
- if (bss == NULL)
- return 0;
- else
- bih->rssi = a_cpu_to_sle16(bss->ni_rssi);
- }
-
buf = datap + sizeof(struct wmi_bss_info_hdr);
len -= sizeof(struct wmi_bss_info_hdr);
ath6kl_dbg(ATH6KL_DBG_WMI,
- "bss info evt - ch %u, rssi %02x, bssid \"%pM\"\n",
- bih->ch, a_sle16_to_cpu(bih->rssi), bih->bssid);
-
- if (bss != NULL) {
- /*
- * Free up the node. We are about to allocate a new node.
- * In case of hidden AP, beacon will not have ssid,
- * but a directed probe response will have it,
- * so cache the probe-resp-ssid if already present.
- */
- if (wmi->is_probe_ssid && (bih->frame_type == BEACON_FTYPE)) {
- ie_ssid = bss->ni_cie.ie_ssid;
- if (ie_ssid && (ie_ssid[1] <= IEEE80211_MAX_SSID_LEN) &&
- (ie_ssid[2] != 0)) {
- cached_ssid_len = ie_ssid[1];
- memcpy(cached_ssid, ie_ssid + 2,
- cached_ssid_len);
- }
- }
-
- /*
- * Use the current average rssi of associated AP base on
- * assumption
- * 1. Most os with GUI will update RSSI by
- * ath6kl_wmi_get_stats_cmd() periodically.
- * 2. ath6kl_wmi_get_stats_cmd(..) will be called when calling
- * ath6kl_wmi_startscan_cmd(...)
- * The average value of RSSI give end-user better feeling for
- * instance value of scan result. It also sync up RSSI info
- * in GUI between scan result and RSSI signal icon.
- */
- if (memcmp(wmi->parent_dev->bssid, bih->bssid, ETH_ALEN) == 0) {
- bih->rssi = a_cpu_to_sle16(bss->ni_rssi);
- bih->snr = bss->ni_snr;
- }
-
- wlan_node_reclaim(&wmi->parent_dev->scan_table, bss);
- }
-
- /*
- * beacon/probe response frame format
- * [8] time stamp
- * [2] beacon interval
- * [2] capability information
- * [tlv] ssid
- */
- beacon_ssid_len = buf[SSID_IE_LEN_INDEX];
-
- /*
- * If ssid is cached for this hidden AP, then change
- * buffer len accordingly.
- */
- if (wmi->is_probe_ssid && (bih->frame_type == BEACON_FTYPE) &&
- (cached_ssid_len != 0) &&
- (beacon_ssid_len == 0 || (cached_ssid_len > beacon_ssid_len &&
- buf[SSID_IE_LEN_INDEX + 1] == 0))) {
-
- len += (cached_ssid_len - beacon_ssid_len);
- }
+ "bss info evt - ch %u, snr %d, rssi %d, bssid \"%pM\" "
+ "frame_type=%d\n",
+ bih->ch, bih->snr, a_sle16_to_cpu(bih->rssi), bih->bssid,
+ bih->frame_type);
- bss = wlan_node_alloc(len);
- if (!bss)
- return -ENOMEM;
+ if (bih->frame_type != BEACON_FTYPE &&
+ bih->frame_type != PROBERESP_FTYPE)
+ return 0; /* Only update BSS table for now */
- bss->ni_snr = bih->snr;
- bss->ni_rssi = a_sle16_to_cpu(bih->rssi);
+ channel = ieee80211_get_channel(ar->wdev->wiphy, le16_to_cpu(bih->ch));
+ if (channel == NULL)
+ return -EINVAL;
- if (WARN_ON(!bss->ni_buf))
+ if (len < 8 + 2 + 2)
return -EINVAL;
/*
- * In case of hidden AP, beacon will not have ssid,
- * but a directed probe response will have it,
- * so place the cached-ssid(probe-resp) in the bss info.
+ * In theory, use of cfg80211_inform_bss() would be more natural here
+ * since we do not have the full frame. However, at least for now,
+ * cfg80211 can only distinguish Beacon and Probe Response frames from
+ * each other when using cfg80211_inform_bss_frame(), so let's build a
+ * fake IEEE 802.11 header to be able to take benefit of this.
*/
- if (wmi->is_probe_ssid && (bih->frame_type == BEACON_FTYPE) &&
- (cached_ssid_len != 0) &&
- (beacon_ssid_len == 0 || (beacon_ssid_len &&
- buf[SSID_IE_LEN_INDEX + 1] == 0))) {
- ni_buf = bss->ni_buf;
- buf_len = len;
-
- /*
- * Copy the first 14 bytes:
- * time-stamp(8), beacon-interval(2),
- * cap-info(2), ssid-id(1), ssid-len(1).
- */
- memcpy(ni_buf, buf, SSID_IE_LEN_INDEX + 1);
-
- ni_buf[SSID_IE_LEN_INDEX] = cached_ssid_len;
- ni_buf += (SSID_IE_LEN_INDEX + 1);
-
- buf += (SSID_IE_LEN_INDEX + 1);
- buf_len -= (SSID_IE_LEN_INDEX + 1);
-
- memcpy(ni_buf, cached_ssid, cached_ssid_len);
- ni_buf += cached_ssid_len;
-
- buf += beacon_ssid_len;
- buf_len -= beacon_ssid_len;
-
- if (cached_ssid_len > beacon_ssid_len)
- buf_len -= (cached_ssid_len - beacon_ssid_len);
-
- memcpy(ni_buf, buf, buf_len);
- } else
- memcpy(bss->ni_buf, buf, len);
+ mgmt = kmalloc(24 + len, GFP_ATOMIC);
+ if (mgmt == NULL)
+ return -EINVAL;
- bss->ni_framelen = len;
+ if (bih->frame_type == BEACON_FTYPE) {
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_BEACON);
+ memset(mgmt->da, 0xff, ETH_ALEN);
+ } else {
+ struct net_device *dev = ar->net_dev;
- ret = ath6kl_wlan_parse_beacon(bss->ni_buf, len, &bss->ni_cie);
- if (ret) {
- wlan_node_free(bss);
- return -EINVAL;
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_RESP);
+ memcpy(mgmt->da, dev->dev_addr, ETH_ALEN);
}
+ mgmt->duration = cpu_to_le16(0);
+ memcpy(mgmt->sa, bih->bssid, ETH_ALEN);
+ memcpy(mgmt->bssid, bih->bssid, ETH_ALEN);
+ mgmt->seq_ctrl = cpu_to_le16(0);
- /*
- * Update the frequency in ie_chan, overwriting of channel number
- * which is done in ath6kl_wlan_parse_beacon
- */
- bss->ni_cie.ie_chan = le16_to_cpu(bih->ch);
- wlan_setup_node(&wmi->parent_dev->scan_table, bss, bih->bssid);
+ memcpy(&mgmt->u.beacon, buf, len);
+
+ bss = cfg80211_inform_bss_frame(ar->wdev->wiphy, channel, mgmt,
+ 24 + len, bih->snr * 100, GFP_ATOMIC);
+ kfree(mgmt);
+ if (bss == NULL)
+ return -ENOMEM;
+ cfg80211_put_bss(bss);
return 0;
}
ev = (struct wmi_scan_complete_event *) datap;
- if (a_sle32_to_cpu(ev->status) == 0)
- wlan_refresh_inactive_nodes(wmi->parent_dev);
-
ath6kl_scan_complete_evt(wmi->parent_dev, a_sle32_to_cpu(ev->status));
wmi->is_probe_ssid = false;