diff options
Diffstat (limited to 'carl9170fw/tools/carlu/src/tx.c')
-rw-r--r-- | carl9170fw/tools/carlu/src/tx.c | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/carl9170fw/tools/carlu/src/tx.c b/carl9170fw/tools/carlu/src/tx.c new file mode 100644 index 0000000..ec97502 --- /dev/null +++ b/carl9170fw/tools/carlu/src/tx.c @@ -0,0 +1,213 @@ +/* + * carlu - userspace testing utility for ar9170 devices + * + * xmit - related functions + * + * 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 + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include "libusb.h" + +#include "carlu.h" +#include "debug.h" +#include "frame.h" +#include "usb.h" +#include "ieee80211.h" +#include "wlan.h" + +struct frame *carlu_alloc_frame(struct carlu *ar, unsigned int size) +{ + struct frame *tmp; + unsigned int total_len; + + total_len = ar->extra_headroom + sizeof(struct _carl9170_tx_superframe) + size; + + tmp = frame_alloc(total_len); + if (!tmp) + return NULL; + + frame_reserve(tmp, sizeof(struct _carl9170_tx_superframe) + ar->extra_headroom); + + tmp->queue = 2; + + return tmp; +} + +static int carlu_alloc_dev_mem(struct carlu *ar, + struct frame *frame) +{ + struct _carl9170_tx_superframe *txp = (void *)frame->data; + unsigned int len, chunks; + + len = roundup(frame->len, ar->dma_chunk_size); + chunks = len / ar->dma_chunk_size; + + SDL_mutexP(ar->mem_lock); + if (ar->tx_pending >= ar->dma_chunks || + ar->used_dma_chunks + chunks >= ar->dma_chunks) { + SDL_mutexV(ar->mem_lock); + return -ENOSPC; + } + + ar->used_dma_chunks += chunks; + ar->tx_pending++; + txp->s.cookie = ar->cookie++; + SDL_mutexV(ar->mem_lock); + + return 0; +} + +static void carlu_free_dev_mem(struct carlu *ar, + struct frame *frame) +{ + struct _carl9170_tx_superframe *txp = (void *)frame->data; + unsigned int len, chunks; + + len = roundup(frame->len, ar->dma_chunk_size); + chunks = len / ar->dma_chunk_size; + + SDL_mutexP(ar->mem_lock); + ar->used_dma_chunks -= chunks; + ar->tx_pending--; + SDL_mutexV(ar->mem_lock); +} + +void carlu_free_frame(struct carlu *ar __unused, + struct frame *frame) +{ + frame_free(frame); +} + +static struct frame *carlu_find_frame(struct carlu *ar, + unsigned int queue, uint8_t cookie) +{ + struct frame *frame = NULL; + + BUG_ON(queue >= __AR9170_NUM_TXQ); + BUG_ON(SDL_mutexP(ar->tx_sent_queue[queue].lock) != 0); + FRAME_WALK(frame, &ar->tx_sent_queue[queue]) { + struct _carl9170_tx_superframe *super; + + super = (void *) frame->data; + if (super->s.cookie == cookie) { + __frame_unlink(&ar->tx_sent_queue[queue], frame); + SDL_mutexV(ar->tx_sent_queue[queue].lock); + return frame; + } + } + SDL_mutexV(ar->tx_sent_queue[queue].lock); + + return NULL; +} + +static void carlu_tx_fb_cb(struct carlu *ar, + struct frame *frame) +{ + if (ar->tx_fb_cb) + ar->tx_fb_cb(ar, frame); + else + carlu_free_frame(ar, frame); + +} + +void carlu_tx_feedback(struct carlu *ar, + struct carl9170_rsp *cmd) +{ + unsigned int i, n, k, q; + struct frame *frame; + struct carlu_tx_info *tx_info; + + n = cmd->hdr.ext; + + for (i = 0; i < n; i++) { + q = (cmd->_tx_status[i].info >> CARL9170_TX_STATUS_QUEUE_S) & + CARL9170_TX_STATUS_QUEUE; + frame = carlu_find_frame(ar, q, cmd->_tx_status[i].cookie); + if (frame) { + carlu_free_dev_mem(ar, frame); + tx_info = get_tx_info(frame); + + k = (cmd->_tx_status[i].info >> CARL9170_TX_STATUS_RIX) + & CARL9170_TX_STATUS_RIX_S; + tx_info->rates[k].cnt = (cmd->_tx_status[i].info >> + CARL9170_TX_STATUS_TRIES_S) & + CARL9170_TX_STATUS_TRIES; + for (k++; k < CARL9170_TX_MAX_RATES; k++) { + tx_info->rates[k].rix = -1; + tx_info->rates[k].cnt = -1; + } + + carlu_tx_fb_cb(ar, frame); + } else { + err("Found no frame for cookie %d.\n", + cmd->_tx_status[i].cookie); + } + } +} + +int carlu_tx(struct carlu *ar, struct frame *frame) +{ + struct _carl9170_tx_superframe *txp; + unsigned int len, queue; + int cookie, err; + + len = frame->len; + + txp = (void *) frame_push(frame, sizeof(struct _carl9170_tx_superframe)); + + if (txp->s.rix) + goto err_out; + + err = carlu_alloc_dev_mem(ar, frame); + if (err) + goto err_out; + + txp->s.len = cpu_to_le16(frame->len); + + queue = (frame->queue % __AR9170_NUM_TXQ); + + SET_VAL(CARL9170_TX_SUPER_MISC_QUEUE, txp->s.misc, queue); + + txp->f.length = len + FCS_LEN; /* + I(C)V_LEN */ + + txp->f.mac_control = cpu_to_le16(AR9170_TX_MAC_HW_DURATION | + AR9170_TX_MAC_BACKOFF); + txp->f.mac_control |= cpu_to_le16(queue << AR9170_TX_MAC_QOS_S); + + txp->f.phy_control = cpu_to_le32(AR9170_TX_PHY_MOD_CCK | AR9170_TX_PHY_BW_20MHZ | + ((17 * 2) << AR9170_TX_PHY_TX_PWR_S) | + (AR9170_TX_PHY_TXCHAIN_1 << AR9170_TX_PHY_TXCHAIN_S) | + (11 << AR9170_TX_PHY_MCS_S)); + + frame_queue_tail(&ar->tx_sent_queue[queue], frame); + carlusb_tx(ar, frame); + return 0; + +err_out: + frame_pull(frame, sizeof(struct _carl9170_tx_superframe)); + return err; +} |