carl9170 firmware: handle download queue exceptions
authorChristian Lamparter <chunkeey@googlemail.com>
Tue, 8 Mar 2011 23:49:28 +0000 (00:49 +0100)
committerChristian Lamparter <chunkeey@googlemail.com>
Thu, 10 Mar 2011 01:51:01 +0000 (02:51 +0100)
Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
carlfw/include/dma.h
carlfw/src/hostif.c

index 70417360854bcab334b47fc08d0d6c14f96fc508..11a760dfc98139cb68c921d188d1cc19c8b71941 100644 (file)
@@ -215,7 +215,6 @@ struct dma_desc *dma_unlink_head(struct dma_queue *queue);
 void dma_init_descriptors(void);
 void dma_reclaim(struct dma_queue *q, struct dma_desc *desc);
 void dma_put(struct dma_queue *q, struct dma_desc *desc);
-void dma_queue_reclaim(struct dma_queue *dst, struct dma_queue *src);
 
 static inline __inline bool is_terminator(struct dma_queue *q, struct dma_desc *desc)
 {
@@ -275,6 +274,10 @@ static inline __inline struct dma_desc *dma_dequeue_not_bits(struct dma_queue *q
             (desc->status & AR9170_OWN_BITS) == bits);                 \
             desc = (queue)->head)
 
+#define __for_each_desc_continue(desc, queue)                          \
+       for (;desc != (queue)->terminator;                              \
+            desc = (desc)->lastAddr->nextAddr)
+
 #define __for_each_desc(desc, queue)                                   \
        for (desc = (queue)->head;                                      \
             desc != (queue)->terminator;                               \
@@ -311,6 +314,15 @@ static inline __inline void dma_rearm(struct dma_desc *desc)
                        AR9170_OWN_BITS_HW);
 }
 
+static inline __inline void dma_fix_downqueue(struct dma_desc *desc)
+{
+       desc->status = AR9170_OWN_BITS_HW;
+       desc->ctrl = 0;
+       desc->dataSize = 0;
+       desc->totalLen = AR9170_BLOCK_SIZE;
+       desc->lastAddr = desc;
+}
+
 static inline void __check_desc(void)
 {
        struct ar9170_dma_memory mem;
index d032c0f72da47ab9ed1ed054c4a38fd5f27600e4..94c2d04f064cca3ce297843c6fa70dfd8a148c0f 100644 (file)
@@ -56,10 +56,10 @@ 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) {
+       for_each_desc_bits(desc, &fw.pta.down_queue, AR9170_OWN_BITS_SE) {
                if (unlikely((length_check(desc) == false))) {
                        /*
                         * There is no easy way of telling what was lost.
@@ -71,10 +71,9 @@ static void handle_download(void)
 
                        dma_reclaim(&fw.pta.down_queue, desc);
                        down_trigger();
-                       continue;
+               } else {
+                       wlan_tx(desc);
                }
-
-               wlan_tx(desc);
        }
 
 #ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT
@@ -111,6 +110,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 +162,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
 }