carl9170 firmware: drop failed frames faster
[carl9170fw.git] / carlfw / src / hostif.c
index e005147a931f6000b256345df266e78a47f7d77f..0e7cbf2a23d572b614e095d6e65c5b2071c3b5a4 100644 (file)
@@ -6,7 +6,7 @@
  * Copyright (c) 2000-2005 ZyDAS Technology Corporation
  * Copyright (c) 2007-2009 Atheros Communications, Inc.
  * Copyright   2009    Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2009, 2010 Christian Lamparter <chunkeey@googlemail.com>
+ * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -30,7 +30,7 @@
 
 static bool length_check(struct dma_desc *desc)
 {
-       volatile struct carl9170_tx_superframe *super = DESC_PAYLOAD(desc);
+       volatile struct carl9170_tx_superframe *super = __get_super(desc);
 
        if (unlikely(desc->totalLen < sizeof(struct carl9170_tx_superdesc)))
                return false;
@@ -56,7 +56,7 @@ static void handle_download(void)
         * Under normal conditions, all completed descs should have
         * the AR9170_OWN_BITS_SE status flag set.
         * However there seems to be a undocumented case where the flag
-        * is _SW...
+        * is _SW ( handle_download_exception )
         */
 
        for_each_desc_not_bits(desc, &fw.pta.down_queue, AR9170_OWN_BITS_HW) {
@@ -69,12 +69,12 @@ static void handle_download(void)
                         * timeout mechanism.
                         */
 
+                       wlan_tx_complete(__get_super(desc), false);
                        dma_reclaim(&fw.pta.down_queue, desc);
                        down_trigger();
-                       continue;
+               } else {
+                       wlan_tx(desc);
                }
-
-               wlan_tx(desc);
        }
 
 #ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT
@@ -101,7 +101,7 @@ static void handle_upload(void)
                        down_trigger();
 #else
                        dma_reclaim(&fw.wlan.rx_queue, desc);
-                       _wlan_trigger(AR9170_DMA_TRIGGER_RXQ);
+                       wlan_trigger(AR9170_DMA_TRIGGER_RXQ);
 #endif /* CONFIG_CARL9170FW_LOOPBACK */
                }
        }
@@ -111,6 +111,40 @@ static void handle_upload(void)
 #endif /* CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT */
 }
 
+static void handle_download_exception(void)
+{
+       struct dma_desc *desc, *target;
+
+       /* actually, the queue should be stopped by now? */
+       usb_stop_down_queue();
+
+       target = (void *)((get(AR9170_PTA_REG_DN_CURR_ADDRH) << 16) |
+                 get(AR9170_PTA_REG_DN_CURR_ADDRL));
+
+       /*
+        * Put "forgotten" packets from the head of the queue, back
+        * to the current position
+        */
+       __while_desc_bits(desc, &fw.pta.down_queue, AR9170_OWN_BITS_HW) {
+               if (desc == target)
+                       break;
+
+               dma_reclaim(&fw.pta.down_queue,
+                   dma_unlink_head(&fw.pta.down_queue));
+       }
+
+       __for_each_desc_continue(desc, &fw.pta.down_queue) {
+               if ((desc->status & AR9170_OWN_BITS) == AR9170_OWN_BITS_SW) {
+                       dma_fix_downqueue(desc);
+               }
+       }
+
+
+       usb_start_down_queue();
+
+       down_trigger();
+}
+
 /* handle interrupts from DMA chip */
 void handle_host_interface(void)
 {
@@ -129,5 +163,9 @@ void handle_host_interface(void)
 
        HANDLER(pta_int, AR9170_PTA_INT_FLAG_UP, handle_upload);
 
+       /* This is just guesswork and MAGIC */
+       pta_int = get(AR9170_PTA_REG_DMA_STATUS);
+       HANDLER(pta_int, 0x1, handle_download_exception);
+
 #undef HANDLER
 }