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)
{
(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; \
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;
* 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.
dma_reclaim(&fw.pta.down_queue, desc);
down_trigger();
- continue;
+ } else {
+ wlan_tx(desc);
}
-
- wlan_tx(desc);
}
#ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT
#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)
{
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
}