u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len, enum nl80211_iftype type) { __le16 fc = hdr->frame_control; /* drop ACK/CTS frames and incorrect hdr len (ctrl) */ if (len < 16) return NULL; if (ieee80211_is_data(fc)) { if (len < 24) /* drop incorrect hdr len (data) */ return NULL; if (ieee80211_has_a4(fc)) return NULL; if (ieee80211_has_tods(fc)) return hdr->addr1; if (ieee80211_has_fromds(fc)) return hdr->addr2; return hdr->addr3; } if (ieee80211_is_mgmt(fc)) { if (len < 24) /* drop incorrect hdr len (mgmt) */ return NULL; return hdr->addr3; } if (ieee80211_is_ctl(fc)) { if(ieee80211_is_pspoll(fc)) return hdr->addr1; if (ieee80211_is_back_req(fc)) { switch (type) { case NL80211_IFTYPE_STATION: return hdr->addr2; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: return hdr->addr1; default: break; /* fall through to the return */ } } } return NULL; }
static bool rtl_phydm_query_phy_status(struct rtl_priv *rtlpriv, u8 *phystrpt, struct ieee80211_hdr *hdr, struct rtl_stats *pstatus) { /* NOTE: phystrpt may be NULL, and need to fill default value */ struct phy_dm_struct *dm = rtlpriv_to_phydm(rtlpriv); struct rtl_efuse *rtlefuse = rtl_efuse(rtlpriv); struct rtl_mac *mac = rtl_mac(rtlpriv); struct dm_per_pkt_info pktinfo; /* input of pydm */ struct dm_phy_status_info phy_info; /* output of phydm */ __le16 fc = hdr->frame_control; /* fill driver pstatus */ ether_addr_copy(pstatus->psaddr, ieee80211_get_SA(hdr)); /* fill pktinfo */ memset(&pktinfo, 0, sizeof(pktinfo)); pktinfo.data_rate = pstatus->rate; if (rtlpriv->mac80211.opmode == NL80211_IFTYPE_STATION) { pktinfo.station_id = 0; } else { /* TODO: use rtl_find_sta() to find ID */ pktinfo.station_id = 0xFF; } pktinfo.is_packet_match_bssid = (!ieee80211_is_ctl(fc) && (ether_addr_equal(mac->bssid, ieee80211_has_tods(fc) ? hdr->addr1 : ieee80211_has_fromds(fc) ? hdr->addr2 : hdr->addr3)) && (!pstatus->hwerror) && (!pstatus->crc) && (!pstatus->icv)); pktinfo.is_packet_to_self = pktinfo.is_packet_match_bssid && (ether_addr_equal(hdr->addr1, rtlefuse->dev_addr)); pktinfo.is_to_self = (!pstatus->icv) && (!pstatus->crc) && (ether_addr_equal(hdr->addr1, rtlefuse->dev_addr)); pktinfo.is_packet_beacon = (ieee80211_is_beacon(fc) ? true : false); /* query phy status */ if (phystrpt) odm_phy_status_query(dm, &phy_info, phystrpt, &pktinfo); else memset(&phy_info, 0, sizeof(phy_info)); /* copy phy_info from phydm to driver */ pstatus->rx_pwdb_all = phy_info.rx_pwdb_all; pstatus->bt_rx_rssi_percentage = phy_info.bt_rx_rssi_percentage; pstatus->recvsignalpower = phy_info.recv_signal_power; pstatus->signalquality = phy_info.signal_quality; pstatus->rx_mimo_signalquality[0] = phy_info.rx_mimo_signal_quality[0]; pstatus->rx_mimo_signalquality[1] = phy_info.rx_mimo_signal_quality[1]; pstatus->rx_packet_bw = phy_info.band_width; /* HT_CHANNEL_WIDTH_20 <- ODM_BW20M */ /* fill driver pstatus */ pstatus->packet_matchbssid = pktinfo.is_packet_match_bssid; pstatus->packet_toself = pktinfo.is_packet_to_self; pstatus->packet_beacon = pktinfo.is_packet_beacon; return true; }
void mwl_rx_recv(unsigned long data) { struct ieee80211_hw *hw = (struct ieee80211_hw *)data; struct mwl_priv *priv; struct mwl_rx_desc *curr_desc; int work_done = 0; struct sk_buff *prx_skb = NULL; int pkt_len; struct ieee80211_rx_status status; struct mwl_vif *mwl_vif = NULL; struct ieee80211_hdr *wh; u32 status_mask; WLDBG_ENTER(DBG_LEVEL_4); BUG_ON(!hw); priv = hw->priv; BUG_ON(!priv); curr_desc = priv->desc_data[0].pnext_rx_desc; if (curr_desc == NULL) { status_mask = readl(priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK); writel(status_mask | MACREG_A2HRIC_BIT_RX_RDY, priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK); priv->is_rx_schedule = false; WLDBG_EXIT_INFO(DBG_LEVEL_4, "busy or no receiving packets"); return; } while ((curr_desc->rx_control == EAGLE_RXD_CTRL_DMA_OWN) && (work_done < priv->recv_limit)) { prx_skb = curr_desc->psk_buff; if (prx_skb == NULL) goto out; pci_unmap_single(priv->pdev, ENDIAN_SWAP32(curr_desc->pphys_buff_data), priv->desc_data[0].rx_buf_size, PCI_DMA_FROMDEVICE); pkt_len = curr_desc->pkt_len; if (skb_tailroom(prx_skb) < pkt_len) { WLDBG_PRINT("Critical error: not enough tail room =%x pkt_len=%x, curr_desc=%x, curr_desc_data=%x", skb_tailroom(prx_skb), pkt_len, curr_desc, curr_desc->pbuff_data); dev_kfree_skb_any(prx_skb); goto out; } if (curr_desc->channel != hw->conf.chandef.chan->hw_value) { dev_kfree_skb_any(prx_skb); goto out; } mwl_rx_prepare_status(curr_desc, &status); priv->noise = -curr_desc->noise_floor; wh = &((struct mwl_dma_data *)prx_skb->data)->wh; if (ieee80211_has_protected(wh->frame_control)) { /* Check if hw crypto has been enabled for * this bss. If yes, set the status flags * accordingly */ if (ieee80211_has_tods(wh->frame_control)) mwl_vif = mwl_rx_find_vif_bss(&priv->vif_list, wh->addr1); else mwl_vif = mwl_rx_find_vif_bss(&priv->vif_list, wh->addr2); if (mwl_vif != NULL && mwl_vif->is_hw_crypto_enabled) { /* * When MMIC ERROR is encountered * by the firmware, payload is * dropped and only 32 bytes of * mwl8k Firmware header is sent * to the host. * * We need to add four bytes of * key information. In it * MAC80211 expects keyidx set to * 0 for triggering Counter * Measure of MMIC failure. */ if (status.flag & RX_FLAG_MMIC_ERROR) { struct mwl_dma_data *tr; tr = (struct mwl_dma_data *)prx_skb->data; memset((void *)&(tr->data), 0, 4); pkt_len += 4; } if (!ieee80211_is_auth(wh->frame_control)) status.flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED; } } skb_put(prx_skb, pkt_len); mwl_rx_remove_dma_header(prx_skb, curr_desc->qos_ctrl); memcpy(IEEE80211_SKB_RXCB(prx_skb), &status, sizeof(status)); ieee80211_rx(hw, prx_skb); out: mwl_rx_refill(priv, curr_desc); curr_desc->rx_control = EAGLE_RXD_CTRL_DRIVER_OWN; curr_desc->qos_ctrl = 0; curr_desc = curr_desc->pnext; work_done++; } priv->desc_data[0].pnext_rx_desc = curr_desc; status_mask = readl(priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK); writel(status_mask | MACREG_A2HRIC_BIT_RX_RDY, priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK); priv->is_rx_schedule = false; WLDBG_EXIT(DBG_LEVEL_4); }