Commit 32eb7c73 authored by Xose Vazquez Perez's avatar Xose Vazquez Perez Committed by Ben Hutchings
Browse files

linux-firmware: add carl9170 firmware



GPLv2 firmware for carl9170, Atheros AR9170 802.11 draft-n USB driver.

Cc: Christian Lamparter <chunkeey@googlemail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Ben Hutchings <ben@decadent.org.uk>
Signed-off-by: default avatarXose Vazquez Perez <xose.vazquez@gmail.com>
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent 8cf14f0a
/*
* Shared Atheros AR9170 Header
*
* RX/TX meta descriptor format
*
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
* 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.
*
* 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; see the file COPYING. If not, see
* http://www.gnu.org/licenses/.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* Copyright (c) 2007-2008 Atheros Communications, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef __CARL9170_SHARED_WLAN_H
#define __CARL9170_SHARED_WLAN_H
#include "fwcmd.h"
#define AR9170_RX_PHY_RATE_CCK_1M 0x0a
#define AR9170_RX_PHY_RATE_CCK_2M 0x14
#define AR9170_RX_PHY_RATE_CCK_5M 0x37
#define AR9170_RX_PHY_RATE_CCK_11M 0x6e
#define AR9170_ENC_ALG_NONE 0x0
#define AR9170_ENC_ALG_WEP64 0x1
#define AR9170_ENC_ALG_TKIP 0x2
#define AR9170_ENC_ALG_AESCCMP 0x4
#define AR9170_ENC_ALG_WEP128 0x5
#define AR9170_ENC_ALG_WEP256 0x6
#define AR9170_ENC_ALG_CENC 0x7
#define AR9170_RX_ENC_SOFTWARE 0x8
#define AR9170_RX_STATUS_MODULATION 0x03
#define AR9170_RX_STATUS_MODULATION_S 0
#define AR9170_RX_STATUS_MODULATION_CCK 0x00
#define AR9170_RX_STATUS_MODULATION_OFDM 0x01
#define AR9170_RX_STATUS_MODULATION_HT 0x02
#define AR9170_RX_STATUS_MODULATION_DUPOFDM 0x03
/* depends on modulation */
#define AR9170_RX_STATUS_SHORT_PREAMBLE 0x08
#define AR9170_RX_STATUS_GREENFIELD 0x08
#define AR9170_RX_STATUS_MPDU 0x30
#define AR9170_RX_STATUS_MPDU_S 4
#define AR9170_RX_STATUS_MPDU_SINGLE 0x00
#define AR9170_RX_STATUS_MPDU_FIRST 0x20
#define AR9170_RX_STATUS_MPDU_MIDDLE 0x30
#define AR9170_RX_STATUS_MPDU_LAST 0x10
#define AR9170_RX_STATUS_CONT_AGGR 0x40
#define AR9170_RX_STATUS_TOTAL_ERROR 0x80
#define AR9170_RX_ERROR_RXTO 0x01
#define AR9170_RX_ERROR_OVERRUN 0x02
#define AR9170_RX_ERROR_DECRYPT 0x04
#define AR9170_RX_ERROR_FCS 0x08
#define AR9170_RX_ERROR_WRONG_RA 0x10
#define AR9170_RX_ERROR_PLCP 0x20
#define AR9170_RX_ERROR_MMIC 0x40
/* these are either-or */
#define AR9170_TX_MAC_PROT_RTS 0x0001
#define AR9170_TX_MAC_PROT_CTS 0x0002
#define AR9170_TX_MAC_PROT 0x0003
#define AR9170_TX_MAC_NO_ACK 0x0004
/* if unset, MAC will only do SIFS space before frame */
#define AR9170_TX_MAC_BACKOFF 0x0008
#define AR9170_TX_MAC_BURST 0x0010
#define AR9170_TX_MAC_AGGR 0x0020
/* encryption is a two-bit field */
#define AR9170_TX_MAC_ENCR_NONE 0x0000
#define AR9170_TX_MAC_ENCR_RC4 0x0040
#define AR9170_TX_MAC_ENCR_CENC 0x0080
#define AR9170_TX_MAC_ENCR_AES 0x00c0
#define AR9170_TX_MAC_MMIC 0x0100
#define AR9170_TX_MAC_HW_DURATION 0x0200
#define AR9170_TX_MAC_QOS_S 10
#define AR9170_TX_MAC_QOS 0x0c00
#define AR9170_TX_MAC_DISABLE_TXOP 0x1000
#define AR9170_TX_MAC_TXOP_RIFS 0x2000
#define AR9170_TX_MAC_IMM_BA 0x4000
/* either-or */
#define AR9170_TX_PHY_MOD_CCK 0x00000000
#define AR9170_TX_PHY_MOD_OFDM 0x00000001
#define AR9170_TX_PHY_MOD_HT 0x00000002
/* depends on modulation */
#define AR9170_TX_PHY_SHORT_PREAMBLE 0x00000004
#define AR9170_TX_PHY_GREENFIELD 0x00000004
#define AR9170_TX_PHY_BW_S 3
#define AR9170_TX_PHY_BW (3 << AR9170_TX_PHY_BW_SHIFT)
#define AR9170_TX_PHY_BW_20MHZ 0
#define AR9170_TX_PHY_BW_40MHZ 2
#define AR9170_TX_PHY_BW_40MHZ_DUP 3
#define AR9170_TX_PHY_TX_HEAVY_CLIP_S 6
#define AR9170_TX_PHY_TX_HEAVY_CLIP (7 << \
AR9170_TX_PHY_TX_HEAVY_CLIP_S)
#define AR9170_TX_PHY_TX_PWR_S 9
#define AR9170_TX_PHY_TX_PWR (0x3f << \
AR9170_TX_PHY_TX_PWR_S)
#define AR9170_TX_PHY_TXCHAIN_S 15
#define AR9170_TX_PHY_TXCHAIN (7 << \
AR9170_TX_PHY_TXCHAIN_S)
#define AR9170_TX_PHY_TXCHAIN_1 1
/* use for cck, ofdm 6/9/12/18/24 and HT if capable */
#define AR9170_TX_PHY_TXCHAIN_2 5
#define AR9170_TX_PHY_MCS_S 18
#define AR9170_TX_PHY_MCS (0x7f << \
AR9170_TX_PHY_MCS_S)
#define AR9170_TX_PHY_RATE_CCK_1M 0x0
#define AR9170_TX_PHY_RATE_CCK_2M 0x1
#define AR9170_TX_PHY_RATE_CCK_5M 0x2
#define AR9170_TX_PHY_RATE_CCK_11M 0x3
/* same as AR9170_RX_PHY_RATE */
#define AR9170_TXRX_PHY_RATE_OFDM_6M 0xb
#define AR9170_TXRX_PHY_RATE_OFDM_9M 0xf
#define AR9170_TXRX_PHY_RATE_OFDM_12M 0xa
#define AR9170_TXRX_PHY_RATE_OFDM_18M 0xe
#define AR9170_TXRX_PHY_RATE_OFDM_24M 0x9
#define AR9170_TXRX_PHY_RATE_OFDM_36M 0xd
#define AR9170_TXRX_PHY_RATE_OFDM_48M 0x8
#define AR9170_TXRX_PHY_RATE_OFDM_54M 0xc
#define AR9170_TXRX_PHY_RATE_HT_MCS0 0x0
#define AR9170_TXRX_PHY_RATE_HT_MCS1 0x1
#define AR9170_TXRX_PHY_RATE_HT_MCS2 0x2
#define AR9170_TXRX_PHY_RATE_HT_MCS3 0x3
#define AR9170_TXRX_PHY_RATE_HT_MCS4 0x4
#define AR9170_TXRX_PHY_RATE_HT_MCS5 0x5
#define AR9170_TXRX_PHY_RATE_HT_MCS6 0x6
#define AR9170_TXRX_PHY_RATE_HT_MCS7 0x7
#define AR9170_TXRX_PHY_RATE_HT_MCS8 0x8
#define AR9170_TXRX_PHY_RATE_HT_MCS9 0x9
#define AR9170_TXRX_PHY_RATE_HT_MCS10 0xa
#define AR9170_TXRX_PHY_RATE_HT_MCS11 0xb
#define AR9170_TXRX_PHY_RATE_HT_MCS12 0xc
#define AR9170_TXRX_PHY_RATE_HT_MCS13 0xd
#define AR9170_TXRX_PHY_RATE_HT_MCS14 0xe
#define AR9170_TXRX_PHY_RATE_HT_MCS15 0xf
#define AR9170_TX_PHY_SHORT_GI 0x80000000
#ifdef __CARL9170FW__
struct ar9170_tx_hw_mac_control {
union {
struct {
/*
* Beware of compiler bugs in all gcc pre 4.4!
*/
u8 erp_prot:2;
u8 no_ack:1;
u8 backoff:1;
u8 burst:1;
u8 ampdu:1;
u8 enc_mode:2;
u8 hw_mmic:1;
u8 hw_duration:1;
u8 qos_queue:2;
u8 disable_txop:1;
u8 txop_rifs:1;
u8 ba_end:1;
u8 probe:1;
} __packed;
__le16 set;
} __packed;
} __packed;
struct ar9170_tx_hw_phy_control {
union {
struct {
/*
* Beware of compiler bugs in all gcc pre 4.4!
*/
u8 modulation:2;
u8 preamble:1;
u8 bandwidth:2;
u8:1;
u8 heavy_clip:3;
u8 tx_power:6;
u8 chains:3;
u8 mcs:7;
u8:6;
u8 short_gi:1;
} __packed;
__le32 set;
} __packed;
} __packed;
struct ar9170_tx_rate_info {
u8 tries:3;
u8 erp_prot:2;
u8 ampdu:1;
u8 free:2; /* free for use (e.g.:RIFS/TXOP/AMPDU) */
} __packed;
struct carl9170_tx_superdesc {
__le16 len;
u8 rix;
u8 cnt;
u8 cookie;
u8 ampdu_density:3;
u8 ampdu_factor:2;
u8 ampdu_commit_density:1;
u8 ampdu_commit_factor:1;
u8 ampdu_unused_bit:1;
u8 queue:2;
u8 assign_seq:1;
u8 vif_id:3;
u8 fill_in_tsf:1;
u8 cab:1;
u8 padding2;
struct ar9170_tx_rate_info ri[CARL9170_TX_MAX_RATES];
struct ar9170_tx_hw_phy_control rr[CARL9170_TX_MAX_RETRY_RATES];
} __packed;
struct ar9170_tx_hwdesc {
__le16 length;
struct ar9170_tx_hw_mac_control mac;
struct ar9170_tx_hw_phy_control phy;
} __packed;
struct ar9170_tx_frame {
struct ar9170_tx_hwdesc hdr;
union {
struct ieee80211_hdr i3e;
u8 payload[0];
} data;
} __packed;
struct carl9170_tx_superframe {
struct carl9170_tx_superdesc s;
struct ar9170_tx_frame f;
} __packed __aligned(4);
#endif /* __CARL9170FW__ */
struct _ar9170_tx_hwdesc {
__le16 length;
__le16 mac_control;
__le32 phy_control;
} __packed;
#define CARL9170_TX_SUPER_AMPDU_DENSITY_S 0
#define CARL9170_TX_SUPER_AMPDU_DENSITY 0x7
#define CARL9170_TX_SUPER_AMPDU_FACTOR 0x18
#define CARL9170_TX_SUPER_AMPDU_FACTOR_S 3
#define CARL9170_TX_SUPER_AMPDU_COMMIT_DENSITY 0x20
#define CARL9170_TX_SUPER_AMPDU_COMMIT_DENSITY_S 5
#define CARL9170_TX_SUPER_AMPDU_COMMIT_FACTOR 0x40
#define CARL9170_TX_SUPER_AMPDU_COMMIT_FACTOR_S 6
#define CARL9170_TX_SUPER_MISC_QUEUE 0x3
#define CARL9170_TX_SUPER_MISC_QUEUE_S 0
#define CARL9170_TX_SUPER_MISC_ASSIGN_SEQ 0x4
#define CARL9170_TX_SUPER_MISC_VIF_ID 0x38
#define CARL9170_TX_SUPER_MISC_VIF_ID_S 3
#define CARL9170_TX_SUPER_MISC_FILL_IN_TSF 0x40
#define CARL9170_TX_SUPER_MISC_CAB 0x80
#define CARL9170_TX_SUPER_RI_TRIES 0x7
#define CARL9170_TX_SUPER_RI_TRIES_S 0
#define CARL9170_TX_SUPER_RI_ERP_PROT 0x18
#define CARL9170_TX_SUPER_RI_ERP_PROT_S 3
#define CARL9170_TX_SUPER_RI_AMPDU 0x20
#define CARL9170_TX_SUPER_RI_AMPDU_S 5
struct _carl9170_tx_superdesc {
__le16 len;
u8 rix;
u8 cnt;
u8 cookie;
u8 ampdu_settings;
u8 misc;
u8 padding;
u8 ri[CARL9170_TX_MAX_RATES];
__le32 rr[CARL9170_TX_MAX_RETRY_RATES];
} __packed;
struct _carl9170_tx_superframe {
struct _carl9170_tx_superdesc s;
struct _ar9170_tx_hwdesc f;
u8 frame_data[0];
} __packed __aligned(4);
#define CARL9170_TX_SUPERDESC_LEN 24
#define AR9170_TX_HWDESC_LEN 8
#define CARL9170_TX_SUPERFRAME_LEN (CARL9170_TX_SUPERDESC_LEN + \
AR9170_TX_HWDESC_LEN)
struct ar9170_rx_head {
u8 plcp[12];
} __packed;
#define AR9170_RX_HEAD_LEN 12
struct ar9170_rx_phystatus {
union {
struct {
u8 rssi_ant0, rssi_ant1, rssi_ant2,
rssi_ant0x, rssi_ant1x, rssi_ant2x,
rssi_combined;
} __packed;
u8 rssi[7];
} __packed;
u8 evm_stream0[6], evm_stream1[6];
u8 phy_err;
} __packed;
#define AR9170_RX_PHYSTATUS_LEN 20
struct ar9170_rx_macstatus {
u8 SAidx, DAidx;
u8 error;
u8 status;
} __packed;
#define AR9170_RX_MACSTATUS_LEN 4
struct ar9170_rx_frame_single {
struct ar9170_rx_head phy_head;
struct ieee80211_hdr i3e;
struct ar9170_rx_phystatus phy_tail;
struct ar9170_rx_macstatus macstatus;
} __packed;
struct ar9170_rx_frame_head {
struct ar9170_rx_head phy_head;
struct ieee80211_hdr i3e;
struct ar9170_rx_macstatus macstatus;
} __packed;
struct ar9170_rx_frame_middle {
struct ieee80211_hdr i3e;
struct ar9170_rx_macstatus macstatus;
} __packed;
struct ar9170_rx_frame_tail {
struct ieee80211_hdr i3e;
struct ar9170_rx_phystatus phy_tail;
struct ar9170_rx_macstatus macstatus;
} __packed;
struct ar9170_rx_frame {
union {
struct ar9170_rx_frame_single single;
struct ar9170_rx_frame_head head;
struct ar9170_rx_frame_middle middle;
struct ar9170_rx_frame_tail tail;
} __packed;
} __packed;
static inline u8 ar9170_get_decrypt_type(struct ar9170_rx_macstatus *t)
{
return (t->SAidx & 0xc0) >> 4 |
(t->DAidx & 0xc0) >> 6;
}
/*
* This is an workaround for several undocumented bugs.
* Don't mess with the QoS/AC <-> HW Queue map, if you don't
* know what you are doing.
*
* Known problems [hardware]:
* * The MAC does not aggregate frames on anything other
* than the first HW queue.
* * when an AMPDU is placed [in the first hw queue] and
* additional frames are already queued on a different
* hw queue, the MAC will ALWAYS freeze.
*
* In a nutshell: The hardware can either do QoS or
* Aggregation but not both at the same time. As a
* result, this makes the device pretty much useless
* for any serious 802.11n setup.
*/
enum ar9170_txq {
AR9170_TXQ_BK = 0, /* TXQ0 */
AR9170_TXQ_BE, /* TXQ1 */
AR9170_TXQ_VI, /* TXQ2 */
AR9170_TXQ_VO, /* TXQ3 */
__AR9170_NUM_TXQ,
};
#define AR9170_TXQ_DEPTH 32
#endif /* __CARL9170_SHARED_WLAN_H */
cmake_minimum_required(VERSION 2.8)
project(miniboot.fw)
include("../extra/sh-elf-linux.cmake")
include("../config.cmake")
set(miniboot_src miniboot.S)
set_source_files_properties(miniboot.S PROPERTIES LANGUAGE C)
add_executable(miniboot.elf miniboot.S)
set_target_properties(miniboot.elf PROPERTIES LINKER_LANGUAGE C)
set_target_properties(miniboot.elf PROPERTIES LINK_FLAGS "-Tminiboot.lds")
add_custom_target(
miniboot.fw ALL
${OBJCOPY} --strip-unneeded -O binary -R .sram -R .eeprom -R .fwdsc miniboot.elf miniboot.fw
DEPENDS miniboot.elf)
config CARL9170FW_BUILD_MINIBOOT
def_bool y
prompt "Build MiniBoot Firmware Header"
.globl _start
.type _start, @function
.section ".boot", "ax"
_start:
mov.l startcode, r0
jmp @r0
startcode: .long 0x00000008
ENTRY(_start);
MEMORY
{
pram : ORIGIN = 0x200000, LENGTH = 16k
}
SECTIONS
{
.padding : {
/* NOP NOP just in case */
LONG(0x00090009)
} > pram
.boot : { *(.boot) } > pram
.text : { *(.text*) } > pram
.rodata : { *(.rodata*) } > pram
.bss : { *(.bss) } > pram
.data : { *(.data*) } > pram
}
BINUTILS_VER=2.22
BINUTILS_URL=http://mirrors.kernel.org/gnu/binutils/binutils-$(BINUTILS_VER).tar.bz2
BINUTILS_TAR=binutils-$(BINUTILS_VER).tar.bz2
NEWLIB_VER=1.20.0
NEWLIB_URL=ftp://sources.redhat.com/pub/newlib/newlib-$(NEWLIB_VER).tar.gz
NEWLIB_TAR=newlib-$(NEWLIB_VER).tar.gz
GCC_VER=4.7.1
GCC_URL=http://mirrors.kernel.org/gnu/gcc/gcc-$(GCC_VER)/gcc-$(GCC_VER).tar.bz2
GCC_TAR=gcc-$(GCC_VER).tar.bz2
BASEDIR=$(shell pwd)
all: gcc
src/$(BINUTILS_TAR):
wget -P src $(BINUTILS_URL)
src/$(NEWLIB_TAR):
wget -P src $(NEWLIB_URL)
src/$(GCC_TAR):
wget -P src $(GCC_URL)
src/binutils-$(BINUTILS_VER): src/$(BINUTILS_TAR)
tar -C src -xjf $<
src/newlib-$(NEWLIB_VER): src/$(NEWLIB_TAR)
tar -C src -xzf $<
src/gcc-$(GCC_VER): src/$(GCC_TAR) src/newlib-$(NEWLIB_VER)
tar -C src -xjf $<
ln -s $(BASEDIR)/src/newlib-$(NEWLIB_VER)/newlib $@
ln -s $(BASEDIR)/src/newlib-$(NEWLIB_VER)/libgloss $@
binutils: src/binutils-$(BINUTILS_VER)
mkdir -p build/binutils
cd build/binutils; \
$(BASEDIR)/$</configure --target=sh-elf --prefix=$(BASEDIR)/inst; \
$(MAKE) -j3; \
$(MAKE) install
gcc: src/gcc-$(GCC_VER) binutils
mkdir -p build/gcc
cd build/gcc; \
$(BASEDIR)/$</configure --target=sh-elf --prefix=$(BASEDIR)/inst -enable-languages=c --without-pkgversion --with-newlib; \
$(MAKE) -j3; \
$(MAKE) install
clean:
rm -rf build inst
distclean: clean
rm -rf src
cmake_minimum_required(VERSION 2.8)
project(tools)
if (CONFIG_CARL9170FW_MAKE_RELEASE)
set(CMAKE_BUILD_TYPE Release)
endif (CONFIG_CARL9170FW_MAKE_RELEASE)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/extra)
include(GCCVersion)
include("../config.cmake")
_COMPILER_DUMPVERSION(_COMPILER_VERSION)
if (("${_COMPILER_VERSION}" VERSION_GREATER 44) OR
("${_COMPILER_VERSION}" VERSION_EQUAL 44))
include_directories (../include/linux ../include/shared ../include lib include)
add_subdirectory(lib)
add_subdirectory(src)
if (CONFIG_CARL9170FW_BUILD_TOOLS_CARLU)
find_package(SDL QUIET)
find_package(USB-1.0 QUIET)
if ("${USB-1.0_FOUND}" AND "${SDL_FOUND}")
add_subdirectory(carlu)
else()
if ("${USB-1.0_FOUND}")
MESSAGE(ERROR "LibUSB not found\n")
endif ("${USB-1.0_FOUND}")
if ("${SDL_FOUND}")
MESSAGE(ERROR "SDL not found\n")
endif ("${SDL_FOUND}")
endif ()
endif (CONFIG_CARL9170FW_BUILD_TOOLS_CARLU)
endif ()
menu "Firmware Tools"
config CARL9170FW_BUILD_TOOLS
def_bool y
prompt "Build Firmware Tools"
config CARL9170FW_BUILD_TOOLS_CARLU
def_bool n
prompt "Build CARLU testbench"
depends on CARL9170FW_BUILD_TOOLS
endmenu
cmake_minimum_required(VERSION 2.8)
project(carlu)
find_package(SDL REQUIRED)
find_package(USB-1.0 REQUIRED)
set(carlu_src src/debug.c src/cmd.c src/usb.c src/rx.c src/tx.c src/fw.c
src/test.c src/main.c)
add_definitions(-D_GNU_SOURCE ${USB-1.0_DEFINITIONS})
add_definitions(-DCARLU_PATH="${CMAKE_CURRENT_SOURCE_DIR}")
include_directories(${SDL_INCLUDE_DIR} ${USB-1.0_INCLUDE_DIRS})
add_executable(carlu ${carlu_src})
target_link_libraries (carlu ${SDL_LIBRARY} ${USB-1.0_LIBRARIES} SDLmain carlfw)
/*
* carlu - userspace testing utility for ar9170 devices
*
* common API declaration
*
* 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.
*/
#ifndef __CARL9170USER_H
#define __CARL9170USER_H
#include "SDL.h"
#include "SDL_thread.h"
#include "carlfw.h"
#include "debug.h"
#include "hw.h"
#include "fwcmd.h"
#include "frame.h"
#include "eeprom.h"
#include "ieee80211.h"
#include "wlan.h"
#include "usb.h"
struct carlu {
libusb_device_handle *dev;
libusb_context *ctx;
SDL_Thread *event_thread;
bool stop_event_polling;
struct libusb_transfer *rx_ring[AR9170_RX_BULK_BUFS];
struct libusb_transfer *rx_interrupt;
unsigned char irq_buf[AR9170_RX_BULK_IRQ_SIZE];
union {
unsigned char buf[CARL9170_MAX_CMD_LEN];
uint32_t buf4[CARL9170_MAX_CMD_LEN / sizeof(uint32_t)];
struct carl9170_cmd cmd;
struct carl9170_rsp rsp;
} cmd;
struct list_head tx_queue;
SDL_mutex *tx_queue_lock;
unsigned int tx_queue_len;
struct list_head dev_list;
unsigned int idx;
unsigned int miniboot_size;
unsigned int rx_max;
int event_pipe[2];
SDL_cond *resp_pend;
SDL_mutex *resp_lock;
uint8_t *resp_buf;
size_t resp_len;
int tx_pending;
uint8_t cookie;
void (*tx_cb)(struct carlu *, struct frame *);
void (*tx_fb_cb)(struct carlu *, struct frame *);
void (*rx_cb)(struct carlu *, void *, unsigned int);
int (*cmd_cb)(struct carlu *, struct carl9170_rsp *,
void *, unsigned int);
struct carlfw *fw;
struct ar9170_eeprom eeprom;
struct frame_queue tx_sent_queue[__AR9170_NUM_TXQ];
SDL_mutex *mem_lock;
unsigned int dma_chunks;
unsigned int dma_chunk_size;
unsigned int used_dma_chunks;
unsigned int extra_headroom;
bool tx_stream;
bool rx_stream;
/* statistics */
unsigned int rxed;
unsigned int txed;
unsigned long tx_octets;
unsigned long rx_octets;
};
struct carlu_rate {
int8_t rix;
int8_t cnt;
uint8_t flags;
};
struct carlu_tx_info_tx {
unsigned int key;
};
struct carlu_tx_info {
uint32_t flags;
struct carlu_rate rates[CARL9170_TX_MAX_RATES];
union {
struct carlu_tx_info_tx tx;
};
};
static inline struct carlu_tx_info *get_tx_info(struct frame *frame)
{
return (void *) frame->cb;
}
void *carlu_alloc_driver(size_t size);
void carlu_free_driver(struct carlu *ar);
int carlu_fw_check(struct carlu *ar);
void carlu_fw_info(struct carlu *ar);
void carlu_rx(struct carlu *ar, struct frame *frame);
int carlu_tx(struct carlu *ar, struct frame *frame);
void carlu_tx_feedback(struct carlu *ar,
struct carl9170_rsp *cmd);
void carlu_handle_command(struct carlu *ar, void *buf, unsigned int len);
struct frame *carlu_alloc_frame(struct carlu *ar, unsigned int size);
void carlu_free_frame(struct carlu *ar, struct frame *frame);
#endif /* __CARL9170USER_H */
/*
* carlu - userspace testing utility for ar9170 devices
*
* Abstraction Layer for FW/HW command interface
*
* 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 <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "carlu.h"
#include "usb.h"
#include "debug.h"
#include "fwcmd.h"
#include "eeprom.h"
#include "cmd.h"
int carlu_cmd_echo(struct carlu *ar, const uint32_t message)
{
uint32_t _message;
int ret;
ret = carlusb_cmd(ar, CARL9170_CMD_ECHO,
(uint8_t *)&message, sizeof(message),
(uint8_t *)&_message, sizeof(_message));
if (ret == 0)
ret = (message == _message) ? 0 : -EIO;
return ret;
}
struct carl9170_cmd *carlu_cmd_buf(struct carlu *ar,
const enum carl9170_cmd_oids cmd, const unsigned int len)
{
struct carl9170_cmd *tmp;
if (len % 4 || (sizeof(struct carl9170_cmd_head) + len > 64))
return ERR_PTR(-EINVAL);
tmp = malloc(sizeof(struct carl9170_cmd_head) + len);
if (tmp) {
tmp->hdr.cmd = cmd;
tmp->hdr.len = len;
}
return tmp;
}
int carlu_cmd_reboot(struct carlu *ar)
{
struct carl9170_cmd *reboot;
int err;
/* sure, we could put the struct on the stack too. */
reboot = carlu_cmd_buf(ar, CARL9170_CMD_REBOOT_ASYNC, 0);
if (IS_ERR_OR_NULL(reboot))
return reboot ? PTR_ERR(reboot) : -ENOMEM;
err = carlusb_cmd_async(ar, reboot, true);
return err;
}
int carlu_cmd_mem_dump(struct carlu *ar, const uint32_t start,
const unsigned int len, void *_buf)
{
#define RW 8 /* number of words to read at once */
#define RB (sizeof(uint32_t) * RW)
uint8_t *buf = _buf;
unsigned int i, j, block;
int err;
__le32 offsets[RW];
for (i = 0; i < (len + RB - 1) / RB; i++) {
block = min_t(unsigned int, (len - RB * i) / sizeof(uint32_t), RW);
for (j = 0; j < block; j++)
offsets[j] = cpu_to_le32(start + RB * i + 4 * j);
err = carlusb_cmd(ar, CARL9170_CMD_RREG,
(void *) &offsets, block * sizeof(uint32_t),
(void *) buf + RB * i, RB);
if (err)
return err;
}
#undef RW
#undef RB
return 0;
}
int carlu_cmd_mem_watch(struct carlu *ar, const uint32_t mem,
const unsigned int len, void *_buf)
{
#define RW 8 /* number of words to read at once */
#define RB (sizeof(uint32_t) * RW)
uint8_t *buf = _buf;
unsigned int i, j, block;
int err;
__le32 offsets[RW];
for (i = 0; i < (len + RB - 1) / RB; i++) {
block = min_t(unsigned int, (len - RB * i) / sizeof(uint32_t), RW);
for (j = 0; j < block; j++)
offsets[j] = cpu_to_le32(mem);
err = carlusb_cmd(ar, CARL9170_CMD_RREG,
(void *) &offsets, block * sizeof(uint32_t),
(void *) buf + RB * i, RB);
if (err)
return err;
}
#undef RW
#undef RB
return 0;
}
int carlu_cmd_write_mem(struct carlu *ar, const uint32_t addr,
const uint32_t val)
{
int err;
__le32 msg, block[2] = { cpu_to_le32(addr), cpu_to_le32(val) };
err = carlusb_cmd(ar, CARL9170_CMD_WREG,
(void *) &block, sizeof(block),
(void *) &msg, sizeof(msg));
return err;
}
int carlu_cmd_read_mem(struct carlu *ar, const uint32_t _addr,
uint32_t *val)
{
int err;
__le32 msg, addr = { cpu_to_le32(_addr) };
err = carlusb_cmd(ar, CARL9170_CMD_RREG, (void *) &addr, sizeof(addr),
(void *) &msg, sizeof(msg));
*val = le32_to_cpu(msg);
return err;
}
int carlu_cmd_read_eeprom(struct carlu *ar)
{
int err;
err = carlu_cmd_mem_dump(ar, AR9170_EEPROM_START, sizeof(ar->eeprom),
&ar->eeprom);
#ifndef __CHECKER__
/* don't want to handle trailing remains */
BUILD_BUG_ON(sizeof(ar->eeprom) % 8);
#endif
if (ar->eeprom.length == cpu_to_le16(0xffff))
return -ENODATA;
return 0;
}
/*
* carlu - userspace testing utility for ar9170 devices
*
* register/memory/command access 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.
*/
#ifndef __CARL9170USER_CMD_H
#define __CARL9170USER_CMD_H
#include "carlu.h"
int carlu_cmd_echo(struct carlu *ar, const uint32_t message);
int carlu_cmd_reboot(struct carlu *ar);
int carlu_cmd_read_eeprom(struct carlu *ar);
int carlu_cmd_mem_dump(struct carlu *ar, const uint32_t start,
const unsigned int len, void *_buf);
int carlu_cmd_write_mem(struct carlu *ar, const uint32_t addr,
const uint32_t val);
int carlu_cmd_mem_watch(struct carlu *ar, const uint32_t mem,
const unsigned int len, void *_buf);
struct carl9170_cmd *carlu_cmd_buf(struct carlu *ar,
const enum carl9170_cmd_oids cmd, const unsigned int len);
#define PAYLOAD_MAX (CARL9170_MAX_CMD_LEN / 4 - 1)
/*
* Macros to facilitate writing multiple registers in a single
* write-combining USB command. Note that when the first group
* fails the whole thing will fail without any others attempted,
* but you won't know which write in the group failed.
*/
#define carlu_regwrite_begin(ar) \
do { \
struct carlu *__ar = ar; \
unsigned int __nreg = 0; \
int __err = 0; \
uint32_t __dummy;
#define carlu_regwrite_flush() \
if (__nreg) { \
__err = carlusb_cmd(__ar, CARL9170_CMD_WREG, \
(u8 *)&__ar->cmd.cmd.data, 8 * __nreg, \
(u8 *)&__dummy, sizeof(__dummy)); \
__nreg = 0; \
if (__err) \
goto __regwrite_out; \
}
#define carlu_regwrite(r, v) do { \
__ar->cmd.buf4[2 * __nreg + 1] = cpu_to_le32(r); \
__ar->cmd.buf4[2 * __nreg + 2] = cpu_to_le32(v); \
__nreg++; \
if ((__nreg >= PAYLOAD_MAX / 2)) { \
__err = carlusb_cmd(__ar, CARL9170_CMD_WREG, \
(u8 *)&__ar->cmd.cmd.data, 8 * __nreg, \
(u8 *)&__dummy, sizeof(__dummy)); \
\
__nreg = 0; \
if (__err) \
goto __regwrite_out; \
} \
} while (0)
#define carlu_regwrite_finish() \
__regwrite_out : \
if (__err == 0 && __nreg) \
carlu_regwrite_flush();
#define carlu_regwrite_result() \
__err; \
} while (0);
#define carlu_async_get_buf() \
do { \
__cmd = carlu_cmd_buf(__carl, CARL9170_CMD_WREG_ASYNC, \
CARL9170_MAX_CMD_PAYLOAD_LEN); \
if (IS_ERR_OR_NULL(__cmd)) { \
__err = __cmd ? PTR_ERR(__cmd) : -ENOMEM; \
goto __async_regwrite_out; \
} \
} while (0);
#define carlu_async_regwrite_begin(carl) \
do { \
int __nreg = 0, __err = 0; \
struct carlu *__carl = carl; \
struct carl9170_cmd *__cmd; \
carlu_async_get_buf(); \
#define carlu_async_regwrite_flush() \
if (__nreg) { \
__cmd->hdr.len = 8 * __nreg; \
__err = carlusb_cmd_async(__carl, __cmd, true); \
__nreg = 0; \
if (__err) \
goto __async_regwrite_out; \
__cmd = NULL; \
carlu_async_get_buf(); \
}
#define carlu_async_regwrite(r, v) do { \
__cmd->wreg.regs[__nreg].addr = cpu_to_le32(r); \
__cmd->wreg.regs[__nreg].val = cpu_to_le32(v); \
__nreg++; \
if ((__nreg >= PAYLOAD_MAX / 2)) \
carlu_async_regwrite_flush(); \
} while (0)
#define carlu_async_regwrite_finish() \
__async_regwrite_out : \
if (__err == 0 && __nreg) \
carlu_async_regwrite_flush();
#define carlu_async_regwrite_result() \
__err; \
} while (0);
#endif /* __CARL9170USER_CMD_H */
/*
* carlu - userspace testing utility for ar9170 devices
*
* Random assortment of debug stuff
*
* 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 <ctype.h>
#include "debug.h"
bool print_message_debug_level;
enum debug_level_t debug_level;
FILE *_stdout;
FILE *_stddbg;
FILE *_stderr;
void init_debug()
{
debug_level = VERBOSE;
debug_level = INFO;
print_message_debug_level = false;
_stdout = stdout;
_stddbg = stdout;
_stderr = stderr;
}
FILE *dbg_lvl_to_fh(const enum debug_level_t lvl)
{
switch (lvl) {
case ERROR:
case WARNING:
return _stderr;
case INFO:
return _stdout;
case VERBOSE:
return _stddbg;
default:
BUG_ON(1);
}
}
void print_hex_dump_bytes(const enum debug_level_t lvl, const char *pre,
const void *buf, size_t len)
{
char line[58];
char str[17] = { 0 };
const unsigned char *tmp = (void *) buf;
char *pbuf = line;
size_t i, j;
for (i = 0; i < len; i++) {
if (i % 16 == 0) {
if (pbuf != line) {
__fprintf(lvl, "%s%s: %s\n", pre, line, str);
pbuf = line;
}
pbuf += sprintf(pbuf, "0x%04lx: ", (unsigned long)i);
}
pbuf += sprintf(pbuf, "%.2x ", tmp[i]);
str[i % 16] = (isprint(tmp[i]) && isascii(tmp[i])) ? tmp[i] : '.';
}
if (pbuf != line) {
if ((i % 16)) {
str[i % 16] = '\0';
for (j = 0; j < (16 - (i % 16)); j++)
pbuf += sprintf(pbuf, " ");
}
__fprintf(lvl, "%s%s: %s\n", pre, line, str);
}
}
/*
* carlu - userspace testing utility for ar9170 devices
*
* Debug API definition
*
* 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.
*/
#ifndef __CARL9170USER_DEBUG_H
#define __CARL9170USER_DEBUG_H
#include <stdio.h>
#include "compiler.h"
enum debug_level_t {
SILENT,
ERROR,
WARNING,
INFO,
VERBOSE,
/* KEEP LAST */
ALL,
};
extern bool print_message_debug_level;
extern enum debug_level_t debug_level;
#define __fprintf(lvl, fmt, args...) do { \
if (lvl <= debug_level) { \
if (print_message_debug_level) \
fprintf(dbg_lvl_to_fh(lvl), "<%d>:" fmt, lvl, ##args); \
else \
fprintf(dbg_lvl_to_fh(lvl), fmt, ##args); \
} \
} while (0);
#define dbg(fmt, args...) __fprintf(VERBOSE, fmt, ##args)
#define info(fmt, args...) __fprintf(INFO, fmt, ##args)
#define warn(fmt, args...) __fprintf(WARNING, fmt, ##args)
#define err(fmt, args...) __fprintf(ERROR, fmt, ##args)
#define BUG_ON(a) \
do { \
if (a) { \
__fprintf(ERROR, "!!!=>BUG IN function \"%s\" at line %d<=!!! %s\n", \
__func__, __LINE__, #a); \
fflush(stderr); \
abort(); \
} \
} while (0)
FILE *dbg_lvl_to_fh(const enum debug_level_t lvl);
void init_debug(void);
void print_hex_dump_bytes(const enum debug_level_t lvl, const char *prefix,
const void *buf, size_t len);
#endif /* __CARL9170USER_DEBUG_H */
/*
* carlu - userspace testing utility for ar9170 devices
*
* Firmware parsers
*
* 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 <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "carlu.h"
#include "usb.h"
#include "debug.h"
int carlu_fw_check(struct carlu *ar)
{
struct carl9170fw_otus_desc *otus_desc;
otus_desc = carlfw_find_desc(ar->fw, (uint8_t *) OTUS_MAGIC,
sizeof(*otus_desc),
CARL9170FW_OTUS_DESC_CUR_VER);
if (!otus_desc) {
err("No valid OTUS descriptor found.\n");
return -EINVAL;
}
if (!carl9170fw_supports(otus_desc->feature_set, CARL9170FW_DUMMY_FEATURE)) {
err("Invalid Firmware Descriptor.\n");
return -EIO;
}
if (carl9170fw_supports(otus_desc->feature_set, CARL9170FW_UNUSABLE))
dbg("Firmware is marked as unuseable.\n");
info("Firmware Version: %d.\n", otus_desc->api_ver);
return 0;
}
int carlusb_fw_check(struct carlu *ar)
{
struct carl9170fw_otus_desc *otus_desc;
otus_desc = carlfw_find_desc(ar->fw, (uint8_t *) OTUS_MAGIC,
sizeof(*otus_desc),
CARL9170FW_OTUS_DESC_CUR_VER);
if (!otus_desc) {
err("No valid USB descriptor found.\n");
return -ENODATA;
}
if (!carl9170fw_supports(otus_desc->feature_set, CARL9170FW_DUMMY_FEATURE)) {
err("Invalid Firmware Descriptor.\n");
return -EINVAL;
}
if (!carl9170fw_supports(otus_desc->feature_set, CARL9170FW_USB_INIT_FIRMWARE)) {
err("Firmware does not know how to initialize USB core.\n");
return -EOPNOTSUPP;
}
if (carl9170fw_supports(otus_desc->feature_set, CARL9170FW_USB_DOWN_STREAM)) {
dbg("Enabled tx stream mode.\n");
ar->tx_stream = true;
ar->extra_headroom = sizeof(struct ar9170_stream);
}
if (carl9170fw_supports(otus_desc->feature_set, CARL9170FW_USB_UP_STREAM)) {
dbg("Enabled rx stream mode.\n");
ar->rx_stream = true;
}
if (carl9170fw_supports(otus_desc->feature_set, CARL9170FW_USB_RESP_EP2))
dbg("Firmware sends traps over EP2.\n");
ar->dma_chunk_size = le16_to_cpu(otus_desc->tx_frag_len);
ar->dma_chunks = otus_desc->tx_descs;
ar->rx_max = le16_to_cpu(otus_desc->rx_max_frame_len);
if (carl9170fw_supports(otus_desc->feature_set, CARL9170FW_MINIBOOT))
ar->miniboot_size = le16_to_cpu(otus_desc->miniboot_size);
return 0;
}
void carlu_fw_info(struct carlu *ar)
{
struct carl9170fw_motd_desc *motd_desc;
unsigned int fw_date;
motd_desc = carlfw_find_desc(ar->fw, (uint8_t *) MOTD_MAGIC,
sizeof(*motd_desc),
CARL9170FW_MOTD_DESC_CUR_VER);
if (motd_desc) {
fw_date = le32_to_cpu(motd_desc->fw_year_month_day);
info("Firmware Date: 2%.3d-%.2d-%.2d\n",
CARL9170FW_GET_YEAR(fw_date), CARL9170FW_GET_MONTH(fw_date),
CARL9170FW_GET_DAY(fw_date));
}
}
/*
* carlu - userspace testing utility for ar9170 devices
*
* main program routine
*
* 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 "SDL.h"
#include <SDL_version.h>
#include "debug.h"
#include "carlu.h"
#include "usb.h"
#include "frame.h"
#include "test.h"
#include "cmd.h"
void *carlu_alloc_driver(size_t size)
{
unsigned int i;
struct carlu *ar;
if (size < sizeof(*ar)) {
err("bogus driver context request.");
return NULL;
}
ar = malloc(size);
if (ar == NULL) {
err("failed to alloc driver context.");
return NULL;
}
memset(ar, 0, size);
for (i = 0; i < __AR9170_NUM_TXQ; i++)
frame_queue_init(&ar->tx_sent_queue[i]);
ar->resp_lock = SDL_CreateMutex();
ar->mem_lock = SDL_CreateMutex();
ar->resp_pend = SDL_CreateCond();
ar->tx_pending = 0;
return ar;
}
void carlu_free_driver(struct carlu *ar)
{
unsigned int i;
dbg("destroy driver struct.\n");
SDL_DestroyMutex(ar->resp_lock);
SDL_DestroyMutex(ar->mem_lock);
SDL_DestroyCond(ar->resp_pend);
for (i = 0; i < __AR9170_NUM_TXQ; i++)
frame_queue_kill(&ar->tx_sent_queue[i]);
free(ar);
}
static int carlu_init()
{
struct SDL_version compiled;
int ret;
SDL_VERSION(&compiled);
dbg("=== SDL %d.%d.%d ===\n", compiled.major, compiled.minor, compiled.patch);
ret = SDL_Init(SDL_INIT_TIMER);
if (ret != 0) {
err("Unable to initialize SDL: (%s)\n", SDL_GetError());
return EXIT_FAILURE;
}
return usb_init();
}
static void carlu_exit()
{
SDL_Quit();
usb_exit();
}
static int carlu_dump_eeprom(void)
{
struct carlu *carl = NULL;
uint8_t data[8192] = { 0 };
int err;
err = carlu_init();
if (err)
goto out;
carl = carlusb_probe();
if (IS_ERR_OR_NULL(carl)) {
err = PTR_ERR(carl);
goto out;
}
err = carlu_cmd_mem_dump(carl, 0, sizeof(data), &data);
if (err)
goto out_close;
print_hex_dump_bytes(INFO, "EEPROM:", data, sizeof(data));
out_close:
carlusb_close(carl);
out:
carlu_exit();
return err ? EXIT_FAILURE : EXIT_SUCCESS;
}
static int carlu_run_gpio_test(void)
{
struct carlu *carl = NULL;
int err;
err = carlu_init();
if (err)
goto out;
carl = carlusb_probe();
if (IS_ERR_OR_NULL(carl)) {
err = PTR_ERR(carl);
goto out;
}
err = carlu_gpio_test(carl);
if (err)
goto out_close;
out_close:
carlusb_close(carl);
out:
carlu_exit();
return err ? EXIT_FAILURE : EXIT_SUCCESS;
}
static int carlu_run_random_test(void)
{
struct carlu *carl = NULL;
int err;
err = carlu_init();
if (err)
goto out;
carl = carlusb_probe();
if (IS_ERR_OR_NULL(carl)) {
err = PTR_ERR(carl);
goto out;
}
err = carlu_random_test(carl);
if (err)
goto out_close;
out_close:
carlusb_close(carl);
out:
carlu_exit();
return err ? EXIT_FAILURE : EXIT_SUCCESS;
}
static int carlu_run_loop_test(void)
{
struct carlu *carl;
int err;
err = carlu_init();
if (err)
return EXIT_FAILURE;
carl = carlusb_probe();
if (IS_ERR_OR_NULL(carl)) {
err = PTR_ERR(carl);
goto out;
}
carlu_cmd_write_mem(carl, AR9170_MAC_REG_BCN_PERIOD, 0xFFFFFFFF);
carlu_cmd_write_mem(carl, AR9170_MAC_REG_PRETBTT, 0xFFFFFFFF);
/* different payload test */
carlu_loopback_test(carl, 9000, 1000, 1566, 1566);
carlusb_close(carl);
out:
return err ? EXIT_FAILURE : EXIT_SUCCESS;
}
static int carlu_probe_all(void)
{
struct carlu *carl[32] = { 0 };
unsigned int devs;
int ret;
ret = carlu_init();
if (ret)
return EXIT_FAILURE;
for (devs = 0; devs < ARRAY_SIZE(carl); devs++) {
carl[devs] = carlusb_probe();
if (IS_ERR_OR_NULL(carl[devs]))
break;
}
info("Found %d devices\n", devs);
for (; devs > 0; devs--)
carlusb_close(carl[devs - 1]);
carlu_exit();
return EXIT_SUCCESS;
}
struct menu_struct {
char option;
unsigned int parameters;
int (*function)(void);
char help_text[80];
};
#define MENU_ITEM(op, func, helpme) \
{ \
.option = op, \
.parameters = 0, \
.function = func, \
.help_text = helpme, \
}
static int show_help(void);
static const struct menu_struct menu[] = {
[0] = MENU_ITEM('h', show_help, "shows this useless help message text."), /* keep this entry at 0! */
MENU_ITEM('e', carlu_dump_eeprom, "hexdumps eeprom content to stdout."),
MENU_ITEM('l', carlusb_print_known_devices, "list of all known ar9170 usb devices."),
MENU_ITEM('p', carlu_probe_all, "probe all possible devices."),
MENU_ITEM('t', carlu_run_loop_test, "run tx/rx test."),
MENU_ITEM('g', carlu_run_gpio_test, "flash the leds."),
MENU_ITEM('r', carlu_run_random_test, "get random numbers."),
};
static int show_help(void)
{
unsigned int i;
char parameters[ARRAY_SIZE(menu) + 1];
for (i = 0; i < ARRAY_SIZE(menu); i++)
parameters[i] = menu[i].option;
parameters[ARRAY_SIZE(menu)] = '\0';
info("usage: ar9170user -[%s]\n", parameters);
for (i = 0; i < ARRAY_SIZE(menu); i++)
info("\t-%c\t%s\n", menu[i].option, menu[i].help_text);
return EXIT_FAILURE;
}
static int select_menu_item(const char arg)
{
unsigned int i;
for (i = ARRAY_SIZE(menu) - 1; i != 0; i--) {
if (arg == menu[i].option)
break;
}
return menu[i].function();
}
int main(int argc, char *argv[])
{
init_debug();
if (argc != 2 || strlen(argv[1]) != 2 || argv[1][0] != '-')
return show_help();
return select_menu_item(argv[1][1]);
}
/*
* carlu - userspace testing utility for ar9170 devices
*
* RX data processing
*
* 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 "ieee80211.h"
#include "wlan.h"
static void carlu_handle_data(struct carlu *ar, void *buf,
unsigned int len)
{
if (ar->rx_cb) {
ar->rx_cb(ar, buf, len);
} else {
dbg("unhandled data:\n");
print_hex_dump_bytes(VERBOSE, "DATA:", buf, len);
}
}
void carlu_handle_command(struct carlu *ar, void *buf,
unsigned int len)
{
struct carl9170_rsp *cmd;
int ret = 0;
cmd = (void *) buf;
if ((cmd->hdr.cmd & CARL9170_RSP_FLAG) != CARL9170_RSP_FLAG) {
if ((cmd->hdr.cmd & CARL9170_CMD_ASYNC_FLAG))
return;
SDL_mutexP(ar->resp_lock);
if (ar->resp_buf && ar->resp_len && ar->resp_len >= (len - 4)) {
memcpy(ar->resp_buf, buf + 4, len - 4);
ar->resp_buf = NULL;
} else {
warn("spurious command response (%d / %d)\n",
(int) len - 4, (int) ar->resp_len);
print_hex_dump_bytes(WARNING, "RSP:", buf, len);
}
SDL_mutexV(ar->resp_lock);
SDL_CondSignal(ar->resp_pend);
return;
}
if (ar->cmd_cb)
ret = ar->cmd_cb(ar, cmd, buf, len);
if (ret) {
switch (cmd->hdr.cmd) {
case CARL9170_RSP_TXCOMP:
carlu_tx_feedback(ar, cmd);
break;
case CARL9170_RSP_TEXT:
info("carl9170 FW: %.*s\n", (int)len - 4, (char *)buf + 4);
break;
case CARL9170_RSP_HEXDUMP:
info("carl9170 FW: hexdump\n");
print_hex_dump_bytes(INFO, "HEX:", (char *)buf + 4, len - 4);
break;
case CARL9170_RSP_WATCHDOG:
err("Woof Woof! Watchdog notification.\n");
break;
case CARL9170_RSP_GPIO:
info("GPIO Interrupt => GPIO state %.8x\n",
le32_to_cpu(cmd->gpio.gpio));
break;
case CARL9170_RSP_RADAR:
info("RADAR Interrupt");
break;
default:
warn("received unhandled event 0x%x\n", cmd->hdr.cmd);
print_hex_dump_bytes(WARNING, "RSP:", (char *)buf + 4, len - 4);
break;
}
}
}
static void __carlu_rx(struct carlu *ar, uint8_t *buf, unsigned int len)
{
unsigned int i;
i = 0;
/* weird thing, but this is the same in the original driver */
while (len > 2 && i < 12 && buf[0] == 0xff && buf[1] == 0xff) {
i += 2;
len -= 2;
buf += 2;
}
if (i == 12) {
struct carl9170_rsp *cmd;
i = 0;
while (i < len) {
cmd = (void *) &buf[i];
carlu_handle_command(ar, cmd, cmd->hdr.len + 4);
i += cmd->hdr.len + 4;
}
} else {
carlu_handle_data(ar, buf, len);
}
}
static void carlu_rx_stream(struct carlu *ar, struct frame *frame)
{
void *buf = frame->data;
unsigned int len = frame->len;
while (len >= 4) {
struct ar9170_stream *rx_stream;
unsigned int resplen, elen;
rx_stream = (void *) buf;
resplen = le16_to_cpu(rx_stream->length);
elen = roundup(resplen + 4, 4);
if (rx_stream->tag != cpu_to_le16(0x4e00)) {
warn("frame has no tag %p %u %x.\n",
buf, (int) len, rx_stream->tag);
print_hex_dump_bytes(WARNING, "FRAME:", frame->data, frame->len);
__carlu_rx(ar, buf, len);
return;
}
__carlu_rx(ar, rx_stream->payload, resplen);
len -= elen;
buf += elen;
}
}
void carlu_rx(struct carlu *ar, struct frame *frame)
{
if (ar->rx_stream)
carlu_rx_stream(ar, frame);
else
__carlu_rx(ar, frame->data, frame->len);
}
/*
* carlu - userspace testing utility for ar9170 devices
*
* Various tests
*
* 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 "SDL.h"
#include "carlu.h"
#include "debug.h"
#include "frame.h"
#include "usb.h"
#include "cmd.h"
void debug_test(void)
{
err("This is an error.\n");
warn("This is a warnig.\n");
info("This is an informative message.\n");
dbg("This is just utter useless babble.\n");
}
void carlu_frame_test(struct carlu *ar)
{
struct frame *frame;
frame = carlu_alloc_frame(ar, 0x40);
frame_reserve(frame, 0x10);
memset(frame_put(frame, 0x10), 0x11, 0x10);
memset(frame_put(frame, 0x10), 0x22, 0x10);
memset(frame_push(frame, 0x10), 0x33, 0x10);
memset(frame_put(frame, 0x10), 0x44, 0x10);
print_hex_dump_bytes(INFO, "DATA:", frame->data, frame->len);
print_hex_dump_bytes(INFO, "PAYLOAD:", frame->payload, frame->alloced);
frame_free(frame);
}
static void carlu_loopback_tx_cb(struct carlu *ar __unused,
struct frame *frame __unused)
{
}
static int carlu_loopback_cmd(struct carlu *ar __unused,
struct carl9170_rsp *cmd, void *buf __unused,
unsigned int len __unused)
{
unsigned int i, n;
switch (cmd->hdr.cmd) {
case CARL9170_RSP_TXCOMP:
n = cmd->hdr.ext;
dbg("received tx feedback (%d).\n", n);
for (i = 0; i < n; i++) {
dbg("cookie:%x info:%x\n",
cmd->_tx_status[i].cookie,
cmd->_tx_status[i].info);
}
return -1;
default:
return -1;
}
}
static void carlu_loopback_rx(struct carlu *ar,
void *buf __unused, unsigned int len)
{
ar->rxed++;
ar->rx_octets += len;
}
static void carlu_loopback_mark_tx_frames(struct frame *frame)
{
unsigned int i;
for (i = 0; i < frame->len; i++)
frame->data[i] = (uint8_t) i;
}
void carlu_loopback_test(struct carlu *ar, const unsigned int total_runs,
const unsigned int interval, const unsigned int min_len, const unsigned int max_len)
{
struct frame *frame;
uint32_t start_time, total_time = 0;
float moctets, dtime;
unsigned int runs = 0, i = 0, j = 0, len;
int ret;
if (min_len > max_len) {
err("stresstest: invalid parameters => min_len:%d > max_len:%d",
min_len, max_len);
return;
}
if (min_len < 4) {
err("stresstest: invalid parameters => min_len is smaller than 4");
return;
}
len = min_len;
frame = carlu_alloc_frame(ar, len);
frame_put(frame, len);
carlu_loopback_mark_tx_frames(frame);
ar->rx_cb = carlu_loopback_rx;
ar->cmd_cb = carlu_loopback_cmd;
ar->tx_cb = carlu_loopback_tx_cb;
start_time = SDL_GetTicks();
while (runs <= total_runs) {
if (frame && carlu_tx(ar, frame) == 0) {
len = min_len;
i++;
} else {
frame_free(frame);
}
frame = NULL;
frame = carlu_alloc_frame(ar, len);
frame_put(frame, len);
carlu_loopback_mark_tx_frames(frame);
j++;
total_time = SDL_GetTicks() - start_time;
if (total_time >= interval) {
moctets = ((float)ar->tx_octets) / (1024.0f * 1024.0f);
dtime = ((float)total_time) / 1000;
info("%d: tx %d of %d => %.2f MiB in %.2f secs => %.4f MBits/s\n",
runs, i, j, moctets, dtime, (moctets * 8.0f) / dtime);
moctets = ((float)ar->rx_octets) / (1024.0f * 1024.0f);
info("%d: rx %d of %d => %.2f MiB in %.2f secs => %.4f MBits/s\n",
runs, ar->rxed, i, moctets, dtime, (moctets * 8.0f) / dtime);
if ((ar->rxed == 0 && i) || !i) {
ret = carlu_cmd_echo(ar, 0xdeadbeef);
if (ret)
warn("firmware crashed... echo_cmd: (%d)\n", ret);
}
total_time = 0;
i = 0;
j = 0;
ar->rxed = 0;
ar->txed = 0;
ar->rx_octets = 0;
ar->tx_octets = 0;
runs++;
start_time = SDL_GetTicks();
}
}
ar->rx_cb = NULL;
ar->cmd_cb = NULL;
ar->tx_cb = NULL;
}
int carlu_gpio_test(struct carlu *ar)
{
uint32_t gpio;
#define CHK(cmd) \
do { \
int __err = cmd; \
if ((__err)) \
return __err; \
} while (0)
CHK(carlu_cmd_read_mem(ar, AR9170_GPIO_REG_PORT_DATA, &gpio));
info("GPIO state:%x\n", gpio);
/* turn both LEDs on */
CHK(carlu_cmd_write_mem(ar, AR9170_GPIO_REG_PORT_DATA,
AR9170_GPIO_PORT_LED_0 | AR9170_GPIO_PORT_LED_1));
SDL_Delay(700);
CHK(carlu_cmd_read_mem(ar, AR9170_GPIO_REG_PORT_DATA, &gpio));
info("GPIO state:%x\n", gpio);
/* turn LEDs off everything */
CHK(carlu_cmd_write_mem(ar, AR9170_GPIO_REG_PORT_DATA, 0));
CHK(carlu_cmd_read_mem(ar, AR9170_GPIO_REG_PORT_DATA, &gpio));
info("GPIO state:%x\n", gpio);
}
int carlu_random_test(struct carlu *ar)
{
uint32_t buf[4096];
int err, i;
err = carlu_cmd_mem_watch(ar, AR9170_RAND_REG_NUM, sizeof(buf), buf);
if (err)
return err;
for (i = 0; i < ARRAY_SIZE(buf); i++)
info("%.2x %.2x ", buf[i] & 0xff, buf[i] >> 8);
info("\n");
}
/*
* carlu - userspace testing utility for ar9170 devices
*
* test.c header
*
* 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.
*/
#ifndef __CARL9170USER_TEST_H
#define __CARL9170USER_TEST_H
#include "carlu.h"
void carlu_loopback_test(struct carlu *ar, const unsigned int total_runs,
const unsigned int interval, const unsigned int min_len,
const unsigned int max_len);
int carlu_gpio_test(struct carlu *ar);
int carlu_random_test(struct carlu *ar);
#endif /* __CARL9170USER_TEST_H */
/*
* 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;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment