X-Git-Url: https://jxself.org/git/?p=carl9170fw.git;a=blobdiff_plain;f=carlfw%2Fsrc%2Fwlan.c;h=6c73b4840f5649c7b0cbb6a1597f4df8257c3ce3;hp=1909de1db86fe7819c22055107619245e9ee10bf;hb=af915846d44d23adc02c7ded807282d607b46faa;hpb=03a8cf510dbef554b55403da7547607f7e326368 diff --git a/carlfw/src/wlan.c b/carlfw/src/wlan.c index 1909de1..6c73b48 100644 --- a/carlfw/src/wlan.c +++ b/carlfw/src/wlan.c @@ -31,7 +31,6 @@ #include "printf.h" #include "rf.h" #include "linux/ieee80211.h" -#include "rom.h" static void wlan_txunstuck(unsigned int queue) { @@ -441,6 +440,7 @@ static bool wlan_tx_status(struct dma_queue *queue, if (unlikely(super == fw.wlan.fw_desc_data)) { fw.wlan.fw_desc = desc; fw.wlan.fw_desc_available = 1; + if (fw.wlan.fw_desc_callback) fw.wlan.fw_desc_callback(super, success); @@ -503,7 +503,7 @@ void __hot wlan_tx(struct dma_desc *desc) wlan_trigger(BIT(super->s.queue)); } -static void wlan_tx_fw(struct carl9170_tx_superdesc *super) +static void wlan_tx_fw(struct carl9170_tx_superdesc *super, fw_desc_callback_t cb) { if (!fw.wlan.fw_desc_available) return; @@ -511,13 +511,14 @@ static void wlan_tx_fw(struct carl9170_tx_superdesc *super) fw.wlan.fw_desc_available = 0; /* Format BlockAck */ - fw.wlan.fw_desc->status = AR9170_OWN_BITS_SW; fw.wlan.fw_desc->ctrl = AR9170_CTRL_FS_BIT | AR9170_CTRL_LS_BIT; + fw.wlan.fw_desc->status = AR9170_OWN_BITS_SW; + fw.wlan.fw_desc->totalLen = fw.wlan.fw_desc->dataSize = super->len; fw.wlan.fw_desc_data = fw.wlan.fw_desc->dataAddr = super; fw.wlan.fw_desc->nextAddr = fw.wlan.fw_desc->lastAddr = fw.wlan.fw_desc; - + fw.wlan.fw_desc_callback = cb; wlan_tx(fw.wlan.fw_desc); } @@ -542,6 +543,7 @@ static void wlan_send_buffered_ba(void) sizeof(struct ar9170_tx_hwdesc) + sizeof(struct ieee80211_ba); baf->s.ri[0].tries = 1; + baf->s.cookie = 0; baf->s.queue = AR9170_TXQ_VO; baf->f.hdr.length = sizeof(struct ieee80211_ba) + FCS_LEN; @@ -554,7 +556,7 @@ static void wlan_send_buffered_ba(void) baf->f.hdr.phy.tx_power = 29; /* 14.5 dBm */ /* format outgoing BA */ - ba->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK); + ba->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_NULLFUNC); ba->duration = cpu_to_le16(0); memcpy(ba->ta, ctx->ta, 6); memcpy(ba->ra, ctx->ra, 6); @@ -572,7 +574,7 @@ static void wlan_send_buffered_ba(void) */ ba->control = ctx->control | cpu_to_le16(1); ba->start_seq_num = ctx->start_seq_num; - wlan_tx_fw(&baf->s); + wlan_tx_fw(&baf->s, NULL); } static struct carl9170_bar_ctx *wlan_get_bar_cache_buffer(void) @@ -655,8 +657,24 @@ static void wlan_check_rx_overrun(void) } #ifdef CONFIG_CARL9170FW_WOL +void wlan_prepare_wol(void) +{ + /* set MAC filter */ + memcpy((void *)AR9170_MAC_REG_MAC_ADDR_L, fw.wlan.wol.cmd.mac, 6); + memcpy((void *)AR9170_MAC_REG_BSSID_L, fw.wlan.wol.cmd.bssid, 6); + set(AR9170_MAC_REG_RX_CONTROL, AR9170_MAC_RX_CTRL_DEAGG); + + /* set filter policy to: discard everything */ + fw.wlan.rx_filter = CARL9170_RX_FILTER_EVERYTHING; -#ifdef CONFIG_CARL9170FW_WOL_MAGIC_PACKET + /* reenable rx dma */ + wlan_trigger(AR9170_DMA_TRIGGER_RXQ); + + /* initialize the last_beacon timer */ + fw.wlan.wol.last_null = fw.wlan.wol.last_beacon = get_clock_counter(); +} + +#ifdef CONFIG_CARL9170FW_WOL_NL80211_TRIGGERS static bool wlan_rx_wol_magic_packet(struct ieee80211_hdr *hdr, unsigned int len) { const unsigned char *data, *end, *mac; @@ -668,16 +686,7 @@ static bool wlan_rx_wol_magic_packet(struct ieee80211_hdr *hdr, unsigned int len * for MAGIC patterns! */ - /* - * TODO: - * Currently, the MAGIC MAC Address is fixed to the EEPROM default. - * It's possible to make it fully configurable, e.g: - * - * mac = (const unsigned char *) AR9170_MAC_REG_MAC_ADDR_L; - * But this will clash with the driver's suspend path, because it - * needs to reset the registers. - */ - mac = rom.sys.mac_address; + mac = (const unsigned char *) AR9170_MAC_REG_MAC_ADDR_L; data = (u8 *)((unsigned long)hdr + ieee80211_hdrlen(hdr->frame_control)); end = (u8 *)((unsigned long)hdr + len); @@ -730,7 +739,77 @@ static bool wlan_rx_wol_magic_packet(struct ieee80211_hdr *hdr, unsigned int len return false; } -#endif /* CONFIG_CARL9170FW_WOL_MAGIC_PACKET */ + +static void wlan_wol_connect_callback(void __unused *dummy, bool success) +{ + if (success) + fw.wlan.wol.lost_null = 0; + else + fw.wlan.wol.lost_null++; +} + +static void wlan_wol_connection_monitor(void) +{ + struct carl9170_tx_null_superframe *nullf = &dma_mem.reserved.cmd.null; + struct ieee80211_hdr *null = (struct ieee80211_hdr *) &nullf->f.null; + + if (!fw.wlan.fw_desc_available) + return; + + memset(nullf, 0, sizeof(nullf)); + + nullf->s.len = sizeof(struct carl9170_tx_superdesc) + + sizeof(struct ar9170_tx_hwdesc) + + sizeof(struct ieee80211_hdr); + nullf->s.ri[0].tries = 3; + nullf->s.assign_seq = true; + nullf->s.queue = AR9170_TXQ_VO; + nullf->f.hdr.length = sizeof(struct ieee80211_hdr) + FCS_LEN; + + nullf->f.hdr.mac.backoff = 1; + nullf->f.hdr.mac.hw_duration = 1; + nullf->f.hdr.mac.erp_prot = AR9170_TX_MAC_PROT_RTS; + + nullf->f.hdr.phy.modulation = AR9170_TX_PHY_MOD_OFDM; + nullf->f.hdr.phy.bandwidth = AR9170_TX_PHY_BW_20MHZ; + nullf->f.hdr.phy.chains = AR9170_TX_PHY_TXCHAIN_2; + nullf->f.hdr.phy.tx_power = 29; /* 14.5 dBm */ + nullf->f.hdr.phy.mcs = AR9170_TXRX_PHY_RATE_OFDM_6M; + + /* format outgoing nullfunc */ + null->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC | IEEE80211_FCTL_TODS); + + memcpy(null->addr1, fw.wlan.wol.cmd.bssid, 6); + memcpy(null->addr2, fw.wlan.wol.cmd.mac, 6); + memcpy(null->addr3, fw.wlan.wol.cmd.bssid, 6); + + wlan_tx_fw(&nullf->s, wlan_wol_connect_callback); +} + +static bool wlan_rx_wol_disconnect(const unsigned int rx_filter, + struct ieee80211_hdr *hdr, + unsigned int __unused len) +{ + const unsigned char *bssid; + bssid = (const unsigned char *) AR9170_MAC_REG_BSSID_L; + + /* should catch both broadcast and unicast MLMEs */ + if (!(rx_filter & CARL9170_RX_FILTER_OTHER_RA)) { + if (ieee80211_is_deauth(hdr->frame_control) || + ieee80211_is_disassoc(hdr->frame_control)) + return true; + } + + if (ieee80211_is_beacon(hdr->frame_control) && + compare_ether_address(hdr->addr3, bssid)) { + fw.wlan.wol.last_beacon = get_clock_counter(); + } + + return false; +} + +#endif /* CARL9170FW_WOL_NL80211_TRIGGERS */ #ifdef CONFIG_CARL9170FW_WOL_PROBE_REQUEST @@ -781,21 +860,46 @@ static bool wlan_rx_wol_probe_ssid(struct ieee80211_hdr *hdr, unsigned int len) static void wlan_rx_wol(unsigned int rx_filter __unused, struct ieee80211_hdr *hdr __unused, unsigned int len __unused) { - bool __unused wake_up = false; +#ifdef CONFIG_CARL9170FW_WOL_NL80211_TRIGGERS + /* Disconnect is always enabled */ + if (fw.wlan.wol.cmd.flags & CARL9170_WOL_DISCONNECT && + rx_filter & CARL9170_RX_FILTER_MGMT) + fw.wlan.wol.wake_up |= wlan_rx_wol_disconnect(rx_filter, hdr, len); -#ifdef CONFIG_CARL9170FW_WOL_MAGIC_PACKET - if (rx_filter & CARL9170_RX_FILTER_DATA) - wake_up |= wlan_rx_wol_magic_packet(hdr, len); -#endif /* CONFIG_CARL9170FW_WOL_MAGIC_PACKET */ + if (fw.wlan.wol.cmd.flags & CARL9170_WOL_MAGIC_PKT && + rx_filter & CARL9170_RX_FILTER_DATA) + fw.wlan.wol.wake_up |= wlan_rx_wol_magic_packet(hdr, len); +#endif /* CONFIG_CARL9170FW_WOL_NL80211_TRIGGERS */ #ifdef CONFIG_CARL9170FW_WOL_PROBE_REQUEST if (rx_filter & CARL9170_RX_FILTER_MGMT) - wake_up |= wlan_rx_wol_probe_ssid(hdr, len); + fw.wlan.wol.wake_up |= wlan_rx_wol_probe_ssid(hdr, len); #endif /* CONFIG_CARL9170FW_WOL_PROBE_REQUEST */ +} + +static void wlan_wol_janitor(void) +{ + if (unlikely(fw.suspend_mode == CARL9170_HOST_SUSPENDED)) { + if (fw.wlan.wol.cmd.flags & CARL9170_WOL_DISCONNECT) { + /* + * connection lost after 10sec without receiving + * a beacon + */ + if (is_after_msecs(fw.wlan.wol.last_beacon, 10000)) + fw.wlan.wol.wake_up |= true; + + if (fw.wlan.wol.cmd.null_interval && + is_after_msecs(fw.wlan.wol.last_null, fw.wlan.wol.cmd.null_interval)) + wlan_wol_connection_monitor(); + + if (fw.wlan.wol.lost_null >= 5) + fw.wlan.wol.wake_up |= true; + } - if (wake_up) { - fw.suspend_mode = CARL9170_AWAKE_HOST; - set(AR9170_USB_REG_WAKE_UP, AR9170_USB_WAKE_UP_WAKE); + 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 */ @@ -855,7 +959,7 @@ static unsigned int wlan_rx_filter(struct dma_desc *desc) #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)); + (unsigned int)AR9170_BLOCK_SIZE)); } #endif /* CONFIG_CARL9170FW_WOL */ @@ -967,6 +1071,38 @@ void wlan_modify_beacon(const unsigned int vif, wlan_assign_seq((struct ieee80211_hdr *)addr, vif); } + +static void wlan_send_buffered_cab(void) +{ + unsigned int i; + + for (i = 0; i < CARL9170_INTF_NUM; i++) { + if (unlikely(fw.wlan.cab_flush_trigger[i] == CARL9170_CAB_TRIGGER_ARMED)) { + /* + * This is hardcoded into carl9170usb driver. + * + * The driver must set the PRETBTT event to beacon_interval - + * CARL9170_PRETBTT_KUS (usually 6) Kus. + * + * But still, we can only do so much about 802.11-2007 9.3.2.1 & + * 11.2.1.6. Let's hope the current solution is adequate enough. + */ + + if (is_after_msecs(fw.wlan.cab_flush_time, (CARL9170_TBTT_DELTA))) { + wlan_cab_flush_queue(i); + + /* + * This prevents the code from sending new BC/MC frames + * which were queued after the previous buffered traffic + * has been sent out... They will have to wait until the + * next DTIM beacon comes along. + */ + fw.wlan.cab_flush_trigger[i] = CARL9170_CAB_TRIGGER_DEFER; + } + } + + } +} #endif /* CONFIG_CARL9170FW_CAB_QUEUE */ static void handle_beacon_config(void) @@ -1014,39 +1150,16 @@ static void handle_radar(void) static void wlan_janitor(void) { #ifdef CONFIG_CARL9170FW_CAB_QUEUE - unsigned int i; - - for (i = 0; i < CARL9170_INTF_NUM; i++) { - if (unlikely(fw.wlan.cab_flush_trigger[i] == CARL9170_CAB_TRIGGER_ARMED)) { - /* - * This is hardcoded into carl9170usb driver. - * - * The driver must set the PRETBTT event to beacon_interval - - * CARL9170_PRETBTT_KUS (usually 6) Kus. - * - * But still, we can only do so much about 802.11-2007 9.3.2.1 & - * 11.2.1.6. Let's hope the current solution is adequate enough. - */ - - if (is_after_msecs(fw.wlan.cab_flush_time, (CARL9170_TBTT_DELTA))) { - wlan_cab_flush_queue(i); - - /* - * This prevents the code from sending new BC/MC frames - * which were queued after the previous buffered traffic - * has been sent out... They will have to wait until the - * next DTIM beacon comes along. - */ - fw.wlan.cab_flush_trigger[i] = CARL9170_CAB_TRIGGER_DEFER; - } - } - - } + wlan_send_buffered_cab(); #endif /* CONFIG_CARL9170FW_CAB_QUEUE */ wlan_send_buffered_tx_status(); wlan_send_buffered_ba(); + +#ifdef CONFIG_CARL9170FW_WOL + wlan_wol_janitor(); +#endif /* CONFIG_CARL9170FW_WOL */ } void handle_wlan(void)