carl9170 firmware: initial WoWLAN support
authorChristian Lamparter <chunkeey@googlemail.com>
Fri, 13 May 2011 23:42:39 +0000 (01:42 +0200)
committerChristian Lamparter <chunkeey@googlemail.com>
Fri, 13 May 2011 23:42:39 +0000 (01:42 +0200)
Unlike the previous proof-of-concept implementation, this
one supports nl80211's magic packet & disconnect wakeup
triggers. Furthermore, the trigger can be enabled from
userspace with "iw", so there's no need for a custom
firmware anymore.

Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
16 files changed:
carlfw/Kconfig
carlfw/include/carl9170.h
carlfw/include/cmd.h
carlfw/include/dma.h
carlfw/include/fwdsc.h
carlfw/include/wl.h
carlfw/src/cmd.c
carlfw/src/dma.c
carlfw/src/fw.c
carlfw/src/wlan.c
carlfw/usb/main.c
include/shared/fwcmd.h
include/shared/fwdesc.h
include/shared/version.h
toolchain/Makefile
tools/src/fwinfo.c

index cd236bafaea80113dc6a9895a8f293b1244f706a..a122f804237d273b9627d2aaa063a82138be5e12 100644 (file)
@@ -113,13 +113,15 @@ config CARL9170FW_WOL
        def_bool n
        depends on CARL9170FW_WOL_OPTION
 
        def_bool n
        depends on CARL9170FW_WOL_OPTION
 
-config CARL9170FW_WOL_MAGIC_PACKET
+config CARL9170FW_WOL_NL80211_TRIGGERS
        def_bool n
        def_bool n
-       prompt "Magic Packet(tm)"
+       prompt "Standard NL80211 wakeup triggers"
        depends on CARL9170FW_WOL_OPTION
        select CARL9170FW_WOL
        ---help---
        depends on CARL9170FW_WOL_OPTION
        select CARL9170FW_WOL
        ---help---
-        Sniff all incoming data frames for the magic packet pattern.
+        Available triggers:
+         * Magic Packet(tm) pattern
+         * disconnect event
 
 config CARL9170FW_WOL_PROBE_REQUEST
        def_bool n
 
 config CARL9170FW_WOL_PROBE_REQUEST
        def_bool n
index b4a7fe72f2a4a453b7fdb3a246290c4852ced8fc..4b0738d651a3fd0bdc93552284c3ed4e63652b85 100644 (file)
@@ -141,6 +141,16 @@ struct firmware_context_struct {
                struct carl9170_bar_ctx ba_cache[CONFIG_CARL9170FW_BACK_REQS_NUM];
                unsigned int ba_tail_idx,
                             ba_head_idx;
                struct carl9170_bar_ctx ba_cache[CONFIG_CARL9170FW_BACK_REQS_NUM];
                unsigned int ba_tail_idx,
                             ba_head_idx;
+
+#ifdef CONFIG_CARL9170FW_WOL
+               struct {
+                       struct carl9170_wol_cmd cmd;
+                       unsigned int last_beacon;
+                       unsigned int lost_null;
+                       unsigned int last_null;
+                       bool wake_up;
+               } wol;
+#endif /* CONFIG_CARL9170FW_WOL */
        } wlan;
 
        struct {
        } wlan;
 
        struct {
index a400cfa4389f64b14757368f8d131c4277c96b25..54dd35046bb1e9ea75237cac08963a0ed759d24f 100644 (file)
@@ -47,6 +47,7 @@ static inline void __check(void)
        BUILD_BUG_ON(sizeof(struct _carl9170_tx_status) != CARL9170_TX_STATUS_SIZE);
        BUILD_BUG_ON(sizeof(struct carl9170_gpio) != CARL9170_GPIO_SIZE);
        BUILD_BUG_ON(sizeof(struct carl9170_rx_filter_cmd) != CARL9170_RX_FILTER_CMD_SIZE);
        BUILD_BUG_ON(sizeof(struct _carl9170_tx_status) != CARL9170_TX_STATUS_SIZE);
        BUILD_BUG_ON(sizeof(struct carl9170_gpio) != CARL9170_GPIO_SIZE);
        BUILD_BUG_ON(sizeof(struct carl9170_rx_filter_cmd) != CARL9170_RX_FILTER_CMD_SIZE);
+       BUILD_BUG_ON(sizeof(struct carl9170_wol_cmd) != CARL9170_WOL_CMD_SIZE);
 }
 
 void handle_cmd(struct carl9170_rsp *resp);
 }
 
 void handle_cmd(struct carl9170_rsp *resp);
index 07d06d607f9bc2fea7429fede6e6799ac94fdd50..51d0db4442aef7c3765a9083cc6f344dee345004 100644 (file)
@@ -75,6 +75,16 @@ struct carl9170_tx_ba_superframe {
        struct ar9170_tx_ba_frame f;
 } __packed;
 
        struct ar9170_tx_ba_frame f;
 } __packed;
 
+struct ar9170_tx_null_frame {
+       struct ar9170_tx_hwdesc hdr;
+       struct ieee80211_hdr null;
+} __packed;
+
+struct carl9170_tx_null_superframe {
+       struct carl9170_tx_superdesc s;
+       struct ar9170_tx_null_frame f;
+} __packed;
+
 #define CARL9170_BA_BUFFER_LEN (__roundup(sizeof(struct carl9170_tx_ba_superframe), 16))
 #define CARL9170_RSP_BUFFER_LEN        AR9170_BLOCK_SIZE
 
 #define CARL9170_BA_BUFFER_LEN (__roundup(sizeof(struct carl9170_tx_ba_superframe), 16))
 #define CARL9170_RSP_BUFFER_LEN        AR9170_BLOCK_SIZE
 
@@ -87,6 +97,10 @@ struct carl9170_sram_reserved {
        union {
                uint32_t buf[CARL9170_MAX_CMD_LEN / sizeof(uint32_t)];
                struct carl9170_cmd cmd;
        union {
                uint32_t buf[CARL9170_MAX_CMD_LEN / sizeof(uint32_t)];
                struct carl9170_cmd cmd;
+
+#ifdef CONFIG_CARL9170FW_WOL
+               struct carl9170_tx_null_superframe null;
+#endif /* CONFIG_CARL9170FW_WOL */
        } cmd;
 
        union {
        } cmd;
 
        union {
@@ -123,6 +137,7 @@ struct carl9170_sram_reserved {
  *                             | BA buffer (128 bytes)
  *                             +--
  *                             | CMD buffer (128 bytes)
  *                             | BA buffer (128 bytes)
  *                             +--
  *                             | CMD buffer (128 bytes)
+ *                             | - used as NULLFRAME buffer (128 bytes) for WOL
  *                             +--
  *                             | RSP buffer (320 bytes)
  *                             +--
  *                             +--
  *                             | RSP buffer (320 bytes)
  *                             +--
@@ -328,6 +343,7 @@ static inline void __check_desc(void)
        BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, cmd.buf) & (BLOCK_ALIGNMENT - 1));
        BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, rsp.buf) & (BLOCK_ALIGNMENT - 1));
        BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, bcn.buf) & (BLOCK_ALIGNMENT - 1));
        BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, cmd.buf) & (BLOCK_ALIGNMENT - 1));
        BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, rsp.buf) & (BLOCK_ALIGNMENT - 1));
        BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, bcn.buf) & (BLOCK_ALIGNMENT - 1));
+       BUILD_BUG_ON(sizeof(struct carl9170_tx_null_superframe) > CARL9170_MAX_CMD_LEN);
 }
 
 #endif /* __CARL9170FW_DMA_H */
 }
 
 #endif /* __CARL9170FW_DMA_H */
index b521c7d79d1c5d8d783de98d968b75a60615b2b9..936bfed03c8b50b162a3b673a7c6c1f3301eacb1 100644 (file)
@@ -31,6 +31,9 @@
 struct carl9170_firmware_descriptor {
        struct carl9170fw_otus_desc otus;
        struct carl9170fw_txsq_desc txsq;
 struct carl9170_firmware_descriptor {
        struct carl9170fw_otus_desc otus;
        struct carl9170fw_txsq_desc txsq;
+#ifdef CONFIG_CARL9170FW_WOL
+       struct carl9170fw_wol_desc  wol;
+#endif /* CONFIG_CARL9170FW_WOL */
        struct carl9170fw_motd_desc motd;
        struct carl9170fw_dbg_desc  dbg;
        struct carl9170fw_last_desc last;
        struct carl9170fw_motd_desc motd;
        struct carl9170fw_dbg_desc  dbg;
        struct carl9170fw_last_desc last;
index e1c8bbd014ab17407847c1df3902e43aaa262787..7152de0eff8515ab29567bb89cd3e623f116d4d2 100644 (file)
@@ -266,14 +266,7 @@ void wlan_modify_beacon(const unsigned int vif,
 void wlan_tx_complete(struct carl9170_tx_superframe *super,
                       bool txs);
 
 void wlan_tx_complete(struct carl9170_tx_superframe *super,
                       bool txs);
 
-static inline void wlan_prepare_wol(void)
-{
-       /* set filter policy to: discard everything */
-       fw.wlan.rx_filter = CARL9170_RX_FILTER_EVERYTHING;
-
-       /* reenable rx dma */
-       wlan_trigger(AR9170_DMA_TRIGGER_RXQ);
-}
+void wlan_prepare_wol(void);
 
 static inline void __check_wlantx(void)
 {
 
 static inline void __check_wlantx(void)
 {
index 2bbfcffaa71f1cacbdda4b33ddcee3e60eef8c72..13081c24cf37c7de245859a78ece1026ffafcbdf 100644 (file)
@@ -86,6 +86,12 @@ void handle_cmd(struct carl9170_rsp *resp)
                fw.wlan.rx_filter = cmd->rx_filter.rx_filter;
                break;
 
                fw.wlan.rx_filter = cmd->rx_filter.rx_filter;
                break;
 
+#ifdef CONFIG_CARL9170FW_WOL
+       case CARL9170_CMD_WOL:
+               memcpy(&fw.wlan.wol.cmd, &cmd->wol, sizeof(cmd->wol));
+               break;
+#endif /* CONFIG_CARL9170FW_WOL */
+
 #ifdef CONFIG_CARL9170FW_CAB_QUEUE
        case CARL9170_CMD_BCN_CTRL:
                resp->hdr.len = 0;
 #ifdef CONFIG_CARL9170FW_CAB_QUEUE
        case CARL9170_CMD_BCN_CTRL:
                resp->hdr.len = 0;
index 8630f98afba6809a94e40eaac4587fc8dd32b4d8..c33608cc4814e251fa7a1bcf3b22a14fb0df6dd6 100644 (file)
@@ -120,7 +120,6 @@ void dma_init_descriptors(void)
                set_wlan_txq_dma_addr(i, (uint32_t) fw.wlan.tx_queue[i].head);
 
        set(AR9170_MAC_REG_DMA_RXQ_ADDR, (uint32_t) fw.wlan.rx_queue.head);
                set_wlan_txq_dma_addr(i, (uint32_t) fw.wlan.tx_queue[i].head);
 
        set(AR9170_MAC_REG_DMA_RXQ_ADDR, (uint32_t) fw.wlan.rx_queue.head);
-
        fw.usb.int_desc->dataSize = AR9170_BLOCK_SIZE;
        fw.usb.int_desc->dataAddr = (void *) &dma_mem.reserved.rsp;
 
        fw.usb.int_desc->dataSize = AR9170_BLOCK_SIZE;
        fw.usb.int_desc->dataAddr = (void *) &dma_mem.reserved.rsp;
 
index 94c52d1207979ee2b2c83a34781d82b7e0e8af3e..b621813c1e1be339c4ee275dc652bd39e083ddc6 100644 (file)
@@ -83,6 +83,14 @@ const struct carl9170_firmware_descriptor __section(fwdsc) carl9170fw_desc = {
             .seq_table_addr = cpu_to_le32(&fw.wlan.sequence),
        ),
 
             .seq_table_addr = cpu_to_le32(&fw.wlan.sequence),
        ),
 
+#ifdef CONFIG_CARL9170FW_WOL
+       FILL(wol, WOL,
+            .supported_triggers = BIT(CARL9170_WOL_DISCONNECT) |
+                                  BIT(CARL9170_WOL_MAGIC_PKT),
+       ),
+#endif /* CONFIG_CARL9170FW_WOL */
+
+
        FILL(motd, MOTD,
             .fw_year_month_day = cpu_to_le32(
                        CARL9170FW_SET_DAY(CARL9170FW_VERSION_DAY) +
        FILL(motd, MOTD,
             .fw_year_month_day = cpu_to_le32(
                        CARL9170FW_SET_DAY(CARL9170FW_VERSION_DAY) +
index 942df83148fa61ff4e24f34d49c0f6e97603be4a..6c73b4840f5649c7b0cbb6a1597f4df8257c3ce3 100644 (file)
@@ -31,7 +31,6 @@
 #include "printf.h"
 #include "rf.h"
 #include "linux/ieee80211.h"
 #include "printf.h"
 #include "rf.h"
 #include "linux/ieee80211.h"
-#include "rom.h"
 
 static void wlan_txunstuck(unsigned int queue)
 {
 
 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 (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);
 
                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));
 }
 
        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;
 {
        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_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->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->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);
 }
 
        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;
                     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;
 
        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 */
        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);
        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;
         */
        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)
 }
 
 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
 }
 
 #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);
 
 
-#ifdef CONFIG_CARL9170FW_WOL_MAGIC_PACKET
+       /* set filter policy to: discard everything */
+       fw.wlan.rx_filter = CARL9170_RX_FILTER_EVERYTHING;
+
+       /* 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;
 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!
         */
 
         * 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);
 
        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;
 }
 
        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
 
 
 #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)
 {
 
 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)
 
 #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 */
 #endif /* CONFIG_CARL9170FW_WOL_PROBE_REQUEST */
+}
 
 
-       if (wake_up) {
-               fw.suspend_mode = CARL9170_AWAKE_HOST;
-               set(AR9170_USB_REG_WAKE_UP, AR9170_USB_WAKE_UP_WAKE);
+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 (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 */
        }
 }
 #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,
 #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 */
 
        }
 #endif /* CONFIG_CARL9170FW_WOL */
 
@@ -1052,6 +1156,10 @@ static void wlan_janitor(void)
        wlan_send_buffered_tx_status();
 
        wlan_send_buffered_ba();
        wlan_send_buffered_tx_status();
 
        wlan_send_buffered_ba();
+
+#ifdef CONFIG_CARL9170FW_WOL
+       wlan_wol_janitor();
+#endif /* CONFIG_CARL9170FW_WOL */
 }
 
 void handle_wlan(void)
 }
 
 void handle_wlan(void)
index 5c09476b29a5592b63906c21067d70ce25969a41..649dc4bd9f797ce8d92671aac5b763ff816ebb14 100644 (file)
@@ -384,7 +384,9 @@ static void usb_handler(uint8_t usb_interrupt_level1)
 
                        fw.suspend_mode = CARL9170_HOST_SUSPENDED;
 
 
                        fw.suspend_mode = CARL9170_HOST_SUSPENDED;
 
-                       if (!(fw.usb.device_feature & USB_DEVICE_REMOTE_WAKEUP)) {
+#ifdef CONFIG_CARL9170FW_WOL
+                       if (!(fw.usb.device_feature & USB_DEVICE_REMOTE_WAKEUP) ||
+                           !fw.wlan.wol.cmd.flags) {
                                disable_watchdog();
 
                                /* GO_TO_SUSPEND stops the CPU clock too. */
                                disable_watchdog();
 
                                /* GO_TO_SUSPEND stops the CPU clock too. */
@@ -392,6 +394,12 @@ static void usb_handler(uint8_t usb_interrupt_level1)
                        } else {
                                wlan_prepare_wol();
                        }
                        } else {
                                wlan_prepare_wol();
                        }
+#else /* CONFIG_CARL9170FW_WOL */
+                       disable_watchdog();
+
+                       /* GO_TO_SUSPEND stops the CPU clock too. */
+                       orb(AR9170_USB_REG_MAIN_CTRL, AR9170_USB_MAIN_CTRL_GO_TO_SUSPEND);
+#endif /* CONFIG_CARL9170FW_WOL */
                }
 
                if (usb_interrupt_level2 & AR9170_USB_INTR_SRC7_USB_RESUME) {
                }
 
                if (usb_interrupt_level2 & AR9170_USB_INTR_SRC7_USB_RESUME) {
index 0e665198752fb5c63f93789072f438e9343df747..8fea62901b2bcfae0405dbfe9739c503ed4af522 100644 (file)
@@ -54,6 +54,7 @@ enum carl9170_cmd_oids {
        CARL9170_CMD_BCN_CTRL           = 0x05,
        CARL9170_CMD_READ_TSF           = 0x06,
        CARL9170_CMD_RX_FILTER          = 0x07,
        CARL9170_CMD_BCN_CTRL           = 0x05,
        CARL9170_CMD_READ_TSF           = 0x06,
        CARL9170_CMD_RX_FILTER          = 0x07,
+       CARL9170_CMD_WOL                = 0x08,
 
        /* CAM */
        CARL9170_CMD_EKEY               = 0x10,
 
        /* CAM */
        CARL9170_CMD_EKEY               = 0x10,
@@ -180,6 +181,21 @@ struct carl9170_bcn_ctrl_cmd {
 #define CARL9170_BCN_CTRL_DRAIN        0
 #define CARL9170_BCN_CTRL_CAB_TRIGGER  1
 
 #define CARL9170_BCN_CTRL_DRAIN        0
 #define CARL9170_BCN_CTRL_CAB_TRIGGER  1
 
+struct carl9170_wol_cmd {
+       __le32          flags;
+       u8              mac[6];
+       u8              bssid[6];
+       __le32          null_interval;
+       __le32          free_for_use2;
+       __le32          mask;
+       u8              pattern[32];
+} __packed;
+
+#define CARL9170_WOL_CMD_SIZE          60
+
+#define CARL9170_WOL_DISCONNECT                1
+#define CARL9170_WOL_MAGIC_PKT         2
+
 struct carl9170_cmd_head {
        union {
                struct {
 struct carl9170_cmd_head {
        union {
                struct {
@@ -203,6 +219,7 @@ struct carl9170_cmd {
                struct carl9170_write_reg       wreg;
                struct carl9170_rf_init         rf_init;
                struct carl9170_psm             psm;
                struct carl9170_write_reg       wreg;
                struct carl9170_rf_init         rf_init;
                struct carl9170_psm             psm;
+               struct carl9170_wol_cmd         wol;
                struct carl9170_bcn_ctrl_cmd    bcn_ctrl;
                struct carl9170_rx_filter_cmd   rx_filter;
                u8 data[CARL9170_MAX_CMD_PAYLOAD_LEN];
                struct carl9170_bcn_ctrl_cmd    bcn_ctrl;
                struct carl9170_rx_filter_cmd   rx_filter;
                u8 data[CARL9170_MAX_CMD_PAYLOAD_LEN];
index 9cfe36680097c7f773a344514494cddb7987a34a..a4769e9cee7c486e0840b59380a37515cf6e81fe 100644 (file)
@@ -82,6 +82,7 @@ enum carl9170fw_feature_list {
 #define DBG_MAGIC      "DBG\0"
 #define CHK_MAGIC      "CHK\0"
 #define TXSQ_MAGIC     "TXSQ"
 #define DBG_MAGIC      "DBG\0"
 #define CHK_MAGIC      "CHK\0"
 #define TXSQ_MAGIC     "TXSQ"
+#define WOL_MAGIC      "WOL\0"
 #define LAST_MAGIC     "LAST"
 
 #define CARL9170FW_SET_DAY(d) (((d) - 1) % 31)
 #define LAST_MAGIC     "LAST"
 
 #define CARL9170FW_SET_DAY(d) (((d) - 1) % 31)
@@ -186,6 +187,16 @@ struct carl9170fw_txsq_desc {
 #define CARL9170FW_TXSQ_DESC_SIZE                      \
        (sizeof(struct carl9170fw_txsq_desc))
 
 #define CARL9170FW_TXSQ_DESC_SIZE                      \
        (sizeof(struct carl9170fw_txsq_desc))
 
+#define CARL9170FW_WOL_DESC_MIN_VER                    1
+#define CARL9170FW_WOL_DESC_CUR_VER                    1
+struct carl9170fw_wol_desc {
+       struct carl9170fw_desc_head head;
+
+       __le32 supported_triggers;      /* CARL9170_WOL_ */
+} __packed;
+#define CARL9170FW_WOL_DESC_SIZE                       \
+       (sizeof(struct carl9170fw_wol_desc))
+
 #define CARL9170FW_LAST_DESC_MIN_VER                   1
 #define CARL9170FW_LAST_DESC_CUR_VER                   2
 struct carl9170fw_last_desc {
 #define CARL9170FW_LAST_DESC_MIN_VER                   1
 #define CARL9170FW_LAST_DESC_CUR_VER                   2
 struct carl9170fw_last_desc {
index e3afc9564e67a66b8a197f12955989c889c5cdc6..4a1bdab6b7c213f181187b50752635a5fa6f2bcf 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef __CARL9170_SHARED_VERSION_H
 #define __CARL9170_SHARED_VERSION_H
 #define CARL9170FW_VERSION_YEAR 11
 #ifndef __CARL9170_SHARED_VERSION_H
 #define __CARL9170_SHARED_VERSION_H
 #define CARL9170FW_VERSION_YEAR 11
-#define CARL9170FW_VERSION_MONTH 3
-#define CARL9170FW_VERSION_DAY 4
+#define CARL9170FW_VERSION_MONTH 5
+#define CARL9170FW_VERSION_DAY 7
 #define CARL9170FW_VERSION_GIT "1.9.3"
 #endif /* __CARL9170_SHARED_VERSION_H */
 #define CARL9170FW_VERSION_GIT "1.9.3"
 #endif /* __CARL9170_SHARED_VERSION_H */
index 492f0178726cea579ad0e464e837df959ae2485a..81c5de4249135211948088dae2254287c24b5c53 100644 (file)
@@ -38,14 +38,14 @@ binutils: src/binutils-$(BINUTILS_VER)
        mkdir -p build/binutils
        cd build/binutils; \
        $(BASEDIR)/$</configure --target=sh-elf --prefix=$(BASEDIR)/inst; \
        mkdir -p build/binutils
        cd build/binutils; \
        $(BASEDIR)/$</configure --target=sh-elf --prefix=$(BASEDIR)/inst; \
-       $(MAKE); \
+       $(MAKE) -j3; \
        $(MAKE) install
 
 gcc: src/gcc-$(GCC_VER) binutils
        mkdir -p build/gcc
        cd build/gcc; \
        $(BASEDIR)/$</configure --target=sh-elf --prefix=$(BASEDIR)/inst -enable-languages=c --without-pkgversion --with-newlib; \
        $(MAKE) install
 
 gcc: src/gcc-$(GCC_VER) binutils
        mkdir -p build/gcc
        cd build/gcc; \
        $(BASEDIR)/$</configure --target=sh-elf --prefix=$(BASEDIR)/inst -enable-languages=c --without-pkgversion --with-newlib; \
-       $(MAKE); \
+       $(MAKE) -j3; \
        $(MAKE) install
 
 clean:
        $(MAKE) install
 
 clean:
index b2f7e2d1238d1f333edc6be2cda673abec0ea86b..90de57520e93980f8db517491cb0d26159446b00 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "carlfw.h"
 
 
 #include "carlfw.h"
 
+#include "fwcmd.h"
 #include "compiler.h"
 
 struct feature_list {
 #include "compiler.h"
 
 struct feature_list {
@@ -166,7 +167,7 @@ static void show_dbg_desc(const struct carl9170fw_desc_head *head,
 }
 
 static void show_txsq_desc(const struct carl9170fw_desc_head *head,
 }
 
 static void show_txsq_desc(const struct carl9170fw_desc_head *head,
-                           struct carlfw *fw __unused)
+                          struct carlfw *fw __unused)
 {
        const struct carl9170fw_txsq_desc *txsq = (const void *) head;
 
 {
        const struct carl9170fw_txsq_desc *txsq = (const void *) head;
 
@@ -174,8 +175,26 @@ static void show_txsq_desc(const struct carl9170fw_desc_head *head,
                le32_to_cpu(txsq->seq_table_addr));
 }
 
                le32_to_cpu(txsq->seq_table_addr));
 }
 
+
+static const struct feature_list wol_triggers_v1[] = {
+       CHECK_FOR_FEATURE(CARL9170_WOL_DISCONNECT),
+       CHECK_FOR_FEATURE(CARL9170_WOL_MAGIC_PKT),
+};
+
+static void show_wol_desc(const struct carl9170fw_desc_head *head,
+                         struct carlfw *fw __unused)
+{
+       const struct carl9170fw_wol_desc *wol = (const void *) head;
+
+       fprintf(stdout, "\tSupported WOWLAN triggers: (raw:%.08x)\n",
+               le32_to_cpu(wol->supported_triggers));
+
+       check_feature_list(head, wol->supported_triggers, wol_triggers_v1,
+                          ARRAY_SIZE(wol_triggers_v1), fw);
+}
+
 static void show_chk_desc(const struct carl9170fw_desc_head *head,
 static void show_chk_desc(const struct carl9170fw_desc_head *head,
-                           struct carlfw *fw __unused)
+                         struct carlfw *fw __unused)
 {
        const struct carl9170fw_chk_desc *chk = (const void *) head;
 
 {
        const struct carl9170fw_chk_desc *chk = (const void *) head;
 
@@ -214,6 +233,7 @@ static const struct {
        ADD_HANDLER(DBG, show_dbg_desc),
        ADD_HANDLER(FIX, show_fix_desc),
        ADD_HANDLER(CHK, show_chk_desc),
        ADD_HANDLER(DBG, show_dbg_desc),
        ADD_HANDLER(FIX, show_fix_desc),
        ADD_HANDLER(CHK, show_chk_desc),
+       ADD_HANDLER(WOL, show_wol_desc),
        ADD_HANDLER(LAST, show_last_desc),
 };
 
        ADD_HANDLER(LAST, show_last_desc),
 };