From 990ce9efaea1c6e46372a6758988aff6e9d202e9 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Wed, 9 Mar 2011 00:49:28 +0100 Subject: [PATCH] carl9170 firmware: handle download queue exceptions Signed-off-by: Christian Lamparter --- carlfw/include/dma.h | 14 ++++++++++++- carlfw/src/hostif.c | 47 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/carlfw/include/dma.h b/carlfw/include/dma.h index 7041736..11a760d 100644 --- a/carlfw/include/dma.h +++ b/carlfw/include/dma.h @@ -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; diff --git a/carlfw/src/hostif.c b/carlfw/src/hostif.c index d032c0f..94c2d04 100644 --- a/carlfw/src/hostif.c +++ b/carlfw/src/hostif.c @@ -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 } -- 2.31.1