+
+ if (fw.wlan.wol.wake_up) {
+ fw.suspend_mode = CARL9170_AWAKE_HOST;
+ set(AR9170_USB_REG_WAKE_UP, AR9170_USB_WAKE_UP_WAKE);
+ }
+ }
+}
+#endif /* CONFIG_CARL9170FW_WOL */
+
+static unsigned int wlan_rx_filter(struct dma_desc *desc)
+{
+ struct ieee80211_hdr *hdr;
+ unsigned int data_len;
+ unsigned int rx_filter;
+ unsigned int mac_err;
+
+ data_len = ar9170_get_rx_mpdu_len(desc);
+ mac_err = ar9170_get_rx_macstatus_error(desc);
+
+#define AR9170_RX_ERROR_BAD (AR9170_RX_ERROR_FCS | AR9170_RX_ERROR_PLCP)
+
+ if (unlikely(data_len < (4 + 6 + FCS_LEN) ||
+ desc->totalLen > CONFIG_CARL9170FW_RX_FRAME_LEN) ||
+ mac_err & AR9170_RX_ERROR_BAD) {
+ /*
+ * This frame is too damaged to do anything
+ * useful with it.
+ */
+
+ return CARL9170_RX_FILTER_BAD;
+ }
+
+ rx_filter = 0;
+ if (mac_err & AR9170_RX_ERROR_WRONG_RA)
+ rx_filter |= CARL9170_RX_FILTER_OTHER_RA;
+
+ if (mac_err & AR9170_RX_ERROR_DECRYPT)
+ rx_filter |= CARL9170_RX_FILTER_DECRY_FAIL;
+
+ hdr = ar9170_get_rx_i3e(desc);
+ if (likely(ieee80211_is_data(hdr->frame_control))) {
+ rx_filter |= CARL9170_RX_FILTER_DATA;
+ } else if (ieee80211_is_ctl(hdr->frame_control)) {
+ switch (le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_BACK_REQ:
+ handle_bar(desc, hdr, data_len, mac_err);
+ /* fallthrough */
+ rx_filter |= CARL9170_RX_FILTER_CTL_BACKR;
+ break;
+ case IEEE80211_STYPE_PSPOLL:
+ rx_filter |= CARL9170_RX_FILTER_CTL_PSPOLL;
+ break;
+ default:
+ rx_filter |= CARL9170_RX_FILTER_CTL_OTHER;
+ break;
+ }
+ } else {
+ /* ieee80211_is_mgmt */
+ rx_filter |= CARL9170_RX_FILTER_MGMT;
+ }
+
+#ifdef CONFIG_CARL9170FW_WOL
+ if (unlikely(fw.suspend_mode == CARL9170_HOST_SUSPENDED)) {
+ wlan_rx_wol(rx_filter, hdr, min(data_len,
+ (unsigned int)AR9170_BLOCK_SIZE));
+ }
+#endif /* CONFIG_CARL9170FW_WOL */
+
+#undef AR9170_RX_ERROR_BAD
+
+ return rx_filter;
+}
+
+static void handle_rx(void)
+{
+ struct dma_desc *desc;
+
+ for_each_desc_not_bits(desc, &fw.wlan.rx_queue, AR9170_OWN_BITS_HW) {
+ if (!(wlan_rx_filter(desc) & fw.wlan.rx_filter)) {
+ dma_put(&fw.pta.up_queue, desc);
+ up_trigger();
+ } else {
+ dma_reclaim(&fw.wlan.rx_queue, desc);
+ wlan_trigger(AR9170_DMA_TRIGGER_RXQ);
+ }
+ }
+}
+
+#ifdef CONFIG_CARL9170FW_CAB_QUEUE
+void wlan_cab_flush_queue(const unsigned int vif)
+{
+ struct dma_queue *cab_queue = &fw.wlan.cab_queue[vif];
+ struct dma_desc *desc;
+
+ /* move queued frames into the main tx queues */
+ for_each_desc(desc, cab_queue) {
+ struct carl9170_tx_superframe *super = get_super(desc);
+ if (!queue_empty(cab_queue)) {
+ /*
+ * Set MOREDATA flag for all,
+ * but the last queued frame.
+ * see: 802.11-2007 11.2.1.5 f)
+ *
+ * This is actually the reason to why
+ * we need to prevent the reentry.
+ */
+
+ super->f.data.i3e.frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ } else {
+ super->f.data.i3e.frame_control &=
+ cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
+ }
+
+ /* ready to roll! */
+ _wlan_tx(desc);
+ wlan_trigger(BIT(super->s.queue));
+ }
+}
+
+static uint8_t *beacon_find_ie(uint8_t ie, void *addr,
+ const unsigned int len)
+{
+ struct ieee80211_mgmt *mgmt = addr;
+ uint8_t *pos, *end;
+
+ pos = mgmt->u.beacon.variable;
+ end = (uint8_t *) ((unsigned long)mgmt + (len - FCS_LEN));
+ while (pos < end) {
+ if (pos + 2 + pos[1] > end)
+ return NULL;
+
+ if (pos[0] == ie)
+ return pos;
+
+ pos += pos[1] + 2;