/* * carl9170 firmware - used by the ar9170 wireless device * * Host interface routines * * Copyright (c) 2000-2005 ZyDAS Technology Corporation * Copyright (c) 2007-2009 Atheros Communications, Inc. * Copyright 2009 Johannes Berg * Copyright 2009-2011 Christian Lamparter * * 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 * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "carl9170.h" #include "hostif.h" #include "printf.h" #include "wl.h" static bool length_check(struct dma_desc *desc) { volatile struct carl9170_tx_superframe *super = __get_super(desc); if (unlikely(desc->totalLen < sizeof(struct carl9170_tx_superdesc))) return false; /* * check if the DMA is complete, or clipped. * * NB: The hardware aligns the descriptor length to * a 4 byte boundary. This makes the direct comparison * difficult, or unnecessary complex for a hot-path. */ if (unlikely(super->s.len > desc->totalLen)) return false; return true; } static void handle_download(void) { struct dma_desc *desc; /* * 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 ( handle_download_exception ) */ for_each_desc_not_bits(desc, &fw.pta.down_queue, AR9170_OWN_BITS_HW) { if (unlikely((length_check(desc) == false))) { /* * There is no easy way of telling what was lost. * * Therefore we just reclaim the data. * The driver has to have some sort frame * timeout mechanism. */ wlan_tx_complete(__get_super(desc), false); dma_reclaim(&fw.pta.down_queue, desc); down_trigger(); } else { wlan_tx(desc); } } #ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT xorl(AR9170_GPIO_REG_PORT_DATA, 2); #endif /* CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT */ } static void handle_upload(void) { struct dma_desc *desc; for_each_desc_not_bits(desc, &fw.pta.up_queue, AR9170_OWN_BITS_HW) { /* * BIG FAT NOTE: * * DO NOT compare the descriptor addresses. */ if (DESC_PAYLOAD(desc) == (void *) &dma_mem.reserved.rsp) { fw.usb.int_desc = desc; fw.usb.int_desc_available = 1; } else { dma_reclaim(&fw.wlan.rx_queue, desc); wlan_trigger(AR9170_DMA_TRIGGER_RXQ); } } #ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT xorl(AR9170_GPIO_REG_PORT_DATA, 2); #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) { uint32_t pta_int; pta_int = get(AR9170_PTA_REG_INT_FLAG); #define HANDLER(intr, flag, func) \ do { \ if ((intr & flag) != 0) { \ func(); \ } \ } while (0) HANDLER(pta_int, AR9170_PTA_INT_FLAG_DN, handle_download); 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 }