mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/
synced 2026-01-03 17:12:41 +08:00
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: Xose Vazquez Perez <xose.vazquez@gmail.com> Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
This commit is contained in:
committed by
Ben Hutchings
parent
8cf14f0aca
commit
32eb7c7307
38
carl9170fw/tools/CMakeLists.txt
Normal file
38
carl9170fw/tools/CMakeLists.txt
Normal file
@@ -0,0 +1,38 @@
|
||||
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 ()
|
||||
12
carl9170fw/tools/Kconfig
Normal file
12
carl9170fw/tools/Kconfig
Normal file
@@ -0,0 +1,12 @@
|
||||
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
|
||||
18
carl9170fw/tools/carlu/CMakeLists.txt
Normal file
18
carl9170fw/tools/carlu/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
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)
|
||||
147
carl9170fw/tools/carlu/src/carlu.h
Normal file
147
carl9170fw/tools/carlu/src/carlu.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* 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 */
|
||||
189
carl9170fw/tools/carlu/src/cmd.c
Normal file
189
carl9170fw/tools/carlu/src/cmd.c
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
135
carl9170fw/tools/carlu/src/cmd.h
Normal file
135
carl9170fw/tools/carlu/src/cmd.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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 */
|
||||
101
carl9170fw/tools/carlu/src/debug.c
Normal file
101
carl9170fw/tools/carlu/src/debug.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
72
carl9170fw/tools/carlu/src/debug.h
Normal file
72
carl9170fw/tools/carlu/src/debug.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 */
|
||||
131
carl9170fw/tools/carlu/src/fw.c
Normal file
131
carl9170fw/tools/carlu/src/fw.c
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
}
|
||||
307
carl9170fw/tools/carlu/src/main.c
Normal file
307
carl9170fw/tools/carlu/src/main.c
Normal file
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* 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]);
|
||||
}
|
||||
181
carl9170fw/tools/carlu/src/rx.c
Normal file
181
carl9170fw/tools/carlu/src/rx.c
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
237
carl9170fw/tools/carlu/src/test.c
Normal file
237
carl9170fw/tools/carlu/src/test.c
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* 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");
|
||||
}
|
||||
35
carl9170fw/tools/carlu/src/test.h
Normal file
35
carl9170fw/tools/carlu/src/test.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 */
|
||||
213
carl9170fw/tools/carlu/src/tx.c
Normal file
213
carl9170fw/tools/carlu/src/tx.c
Normal file
@@ -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;
|
||||
}
|
||||
793
carl9170fw/tools/carlu/src/usb.c
Normal file
793
carl9170fw/tools/carlu/src/usb.c
Normal file
@@ -0,0 +1,793 @@
|
||||
/*
|
||||
* carlu - userspace testing utility for ar9170 devices
|
||||
*
|
||||
* USB back-end driver
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include "libusb.h"
|
||||
|
||||
#include "carlu.h"
|
||||
#include "usb.h"
|
||||
#include "debug.h"
|
||||
#include "list.h"
|
||||
#include "cmd.h"
|
||||
|
||||
#define ADD_DEV(_vid, _pid, _vs, _ps) { \
|
||||
.idVendor = _vid, \
|
||||
.idProduct = _pid, \
|
||||
.vendor_name = _vs, \
|
||||
.product_name = _ps \
|
||||
}
|
||||
|
||||
static const struct {
|
||||
uint16_t idVendor;
|
||||
uint16_t idProduct;
|
||||
char *vendor_name;
|
||||
char *product_name;
|
||||
} dev_list[] = {
|
||||
ADD_DEV(0x0cf3, 0x9170, "Atheros", "9170"),
|
||||
ADD_DEV(0x0cf3, 0x1001, "Atheros", "TG121N"),
|
||||
ADD_DEV(0x0cf3, 0x1002, "TP-Link", "TL-WN821N v2"),
|
||||
ADD_DEV(0xcace, 0x0300, "Cace", "Airpcap NX"),
|
||||
ADD_DEV(0x07d1, 0x3c10, "D-Link", "DWA 160 A1"),
|
||||
ADD_DEV(0x07d1, 0x3a09, "D-Link", "DWA 160 A2"),
|
||||
ADD_DEV(0x0846, 0x9010, "Netgear", "WNDA3100"),
|
||||
ADD_DEV(0x0846, 0x9001, "Netgear", "WN111 v2"),
|
||||
ADD_DEV(0x0ace, 0x1221, "Zydas", "ZD1221"),
|
||||
ADD_DEV(0x0586, 0x3417, "ZyXEL", "NWD271N"),
|
||||
ADD_DEV(0x0cde, 0x0023, "Z-Com", "UB81 BG"),
|
||||
ADD_DEV(0x0cde, 0x0026, "Z-Com", "UB82 ABG"),
|
||||
ADD_DEV(0x0cde, 0x0027, "Sphairon", "Homelink 1202"),
|
||||
ADD_DEV(0x083a, 0xf522, "Arcadyan", "WN7512"),
|
||||
ADD_DEV(0x2019, 0x5304, "Planex", "GWUS300"),
|
||||
ADD_DEV(0x04bb, 0x093f, "IO-Data", "WNGDNUS2"),
|
||||
ADD_DEV(0x057C, 0x8401, "AVM", "FRITZ!WLAN USB Stick N"),
|
||||
ADD_DEV(0x057C, 0x8402, "AVM", "FRITZ!WLAN USB Stick N 2.4"),
|
||||
};
|
||||
|
||||
static libusb_context *usb_ctx;
|
||||
static LIST_HEAD(active_dev_list);
|
||||
|
||||
static int carlusb_event_thread(void *_ar)
|
||||
{
|
||||
struct carlu *ar = (void *)_ar;
|
||||
dbg("event thread active and polling.\n");
|
||||
|
||||
while (!ar->stop_event_polling)
|
||||
libusb_handle_events(ar->ctx);
|
||||
|
||||
dbg("==> event thread desixed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int carlusb_is_ar9170(struct libusb_device_descriptor *desc)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
|
||||
if ((desc->idVendor == dev_list[i].idVendor) &&
|
||||
(desc->idProduct == dev_list[i].idProduct)) {
|
||||
dbg("== found device \"%s %s\" [0x%04x:0x%04x]\n",
|
||||
dev_list[i].vendor_name, dev_list[i].product_name,
|
||||
desc->idVendor, desc->idProduct);
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool carlusb_is_dev(struct carlu *iter,
|
||||
struct libusb_device *dev)
|
||||
{
|
||||
libusb_device *list_dev;
|
||||
|
||||
if (!iter->dev)
|
||||
return false;
|
||||
|
||||
list_dev = libusb_get_device(iter->dev);
|
||||
|
||||
if (libusb_get_bus_number(list_dev) == libusb_get_bus_number(dev) &&
|
||||
libusb_get_device_address(list_dev) == libusb_get_device_address(dev))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int carlusb_show_devinfo(struct carlu *ar)
|
||||
{
|
||||
struct libusb_device_descriptor desc;
|
||||
libusb_device *dev;
|
||||
int err;
|
||||
|
||||
dev = libusb_get_device(ar->dev);
|
||||
|
||||
err = libusb_get_device_descriptor(dev, &desc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
info("USB Device Information:\n");
|
||||
info("\tUSB VendorID:%.4x(%s), ProductID:%.4x(%s)\n",
|
||||
dev_list[ar->idx].idVendor, dev_list[ar->idx].vendor_name,
|
||||
dev_list[ar->idx].idProduct, dev_list[ar->idx].product_name);
|
||||
info("\tBus:%d Address:%d\n", libusb_get_bus_number(dev),
|
||||
libusb_get_device_address(dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int carlusb_get_dev(struct carlu *ar, bool reset)
|
||||
{
|
||||
struct carlu *iter;
|
||||
libusb_device_handle *dev;
|
||||
libusb_device **list;
|
||||
int ret, err, i, idx = -1;
|
||||
|
||||
ret = libusb_get_device_list(usb_ctx, &list);
|
||||
if (ret < 0) {
|
||||
err("usb device enum failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < ret; i++) {
|
||||
struct libusb_device_descriptor desc;
|
||||
|
||||
memset(&desc, 0, sizeof(desc));
|
||||
err = libusb_get_device_descriptor(list[i], &desc);
|
||||
if (err != 0)
|
||||
continue;
|
||||
|
||||
idx = carlusb_is_ar9170(&desc);
|
||||
if (idx < 0)
|
||||
continue;
|
||||
|
||||
list_for_each_entry(iter, &active_dev_list, dev_list) {
|
||||
if (carlusb_is_dev(iter, list[i])) {
|
||||
err = -EALREADY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
continue;
|
||||
|
||||
err = libusb_open(list[i], &dev);
|
||||
if (err != 0) {
|
||||
err("failed to open device (%d)\n", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
err = libusb_kernel_driver_active(dev, 0);
|
||||
switch (err) {
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
err("failed to aquire exculusive access (%d).\n", err);
|
||||
goto skip;
|
||||
}
|
||||
|
||||
if (reset) {
|
||||
err = libusb_reset_device(dev);
|
||||
if (err != 0) {
|
||||
err("failed to reset device (%d)\n", err);
|
||||
goto skip;
|
||||
}
|
||||
}
|
||||
|
||||
err = libusb_claim_interface(dev, 0);
|
||||
if (err == 0) {
|
||||
dbg(">device is now under our control.\n");
|
||||
break;
|
||||
} else {
|
||||
err("failed to claim device (%d)\n", err);
|
||||
goto skip;
|
||||
}
|
||||
|
||||
skip:
|
||||
libusb_close(dev);
|
||||
}
|
||||
|
||||
if (i != ret) {
|
||||
ar->idx = idx;
|
||||
ar->ctx = usb_ctx;
|
||||
ar->dev = dev;
|
||||
list_add_tail(&ar->dev_list, &active_dev_list);
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
||||
libusb_free_device_list(list, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void carlusb_tx_cb(struct carlu *ar,
|
||||
struct frame *frame)
|
||||
{
|
||||
if (ar->tx_cb)
|
||||
ar->tx_cb(ar, frame);
|
||||
|
||||
ar->tx_octets += frame->len;
|
||||
|
||||
carlu_free_frame(ar, frame);
|
||||
}
|
||||
|
||||
static void carlusb_zap_queues(struct carlu *ar)
|
||||
{
|
||||
struct frame *frame;
|
||||
|
||||
BUG_ON(SDL_mutexP(ar->tx_queue_lock) != 0);
|
||||
while (!list_empty(&ar->tx_queue)) {
|
||||
frame = list_first_entry(&ar->tx_queue, struct frame, dcb.list);
|
||||
list_del(&frame->dcb.list);
|
||||
carlusb_tx_cb(ar, frame);
|
||||
}
|
||||
SDL_mutexV(ar->tx_queue_lock);
|
||||
}
|
||||
|
||||
static void carlusb_free_driver(struct carlu *ar)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(ar)) {
|
||||
if (ar->event_pipe[0] > -1)
|
||||
close(ar->event_pipe[0]);
|
||||
|
||||
if (ar->event_pipe[1] > -1)
|
||||
close(ar->event_pipe[1]);
|
||||
|
||||
carlusb_zap_queues(ar);
|
||||
carlfw_release(ar->fw);
|
||||
ar->fw = NULL;
|
||||
|
||||
if (ar->dev) {
|
||||
libusb_release_interface(ar->dev, 0);
|
||||
libusb_close(ar->dev);
|
||||
ar->dev = NULL;
|
||||
}
|
||||
carlu_free_driver(ar);
|
||||
}
|
||||
}
|
||||
|
||||
static int carlusb_init(struct carlu *ar)
|
||||
{
|
||||
init_list_head(&ar->tx_queue);
|
||||
ar->tx_queue_lock = SDL_CreateMutex();
|
||||
ar->event_pipe[0] = ar->event_pipe[1] = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct carlu *carlusb_open(void)
|
||||
{
|
||||
struct carlu *tmp;
|
||||
int err;
|
||||
|
||||
tmp = carlu_alloc_driver(sizeof(*tmp));
|
||||
if (tmp == NULL)
|
||||
return NULL;
|
||||
|
||||
err = carlusb_init(tmp);
|
||||
if (err < 0)
|
||||
goto err_out;
|
||||
|
||||
err = carlusb_get_dev(tmp, true);
|
||||
if (err < 0)
|
||||
goto err_out;
|
||||
|
||||
return tmp;
|
||||
|
||||
err_out:
|
||||
carlusb_free_driver(tmp);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static void carlusb_cancel_rings(struct carlu *ar)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ar->rx_ring); i++)
|
||||
libusb_cancel_transfer(ar->rx_ring[i]);
|
||||
|
||||
libusb_cancel_transfer(ar->rx_interrupt);
|
||||
}
|
||||
|
||||
static void carlusb_free_rings(struct carlu *ar)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ar->rx_ring); i++)
|
||||
libusb_free_transfer(ar->rx_ring[i]);
|
||||
|
||||
libusb_free_transfer(ar->rx_interrupt);
|
||||
}
|
||||
|
||||
static void carlusb_destroy(struct carlu *ar)
|
||||
{
|
||||
int event_thread_status;
|
||||
|
||||
dbg("==>release device.\n");
|
||||
|
||||
ar->stop_event_polling = true;
|
||||
|
||||
carlusb_cancel_rings(ar);
|
||||
|
||||
SDL_WaitThread(ar->event_thread, &event_thread_status);
|
||||
|
||||
carlusb_free_rings(ar);
|
||||
list_del(&ar->dev_list);
|
||||
}
|
||||
|
||||
static void carlusb_tx_bulk_cb(struct libusb_transfer *transfer);
|
||||
|
||||
static void carlusb_tx_pending(struct carlu *ar)
|
||||
{
|
||||
struct frame *frame;
|
||||
struct libusb_transfer *urb;
|
||||
int err;
|
||||
|
||||
BUG_ON(SDL_mutexP(ar->tx_queue_lock) != 0);
|
||||
if (ar->tx_queue_len >= (AR9170_TX_MAX_ACTIVE_URBS) ||
|
||||
list_empty(&ar->tx_queue))
|
||||
goto out;
|
||||
|
||||
ar->tx_queue_len++;
|
||||
|
||||
urb = libusb_alloc_transfer(0);
|
||||
if (urb == NULL)
|
||||
goto out;
|
||||
|
||||
frame = list_first_entry(&ar->tx_queue, struct frame, dcb.list);
|
||||
list_del(&frame->dcb.list);
|
||||
|
||||
if (ar->tx_stream) {
|
||||
struct ar9170_stream *tx_stream;
|
||||
|
||||
tx_stream = frame_push(frame, sizeof(*tx_stream));
|
||||
tx_stream->length = cpu_to_le16(frame->len);
|
||||
tx_stream->tag = cpu_to_le16(0x697e);
|
||||
}
|
||||
|
||||
libusb_fill_bulk_transfer(urb, ar->dev, AR9170_EP_TX, (unsigned char *)
|
||||
frame->data, frame->len, carlusb_tx_bulk_cb, (void *)frame, 0);
|
||||
|
||||
/* FIXME: ZERO_PACKET support! */
|
||||
urb->flags |= LIBUSB_TRANSFER_FREE_TRANSFER;
|
||||
/* urb->flags |= LIBUSB_TRANSFER_ZERO_PACKET; */
|
||||
frame->dev = (void *) ar;
|
||||
frame_get(frame);
|
||||
|
||||
err = libusb_submit_transfer(urb);
|
||||
if (err != 0) {
|
||||
err("->usb bulk tx submit failed (%d).\n", err);
|
||||
libusb_free_transfer(urb);
|
||||
}
|
||||
|
||||
out:
|
||||
SDL_mutexV(ar->tx_queue_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
void carlusb_tx(struct carlu *ar, struct frame *frame)
|
||||
{
|
||||
BUG_ON(SDL_mutexP(ar->tx_queue_lock) != 0);
|
||||
|
||||
list_add_tail(&frame->dcb.list, &ar->tx_queue);
|
||||
SDL_mutexV(ar->tx_queue_lock);
|
||||
|
||||
carlusb_tx_pending(ar);
|
||||
}
|
||||
|
||||
static void carlusb_tx_bulk_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct frame *frame = (void *) transfer->user_data;
|
||||
struct carlu *ar = (void *) frame->dev;
|
||||
|
||||
BUG_ON(SDL_mutexP(ar->tx_queue_lock) != 0);
|
||||
ar->tx_queue_len--;
|
||||
SDL_mutexV(ar->tx_queue_lock);
|
||||
|
||||
if (ar->tx_stream)
|
||||
frame_pull(frame, 4);
|
||||
|
||||
carlusb_tx_cb(ar, frame);
|
||||
carlusb_tx_pending(ar);
|
||||
}
|
||||
|
||||
static void carlusb_rx_interrupt_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct carlu *ar = (void *) transfer->user_data;
|
||||
int err;
|
||||
|
||||
switch (transfer->status) {
|
||||
case LIBUSB_TRANSFER_COMPLETED:
|
||||
carlu_handle_command(ar, transfer->buffer, transfer->actual_length);
|
||||
break;
|
||||
|
||||
case LIBUSB_TRANSFER_CANCELLED:
|
||||
return;
|
||||
|
||||
default:
|
||||
err("==>rx_irq urb died (%d)\n", transfer->status);
|
||||
break;
|
||||
}
|
||||
|
||||
err = libusb_submit_transfer(transfer);
|
||||
if (err != 0)
|
||||
err("==>rx_irq urb resubmit failed (%d)\n", err);
|
||||
}
|
||||
|
||||
static void carlusb_rx_bulk_cb(struct libusb_transfer *transfer)
|
||||
{
|
||||
struct frame *frame = (void *) transfer->user_data;
|
||||
struct carlu *ar = (void *) frame->dev;
|
||||
int err;
|
||||
|
||||
switch (transfer->status) {
|
||||
case LIBUSB_TRANSFER_COMPLETED:
|
||||
frame_put(frame, transfer->actual_length);
|
||||
|
||||
carlu_rx(ar, frame);
|
||||
|
||||
frame_trim(frame, 0);
|
||||
break;
|
||||
|
||||
case LIBUSB_TRANSFER_CANCELLED:
|
||||
return;
|
||||
|
||||
default:
|
||||
err("==>rx_bulk urb died (%d)\n", transfer->status);
|
||||
break;
|
||||
}
|
||||
|
||||
err = libusb_submit_transfer(transfer);
|
||||
if (err != 0)
|
||||
err("->rx_bulk urb resubmit failed (%d)\n", err);
|
||||
}
|
||||
|
||||
static int carlusb_initialize_rxirq(struct carlu *ar)
|
||||
{
|
||||
int err;
|
||||
|
||||
ar->rx_interrupt = libusb_alloc_transfer(0);
|
||||
if (ar->rx_interrupt == NULL) {
|
||||
err("==>cannot alloc rx interrupt urb\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
libusb_fill_interrupt_transfer(ar->rx_interrupt, ar->dev, AR9170_EP_IRQ,
|
||||
(unsigned char *)&ar->irq_buf, sizeof(ar->irq_buf),
|
||||
carlusb_rx_interrupt_cb, (void *) ar, 0);
|
||||
|
||||
err = libusb_submit_transfer(ar->rx_interrupt);
|
||||
if (err != 0) {
|
||||
err("==>failed to submit rx interrupt (%d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
dbg("rx interrupt is now operational.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int carlusb_initialize_rxrings(struct carlu *ar)
|
||||
{
|
||||
struct frame *tmp;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ar->rx_ring); i++) {
|
||||
tmp = frame_alloc(ar->rx_max);
|
||||
if (tmp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
tmp->dev = (void *) ar;
|
||||
|
||||
ar->rx_ring[i] = libusb_alloc_transfer(0);
|
||||
if (ar->rx_ring[i] == NULL) {
|
||||
frame_free(tmp);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
libusb_fill_bulk_transfer(ar->rx_ring[i], ar->dev,
|
||||
AR9170_EP_RX, (unsigned char *)tmp->data,
|
||||
ar->rx_max, carlusb_rx_bulk_cb, (void *)tmp, 0);
|
||||
|
||||
err = libusb_submit_transfer(ar->rx_ring[i]);
|
||||
if (err != 0) {
|
||||
err("==>failed to submit rx buld urb (%d)\n", err);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
dbg("rx ring is now ready to receive.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int carlusb_load_firmware(struct carlu *ar)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
dbg("loading firmware...\n");
|
||||
|
||||
ar->fw = carlfw_load(CARL9170_FIRMWARE_FILE);
|
||||
if (IS_ERR_OR_NULL(ar->fw))
|
||||
return PTR_ERR(ar->fw);
|
||||
|
||||
ret = carlu_fw_check(ar);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = carlusb_fw_check(ar);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int carlusb_upload_firmware(struct carlu *ar, bool boot)
|
||||
{
|
||||
uint32_t addr = 0x200000;
|
||||
size_t len;
|
||||
void *buf;
|
||||
int ret = 0;
|
||||
|
||||
dbg("initiating firmware image upload procedure.\n");
|
||||
|
||||
buf = carlfw_get_fw(ar->fw, &len);
|
||||
if (IS_ERR_OR_NULL(buf))
|
||||
return PTR_ERR(buf);
|
||||
|
||||
if (ar->miniboot_size) {
|
||||
dbg("Miniboot firmware size:%d\n", ar->miniboot_size);
|
||||
len -= ar->miniboot_size;
|
||||
buf += ar->miniboot_size;
|
||||
}
|
||||
|
||||
while (len) {
|
||||
int blocklen = len > 4096 ? 4096 : len;
|
||||
|
||||
ret = libusb_control_transfer(ar->dev, 0x40, 0x30, addr >> 8, 0, buf, blocklen, 1000);
|
||||
if (ret != blocklen && ret != LIBUSB_ERROR_TIMEOUT) {
|
||||
err("==>firmware upload failed (%d)\n", ret);
|
||||
return -EXIT_FAILURE;
|
||||
}
|
||||
|
||||
dbg("uploaded %d bytes to start address 0x%04x.\n", blocklen, addr);
|
||||
|
||||
buf += blocklen;
|
||||
addr += blocklen;
|
||||
len -= blocklen;
|
||||
}
|
||||
|
||||
if (boot) {
|
||||
ret = libusb_control_transfer(ar->dev, 0x40, 0x31, 0, 0, NULL, 0, 5000);
|
||||
if (ret < 0) {
|
||||
err("unable to boot firmware (%d)\n", ret);
|
||||
return -EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* give the firmware some time to reset & reboot */
|
||||
SDL_Delay(100);
|
||||
|
||||
/*
|
||||
* since the device did a full usb reset,
|
||||
* we have to get a new "dev".
|
||||
*/
|
||||
libusb_release_interface(ar->dev, 0);
|
||||
libusb_close(ar->dev);
|
||||
ar->dev = NULL;
|
||||
list_del(&ar->dev_list);
|
||||
|
||||
ret = carlusb_get_dev(ar, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int carlusb_cmd_async(struct carlu *ar, struct carl9170_cmd *cmd,
|
||||
const bool free_buf)
|
||||
{
|
||||
struct libusb_transfer *urb;
|
||||
int ret, send;
|
||||
|
||||
if (cmd->hdr.len > (CARL9170_MAX_CMD_LEN - 4)) {
|
||||
err("|-> too much payload\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cmd->hdr.len % 4) {
|
||||
err("|-> invalid command length\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = libusb_interrupt_transfer(ar->dev, AR9170_EP_CMD, (void *) cmd, cmd->hdr.len + 4, &send, 32);
|
||||
if (ret != 0) {
|
||||
err("OID:0x%.2x failed due to (%d) (%d).\n", cmd->hdr.cmd, ret, send);
|
||||
print_hex_dump_bytes(ERROR, "CMD:", cmd, cmd->hdr.len);
|
||||
}
|
||||
|
||||
out:
|
||||
if (free_buf)
|
||||
free((void *)cmd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int carlusb_cmd(struct carlu *ar, uint8_t oid,
|
||||
uint8_t *cmd, size_t clen,
|
||||
uint8_t *rsp, size_t rlen)
|
||||
{
|
||||
int ret, send;
|
||||
|
||||
if (clen > (CARL9170_MAX_CMD_LEN - 4)) {
|
||||
err("|-> OID:0x%.2x has too much payload (%d octs)\n", oid, (int)clen);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = SDL_mutexP(ar->resp_lock);
|
||||
if (ret != 0) {
|
||||
err("failed to acquire resp_lock.\n");
|
||||
print_hex_dump_bytes(ERROR, "CMD:", ar->cmd.buf, clen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ar->cmd.cmd.hdr.len = clen;
|
||||
ar->cmd.cmd.hdr.cmd = oid;
|
||||
/* buf[2] & buf[3] are padding */
|
||||
if (clen && cmd != (uint8_t *)(&ar->cmd.cmd.data))
|
||||
memcpy(&ar->cmd.cmd.data, cmd, clen);
|
||||
|
||||
ar->resp_buf = (uint8_t *)rsp;
|
||||
ar->resp_len = rlen;
|
||||
|
||||
ret = carlusb_cmd_async(ar, &ar->cmd.cmd, false);
|
||||
if (ret != 0) {
|
||||
err("OID:0x%.2x failed due to (%d) (%d).\n", oid, ret, send);
|
||||
print_hex_dump_bytes(ERROR, "CMD:", ar->cmd.buf, clen);
|
||||
SDL_mutexV(ar->resp_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = SDL_CondWaitTimeout(ar->resp_pend, ar->resp_lock, 100);
|
||||
if (ret != 0) {
|
||||
err("|-> OID:0x%.2x timed out %d.\n", oid, ret);
|
||||
ar->resp_buf = NULL;
|
||||
ar->resp_len = 0;
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
SDL_mutexV(ar->resp_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct carlu *carlusb_probe(void)
|
||||
{
|
||||
struct carlu *ar;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
ar = carlusb_open();
|
||||
if (IS_ERR_OR_NULL(ar)) {
|
||||
if (IS_ERR(ar))
|
||||
ret = PTR_ERR(ar);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ret = carlusb_show_devinfo(ar);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
ret = carlusb_load_firmware(ar);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
ret = pipe(ar->event_pipe);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
ar->stop_event_polling = false;
|
||||
ar->event_thread = SDL_CreateThread(carlusb_event_thread, ar);
|
||||
|
||||
ret = carlusb_upload_firmware(ar, true);
|
||||
if (ret)
|
||||
goto err_kill;
|
||||
|
||||
ret = carlusb_initialize_rxirq(ar);
|
||||
if (ret)
|
||||
goto err_kill;
|
||||
|
||||
ret = carlusb_initialize_rxrings(ar);
|
||||
if (ret)
|
||||
goto err_kill;
|
||||
|
||||
ret = carlu_cmd_echo(ar, 0x44110dee);
|
||||
if (ret) {
|
||||
err("echo test failed...\n");
|
||||
goto err_kill;
|
||||
}
|
||||
|
||||
info("firmware is active and running.\n");
|
||||
|
||||
carlu_fw_info(ar);
|
||||
|
||||
return ar;
|
||||
|
||||
err_kill:
|
||||
carlusb_destroy(ar);
|
||||
|
||||
err_out:
|
||||
carlusb_free_driver(ar);
|
||||
err("usb device rendezvous failed (%d).\n", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void carlusb_close(struct carlu *ar)
|
||||
{
|
||||
carlu_cmd_reboot(ar);
|
||||
|
||||
carlusb_destroy(ar);
|
||||
carlusb_free_driver(ar);
|
||||
}
|
||||
|
||||
int carlusb_print_known_devices(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
debug_level = INFO;
|
||||
|
||||
info("==> dumping known device list <==\n");
|
||||
for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
|
||||
info("Vendor:\"%-9s\" Product:\"%-26s\" => USBID:[0x%04x:0x%04x]\n",
|
||||
dev_list[i].vendor_name, dev_list[i].product_name,
|
||||
dev_list[i].idVendor, dev_list[i].idProduct);
|
||||
}
|
||||
info("==> end of device list <==\n");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int usb_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = libusb_init(&usb_ctx);
|
||||
if (ret != 0) {
|
||||
err("failed to initialize usb subsystem (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* like a silent chatterbox! */
|
||||
libusb_set_debug(usb_ctx, 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_exit(void)
|
||||
{
|
||||
libusb_exit(usb_ctx);
|
||||
}
|
||||
68
carl9170fw/tools/carlu/src/usb.h
Normal file
68
carl9170fw/tools/carlu/src/usb.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* carlu - userspace testing utility for ar9170 devices
|
||||
*
|
||||
* USB back-end 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_USB_H
|
||||
#define __CARL9170USER_USB_H
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SDL_thread.h"
|
||||
#include "libusb.h"
|
||||
#include "frame.h"
|
||||
#include "list.h"
|
||||
|
||||
#include "fwcmd.h"
|
||||
#include <unistd.h>
|
||||
#include "carlu.h"
|
||||
|
||||
#define AR9170_RX_BULK_BUFS 16
|
||||
#define AR9170_RX_BULK_BUF_SIZE 8192
|
||||
#define AR9170_RX_BULK_IRQ_SIZE 64
|
||||
|
||||
/* endpoints */
|
||||
#define AR9170_EP_TX (LIBUSB_ENDPOINT_OUT | AR9170_USB_EP_TX)
|
||||
#define AR9170_EP_RX (LIBUSB_ENDPOINT_IN | AR9170_USB_EP_RX)
|
||||
#define AR9170_EP_IRQ (LIBUSB_ENDPOINT_IN | AR9170_USB_EP_IRQ)
|
||||
#define AR9170_EP_CMD (LIBUSB_ENDPOINT_OUT | AR9170_USB_EP_CMD)
|
||||
|
||||
#define AR9170_TX_MAX_ACTIVE_URBS 8
|
||||
|
||||
#define CARL9170_FIRMWARE_FILE (CARLU_PATH "/../../carlfw/carl9170.fw")
|
||||
|
||||
struct carlu;
|
||||
|
||||
void carlusb_reset_txep(struct carlu *ar);
|
||||
|
||||
int usb_init(void);
|
||||
void usb_exit(void);
|
||||
|
||||
struct carlu *carlusb_probe(void);
|
||||
void carlusb_close(struct carlu *ar);
|
||||
|
||||
void carlusb_tx(struct carlu *ar, struct frame *frame);
|
||||
int carlusb_fw_check(struct carlu *ar);
|
||||
|
||||
int carlusb_cmd(struct carlu *_ar, uint8_t oid, uint8_t *cmd, size_t clen,
|
||||
uint8_t *rsp, size_t rlen);
|
||||
|
||||
int carlusb_print_known_devices(void);
|
||||
|
||||
#endif /* __CARL9170USER_USB_H */
|
||||
401
carl9170fw/tools/include/frame.h
Normal file
401
carl9170fw/tools/include/frame.h
Normal file
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* 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 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; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Most ideas and some code are copied from the linux' kernels
|
||||
* include/linux/skbuff.h
|
||||
*/
|
||||
|
||||
#ifndef __TOOLS_FRAME_H
|
||||
#define __TOOLS_FRAME_H
|
||||
|
||||
#include "SDL.h"
|
||||
#include "list.h"
|
||||
|
||||
/**
|
||||
* struct frame_queue - sk_buff_head like frame queue
|
||||
*
|
||||
* @list: pointer to head and tail
|
||||
* @lock: mutex lock for serialize access
|
||||
* @len: exact number of queued frames
|
||||
*/
|
||||
|
||||
struct frame_queue {
|
||||
struct list_head list;
|
||||
SDL_mutex *lock;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct frame - frame data structure (like sk_buff)
|
||||
*
|
||||
* @list: storage for double-linked &struct frame_queue list
|
||||
* @dev: pointer to private device/driver structure
|
||||
* @timestamp: space for the current timestamp
|
||||
* @cb: private driver data
|
||||
* @dcb: more reserved space for low-level / backend driver
|
||||
* @queue: selected frame queue / priority
|
||||
* @ref: reference counter
|
||||
* @alloced: maximum available space
|
||||
* @total_len: currently consumed and reserved memory
|
||||
* @len: current frame length
|
||||
* @head: points to the buffer head
|
||||
* @data: current frame data pointer
|
||||
* @tail: frame data tail pointer
|
||||
* @payload: frame data storage container
|
||||
*/
|
||||
|
||||
struct frame {
|
||||
struct list_head list;
|
||||
void *dev;
|
||||
unsigned long timestamp;
|
||||
uint8_t cb[64];
|
||||
union {
|
||||
struct list_head list;
|
||||
uint8_t raw_data[32];
|
||||
} dcb;
|
||||
|
||||
unsigned int queue;
|
||||
unsigned int ref;
|
||||
unsigned int alloced;
|
||||
unsigned int total_len;
|
||||
unsigned int len;
|
||||
uint8_t *head;
|
||||
uint8_t *data;
|
||||
uint8_t *tail;
|
||||
|
||||
/* payload must be the last entry */
|
||||
uint8_t payload[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* frame_put - append more data to &struct frame
|
||||
*
|
||||
* Allocate @size bytes from &struct frame tail and
|
||||
* returns a pointer to the requested location.
|
||||
*
|
||||
* @frame: frame to alter
|
||||
* @size: extra size
|
||||
*/
|
||||
static inline void *frame_put(struct frame *frame, unsigned int size)
|
||||
{
|
||||
void *tmp;
|
||||
|
||||
BUG_ON(frame->total_len + size > frame->alloced);
|
||||
|
||||
frame->len += size;
|
||||
frame->total_len += size;
|
||||
|
||||
tmp = (void *) frame->tail;
|
||||
frame->tail += size;
|
||||
|
||||
BUG_ON(frame->tail > (frame->payload + frame->alloced));
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_push - allocate head
|
||||
*
|
||||
* returns a pointer to a newly allocate area at &struct frame head.
|
||||
*
|
||||
* @frame: frame to modify
|
||||
* @size: requested extra size
|
||||
*/
|
||||
static inline void *frame_push(struct frame *frame, unsigned int size)
|
||||
{
|
||||
frame->len += size;
|
||||
frame->data -= size;
|
||||
|
||||
BUG_ON(frame->data < frame->payload);
|
||||
return frame->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_get - reference frame buffer
|
||||
*
|
||||
* grab a reference from the frame buffer, in order to
|
||||
* prevent it from being freed prematurely by a different user.
|
||||
*
|
||||
* @frame: frame pointer
|
||||
*/
|
||||
static inline struct frame *frame_get(struct frame *frame)
|
||||
{
|
||||
frame->ref++;
|
||||
return frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_pull - remove space from &struct frame head
|
||||
*
|
||||
* Does the opposite of frame_push() and removes freed-up
|
||||
* space at the frames's head.
|
||||
*
|
||||
* @frame: pointer to frame structure
|
||||
* @size: bytes to remove from head
|
||||
*/
|
||||
static inline void *frame_pull(struct frame *frame, unsigned int size)
|
||||
{
|
||||
BUG_ON(frame->len < size);
|
||||
|
||||
frame->len -= size;
|
||||
frame->total_len -= size;
|
||||
frame->data += size;
|
||||
|
||||
return frame->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_reserve - reserve frame headroom
|
||||
*
|
||||
* Reserve a certain amount of space to allow headroom manipulations
|
||||
* in the future.
|
||||
*
|
||||
* @frame: frame to adjust
|
||||
* @size: bytes to reserve
|
||||
*/
|
||||
static inline void frame_reserve(struct frame *frame, unsigned int size)
|
||||
{
|
||||
BUG_ON(frame->total_len + size > frame->alloced);
|
||||
BUG_ON(frame->len != 0);
|
||||
|
||||
frame->total_len += size;
|
||||
frame->data += size;
|
||||
frame->tail += size;
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_trim - set frame length
|
||||
*
|
||||
* cut the frame to @size length.
|
||||
*
|
||||
* @frame: frame to be trimmed
|
||||
* @size: new length
|
||||
*/
|
||||
static inline void frame_trim(struct frame *frame, unsigned int size)
|
||||
{
|
||||
BUG_ON(size > frame->total_len);
|
||||
|
||||
frame->len = size;
|
||||
frame->total_len = size;
|
||||
frame->data = frame->head;
|
||||
frame->tail = frame->head + size;
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_alloc - alloc and initialize new frame
|
||||
*
|
||||
* returns a newly created &struct frame object.
|
||||
*
|
||||
* @size: maximum frame size of the new frame
|
||||
*/
|
||||
static inline struct frame *frame_alloc(unsigned int size)
|
||||
{
|
||||
struct frame *tmp;
|
||||
|
||||
tmp = malloc(size + sizeof(*tmp));
|
||||
if (tmp != NULL) {
|
||||
memset(tmp, 0, sizeof(*tmp));
|
||||
init_list_head(&tmp->list);
|
||||
init_list_head(&tmp->dcb.list);
|
||||
tmp->len = 0;
|
||||
tmp->total_len = 0;
|
||||
tmp->alloced = size;
|
||||
|
||||
tmp->head = tmp->payload;
|
||||
tmp->data = tmp->payload;
|
||||
tmp->tail = tmp->payload;
|
||||
tmp->ref = 1;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_free - unref and free frame
|
||||
*
|
||||
* Unreference frame and free it up, if all users are gone.
|
||||
*
|
||||
* @frame: frame to be freed
|
||||
*/
|
||||
static inline void frame_free(struct frame *frame)
|
||||
{
|
||||
if (!--frame->ref)
|
||||
free(frame);
|
||||
}
|
||||
|
||||
/**
|
||||
* FRAME_WALK - MACRO walker
|
||||
*
|
||||
* Walks over all queued elements in &struct frame_queue
|
||||
*
|
||||
* NOTE: This function is vulnerable in concurrent access
|
||||
* scenarios without proper locking.
|
||||
*
|
||||
* @pos: current position inside the queue
|
||||
* @head: &struct frame_queue head
|
||||
*/
|
||||
#define FRAME_WALK(pos, head) \
|
||||
list_for_each_entry((pos), &(head)->list, list)
|
||||
|
||||
static inline void __frame_queue_init(struct frame_queue *queue)
|
||||
{
|
||||
queue->len = 0;
|
||||
init_list_head(&queue->list);
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_queue_init - initialize frame_queue
|
||||
*
|
||||
* Initialize the given &struct frame_queue object.
|
||||
*
|
||||
* @queue: frame_queue to be initialized
|
||||
*/
|
||||
static inline void frame_queue_init(struct frame_queue *queue)
|
||||
{
|
||||
queue->lock = SDL_CreateMutex();
|
||||
__frame_queue_init(queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_queue_len - returns number of queue elements
|
||||
*
|
||||
* @queue: frame_queue object
|
||||
*/
|
||||
static inline unsigned int frame_queue_len(struct frame_queue *queue)
|
||||
{
|
||||
return queue->len;
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_queue_empty - returns %TRUE whenever queue is empty
|
||||
*
|
||||
* @queue: frame_queue object
|
||||
*/
|
||||
static inline bool frame_queue_empty(struct frame_queue *queue)
|
||||
{
|
||||
return list_empty(&queue->list);
|
||||
}
|
||||
|
||||
static inline void __frame_queue_head(struct frame_queue *queue, struct frame *frame)
|
||||
{
|
||||
list_add_head(&frame->list, &queue->list);
|
||||
queue->len++;
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_queue_head - queue a frame at the queues head
|
||||
* @queue: queue to use
|
||||
*/
|
||||
static inline void frame_queue_head(struct frame_queue *queue, struct frame *frame)
|
||||
{
|
||||
BUG_ON((SDL_mutexP(queue->lock) != 0));
|
||||
__frame_queue_head(queue, frame);
|
||||
SDL_mutexV(queue->lock);
|
||||
}
|
||||
|
||||
static inline void __frame_queue_tail(struct frame_queue *queue, struct frame *frame)
|
||||
{
|
||||
list_add_tail(&frame->list, &queue->list);
|
||||
queue->len++;
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_queue_head - queue a frame at the queues tail
|
||||
* @queue: queue to use
|
||||
*/
|
||||
static inline void frame_queue_tail(struct frame_queue *queue, struct frame *frame)
|
||||
{
|
||||
BUG_ON((SDL_mutexP(queue->lock) != 0));
|
||||
__frame_queue_tail(queue, frame);
|
||||
SDL_mutexV(queue->lock);
|
||||
}
|
||||
|
||||
static inline void __frame_unlink(struct frame_queue *queue, struct frame *frame)
|
||||
{
|
||||
list_del(&frame->list);
|
||||
queue->len--;
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_queue_unlink - remove a frame from the queue
|
||||
* @queue: queue to use
|
||||
* @frame: frame to remove
|
||||
*/
|
||||
static inline void frame_unlink(struct frame_queue *queue, struct frame *frame)
|
||||
{
|
||||
BUG_ON((SDL_mutexP(queue->lock) != 0));
|
||||
__frame_unlink(queue, frame);
|
||||
SDL_mutexV(queue->lock);
|
||||
}
|
||||
|
||||
|
||||
static inline struct frame *__frame_dequeue(struct frame_queue *queue)
|
||||
{
|
||||
struct frame *tmp = NULL;
|
||||
|
||||
if (!frame_queue_empty(queue)) {
|
||||
tmp = list_entry(queue->list.next, struct frame, list);
|
||||
__frame_unlink(queue, tmp);
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_dequeue - remove frame from the head of the queue
|
||||
*
|
||||
* @queue: queue to dequeue from
|
||||
*/
|
||||
static inline struct frame *frame_dequeue(struct frame_queue *queue)
|
||||
{
|
||||
struct frame *tmp;
|
||||
|
||||
BUG_ON((SDL_mutexP(queue->lock) != 0));
|
||||
|
||||
tmp = __frame_dequeue(queue);
|
||||
SDL_mutexV(queue->lock);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static inline void __frame_queue_purge(struct frame_queue *queue)
|
||||
{
|
||||
while (list_empty(&queue->list) == false)
|
||||
frame_free(__frame_dequeue(queue));
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_queue_purge - frees all queued &struct frame objects
|
||||
*
|
||||
* @queue: queue to be freed
|
||||
*/
|
||||
static inline void frame_queue_purge(struct frame_queue *queue)
|
||||
{
|
||||
BUG_ON((SDL_mutexP(queue->lock) != 0));
|
||||
__frame_queue_purge(queue);
|
||||
SDL_mutexV(queue->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* frame_queue_kill - destroys frame_queue object
|
||||
*
|
||||
* Destroy object and frees up all remaining elements
|
||||
*
|
||||
* @queue: frame_queue victim
|
||||
*/
|
||||
static inline void frame_queue_kill(struct frame_queue *queue)
|
||||
{
|
||||
SDL_DestroyMutex(queue->lock);
|
||||
__frame_queue_purge(queue);
|
||||
}
|
||||
|
||||
#endif /* __TOOLS_FRAME_H */
|
||||
92
carl9170fw/tools/include/list.h
Normal file
92
carl9170fw/tools/include/list.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* list.h List Utilities
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
|
||||
*/
|
||||
|
||||
#ifndef __LIST_H
|
||||
#define __LIST_H
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next;
|
||||
struct list_head *prev;
|
||||
};
|
||||
|
||||
static inline void list_add(struct list_head *obj,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
prev->next = obj;
|
||||
obj->prev = prev;
|
||||
next->prev = obj;
|
||||
obj->next = next;
|
||||
}
|
||||
|
||||
static inline void list_add_tail(struct list_head *obj,
|
||||
struct list_head *head)
|
||||
{
|
||||
list_add(obj, head->prev, head);
|
||||
}
|
||||
|
||||
static inline void list_add_head(struct list_head *obj,
|
||||
struct list_head *head)
|
||||
{
|
||||
list_add(obj, head, head->next);
|
||||
}
|
||||
|
||||
static inline void list_del(struct list_head *obj)
|
||||
{
|
||||
obj->prev->next = obj->next;
|
||||
obj->next->prev = obj->prev;
|
||||
obj->next = obj->prev = obj;
|
||||
}
|
||||
|
||||
static inline void list_replace(struct list_head *old,
|
||||
struct list_head *obj)
|
||||
{
|
||||
obj->next = old->next;
|
||||
obj->next->prev = obj;
|
||||
obj->prev = old->prev;
|
||||
obj->prev->next = obj;
|
||||
}
|
||||
|
||||
static inline int list_empty(struct list_head *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
#define list_entry(ptr, type, member) \
|
||||
container_of(ptr, type, member)
|
||||
|
||||
#define list_first_entry(ptr, type, member) \
|
||||
container_of((ptr)->next, type, member)
|
||||
|
||||
#define list_at_tail(pos, head, member) \
|
||||
((pos)->member.next == (head))
|
||||
|
||||
#define list_at_head(pos, head, member) \
|
||||
((pos)->member.prev == (head))
|
||||
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = { &(name), &(name) }
|
||||
|
||||
#define list_for_each_entry(pos, head, member) \
|
||||
for (pos = list_entry((head)->next, typeof(*pos), member); \
|
||||
&(pos)->member != (head); \
|
||||
(pos) = list_entry((pos)->member.next, typeof(*(pos)), member))
|
||||
|
||||
#define list_for_each_entry_safe(pos, n, head, member) \
|
||||
for (pos = list_entry((head)->next, typeof(*pos), member), \
|
||||
n = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&(pos)->member != (head); \
|
||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
||||
|
||||
#define init_list_head(head) \
|
||||
do { (head)->next = (head); (head)->prev = (head); } while (0)
|
||||
|
||||
#endif /* __LIST_H */
|
||||
7
carl9170fw/tools/lib/CMakeLists.txt
Normal file
7
carl9170fw/tools/lib/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
project(libcarlfw)
|
||||
|
||||
set(carlfw_src carlfw.c)
|
||||
|
||||
add_library( carlfw STATIC ${carlfw_src})
|
||||
630
carl9170fw/tools/lib/carlfw.c
Normal file
630
carl9170fw/tools/lib/carlfw.c
Normal file
@@ -0,0 +1,630 @@
|
||||
/*
|
||||
* Copyright 2010-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 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; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <error.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "carlfw.h"
|
||||
|
||||
struct carlfw_file {
|
||||
char *name;
|
||||
size_t len;
|
||||
char *data;
|
||||
};
|
||||
|
||||
struct carlfw {
|
||||
struct carlfw_file fw;
|
||||
struct carlfw_file hdr;
|
||||
|
||||
struct list_head desc_list;
|
||||
unsigned int desc_list_entries,
|
||||
desc_list_len;
|
||||
};
|
||||
|
||||
#define carlfw_walk_descs(iter, fw) \
|
||||
list_for_each_entry(iter, &fw->desc_list, h.list)
|
||||
|
||||
struct carlfw_list_entry_head {
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct carlfw_list_entry {
|
||||
struct carlfw_list_entry_head h;
|
||||
union {
|
||||
struct carl9170fw_desc_head head;
|
||||
uint32_t data[0];
|
||||
char text[0];
|
||||
};
|
||||
};
|
||||
|
||||
static inline struct carlfw_list_entry *carlfw_desc_to_entry(struct carl9170fw_desc_head *head)
|
||||
{
|
||||
return container_of(head, struct carlfw_list_entry, head);
|
||||
}
|
||||
|
||||
static inline struct carl9170fw_desc_head *carlfw_entry_to_desc(struct carlfw_list_entry *entry)
|
||||
{
|
||||
return &entry->head;
|
||||
}
|
||||
|
||||
static void carlfw_entry_unlink(struct carlfw *fw,
|
||||
struct carlfw_list_entry *entry)
|
||||
{
|
||||
fw->desc_list_entries--;
|
||||
fw->desc_list_len -= le16_to_cpu(entry->head.length);
|
||||
list_del(&entry->h.list);
|
||||
}
|
||||
|
||||
static void carlfw_entry_del(struct carlfw *fw,
|
||||
struct carlfw_list_entry *entry)
|
||||
{
|
||||
carlfw_entry_unlink(fw, entry);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
static struct carlfw_list_entry *carlfw_find_entry(struct carlfw *fw,
|
||||
const uint8_t descid[4],
|
||||
unsigned int len,
|
||||
uint8_t compatible_revision)
|
||||
{
|
||||
struct carlfw_list_entry *iter;
|
||||
|
||||
carlfw_walk_descs(iter, fw) {
|
||||
if (carl9170fw_desc_cmp(&iter->head, descid, len,
|
||||
compatible_revision))
|
||||
return (void *)iter;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct carlfw_list_entry *__carlfw_entry_add_prepare(struct carlfw *fw,
|
||||
const struct carl9170fw_desc_head *desc)
|
||||
{
|
||||
struct carlfw_list_entry *tmp;
|
||||
unsigned int len;
|
||||
|
||||
len = le16_to_cpu(desc->length);
|
||||
|
||||
if (len < sizeof(struct carl9170fw_desc_head))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
tmp = malloc(sizeof(*tmp) + len);
|
||||
if (!tmp)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
fw->desc_list_entries++;
|
||||
fw->desc_list_len += len;
|
||||
|
||||
memcpy(tmp->data, desc, len);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void __carlfw_release(struct carlfw_file *f)
|
||||
{
|
||||
f->len = 0;
|
||||
if (f->name)
|
||||
free(f->name);
|
||||
f->name = NULL;
|
||||
|
||||
if (f->data)
|
||||
free(f->data);
|
||||
f->data = NULL;
|
||||
}
|
||||
|
||||
void carlfw_release(struct carlfw *fw)
|
||||
{
|
||||
struct carlfw_list_entry *entry;
|
||||
|
||||
if (!IS_ERR_OR_NULL(fw)) {
|
||||
while (!list_empty(&fw->desc_list)) {
|
||||
entry = list_entry(fw->desc_list.next,
|
||||
struct carlfw_list_entry, h.list);
|
||||
carlfw_entry_del(fw, entry);
|
||||
}
|
||||
|
||||
__carlfw_release(&fw->fw);
|
||||
__carlfw_release(&fw->hdr);
|
||||
free(fw);
|
||||
}
|
||||
}
|
||||
|
||||
static int __carlfw_load(struct carlfw_file *file, const char *name, const char *mode)
|
||||
{
|
||||
struct stat file_stat;
|
||||
FILE *fh;
|
||||
int err;
|
||||
|
||||
fh = fopen(name, mode);
|
||||
if (fh == NULL)
|
||||
return errno ? -errno : -1;
|
||||
|
||||
err = fstat(fileno(fh), &file_stat);
|
||||
if (err)
|
||||
return errno ? -errno : -1;
|
||||
|
||||
file->len = file_stat.st_size;
|
||||
file->data = malloc(file->len);
|
||||
if (file->data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
err = fread(file->data, file->len, 1, fh);
|
||||
if (err != 1)
|
||||
return -ferror(fh);
|
||||
|
||||
file->name = strdup(name);
|
||||
fclose(fh);
|
||||
|
||||
if (!file->name)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *__carlfw_find_desc(struct carlfw_file *file,
|
||||
uint8_t descid[4],
|
||||
unsigned int len,
|
||||
uint8_t compatible_revision)
|
||||
{
|
||||
int scan = file->len, found = 0;
|
||||
struct carl9170fw_desc_head *tmp = NULL;
|
||||
|
||||
while (scan >= 0) {
|
||||
if (file->data[scan] == descid[CARL9170FW_MAGIC_SIZE - found - 1])
|
||||
found++;
|
||||
else
|
||||
found = 0;
|
||||
|
||||
if (found == CARL9170FW_MAGIC_SIZE)
|
||||
break;
|
||||
|
||||
scan--;
|
||||
}
|
||||
|
||||
if (found == CARL9170FW_MAGIC_SIZE) {
|
||||
tmp = (void *) &file->data[scan];
|
||||
|
||||
if (!CHECK_HDR_VERSION(tmp, compatible_revision) &&
|
||||
(le16_to_cpu(tmp->length) >= len))
|
||||
return tmp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *carlfw_find_desc(struct carlfw *fw,
|
||||
const uint8_t descid[4],
|
||||
const unsigned int len,
|
||||
const uint8_t compatible_revision)
|
||||
{
|
||||
struct carlfw_list_entry *tmp;
|
||||
|
||||
tmp = carlfw_find_entry(fw, descid, len, compatible_revision);
|
||||
|
||||
return tmp ? carlfw_entry_to_desc(tmp) : NULL;
|
||||
}
|
||||
|
||||
int carlfw_desc_add_tail(struct carlfw *fw,
|
||||
const struct carl9170fw_desc_head *desc)
|
||||
{
|
||||
struct carlfw_list_entry *tmp;
|
||||
|
||||
tmp = __carlfw_entry_add_prepare(fw, desc);
|
||||
if (IS_ERR(tmp))
|
||||
return PTR_ERR(tmp);
|
||||
|
||||
list_add_tail(&tmp->h.list, &fw->desc_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int carlfw_desc_add(struct carlfw *fw,
|
||||
const struct carl9170fw_desc_head *desc,
|
||||
struct carl9170fw_desc_head *prev,
|
||||
struct carl9170fw_desc_head *next)
|
||||
{
|
||||
struct carlfw_list_entry *tmp;
|
||||
|
||||
tmp = __carlfw_entry_add_prepare(fw, desc);
|
||||
if (IS_ERR(tmp))
|
||||
return PTR_ERR(tmp);
|
||||
|
||||
list_add(&tmp->h.list, &((carlfw_desc_to_entry(prev))->h.list),
|
||||
&((carlfw_desc_to_entry(next))->h.list));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int carlfw_desc_add_before(struct carlfw *fw,
|
||||
const struct carl9170fw_desc_head *desc,
|
||||
struct carl9170fw_desc_head *pos)
|
||||
{
|
||||
struct carl9170fw_desc_head *prev;
|
||||
struct carlfw_list_entry *prev_entry;
|
||||
|
||||
prev_entry = carlfw_desc_to_entry(pos);
|
||||
|
||||
prev = carlfw_entry_to_desc((struct carlfw_list_entry *) prev_entry->h.list.prev);
|
||||
|
||||
return carlfw_desc_add(fw, desc, prev, pos);
|
||||
}
|
||||
|
||||
void carlfw_desc_unlink(struct carlfw *fw,
|
||||
struct carl9170fw_desc_head *desc)
|
||||
{
|
||||
carlfw_entry_unlink(fw, carlfw_desc_to_entry(desc));
|
||||
}
|
||||
|
||||
void carlfw_desc_del(struct carlfw *fw,
|
||||
struct carl9170fw_desc_head *desc)
|
||||
{
|
||||
carlfw_entry_del(fw, carlfw_desc_to_entry(desc));
|
||||
}
|
||||
|
||||
void *carlfw_desc_mod_len(struct carlfw *fw __unused,
|
||||
struct carl9170fw_desc_head *desc, size_t len)
|
||||
{
|
||||
struct carlfw_list_entry *obj, tmp;
|
||||
int new_len = le16_to_cpu(desc->length) + len;
|
||||
|
||||
if (new_len < (int)sizeof(*desc))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (new_len > CARL9170FW_DESC_MAX_LENGTH)
|
||||
return ERR_PTR(-E2BIG);
|
||||
|
||||
obj = carlfw_desc_to_entry(desc);
|
||||
|
||||
memcpy(&tmp, obj, sizeof(tmp));
|
||||
obj = realloc(obj, new_len + sizeof(struct carlfw_list_entry_head));
|
||||
if (obj == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
list_replace(&tmp.h.list, &obj->h.list);
|
||||
|
||||
desc = carlfw_entry_to_desc(obj);
|
||||
desc->length = le16_to_cpu(new_len);
|
||||
fw->desc_list_len += len;
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
void *carlfw_desc_next(struct carlfw *fw,
|
||||
struct carl9170fw_desc_head *pos)
|
||||
{
|
||||
struct carlfw_list_entry *entry;
|
||||
|
||||
if (!pos)
|
||||
entry = (struct carlfw_list_entry *) &fw->desc_list;
|
||||
else
|
||||
entry = carlfw_desc_to_entry(pos);
|
||||
|
||||
if (list_at_tail(entry, &fw->desc_list, h.list))
|
||||
return NULL;
|
||||
|
||||
entry = (struct carlfw_list_entry *) entry->h.list.next;
|
||||
|
||||
return carlfw_entry_to_desc(entry);
|
||||
}
|
||||
|
||||
static int carlfw_parse_descs(struct carlfw *fw,
|
||||
struct carl9170fw_otus_desc *otus_desc)
|
||||
{
|
||||
const struct carl9170fw_desc_head *iter = NULL;
|
||||
int err;
|
||||
|
||||
carl9170fw_for_each_hdr(iter, &otus_desc->head) {
|
||||
err = carlfw_desc_add_tail(fw, iter);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
/* LAST is added automatically by carlfw_store */
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
#define CRCPOLY_LE 0xedb88320
|
||||
|
||||
/* copied from the linux kernel */
|
||||
static uint32_t crc32_le(uint32_t crc, unsigned char const *p, size_t len)
|
||||
{
|
||||
int i;
|
||||
while (len--) {
|
||||
crc ^= *p++;
|
||||
for (i = 0; i < 8; i++)
|
||||
crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0);
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
#else
|
||||
#error "this tool does not work with a big endian host yet!"
|
||||
#endif
|
||||
|
||||
static int carlfw_check_crc32s(struct carlfw *fw)
|
||||
{
|
||||
struct carl9170fw_chk_desc *chk_desc;
|
||||
struct carlfw_list_entry *iter;
|
||||
unsigned int elen;
|
||||
uint32_t crc32;
|
||||
|
||||
chk_desc = carlfw_find_desc(fw, (uint8_t *) CHK_MAGIC,
|
||||
sizeof(*chk_desc),
|
||||
CARL9170FW_CHK_DESC_CUR_VER);
|
||||
if (!chk_desc)
|
||||
return -ENODATA;
|
||||
|
||||
crc32 = crc32_le(~0, (void *) fw->fw.data, fw->fw.len);
|
||||
if (crc32 != le32_to_cpu(chk_desc->fw_crc32))
|
||||
return -EINVAL;
|
||||
|
||||
carlfw_walk_descs(iter, fw) {
|
||||
elen = le16_to_cpu(iter->head.length);
|
||||
|
||||
if (carl9170fw_desc_cmp(&iter->head, (uint8_t *) CHK_MAGIC,
|
||||
sizeof(*chk_desc),
|
||||
CARL9170FW_CHK_DESC_CUR_VER))
|
||||
continue;
|
||||
|
||||
crc32 = crc32_le(crc32, (void *) &iter->head, elen);
|
||||
}
|
||||
|
||||
if (crc32 != le32_to_cpu(chk_desc->hdr_crc32))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct carlfw *carlfw_load(const char *basename)
|
||||
{
|
||||
char filename[256];
|
||||
struct carlfw *fw;
|
||||
struct carl9170fw_otus_desc *otus_desc;
|
||||
struct carl9170fw_last_desc *last_desc;
|
||||
struct carlfw_file *hdr_file;
|
||||
unsigned long fin, diff, off, rem;
|
||||
int err;
|
||||
|
||||
fw = calloc(1, sizeof(*fw));
|
||||
if (!fw)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init_list_head(&fw->desc_list);
|
||||
|
||||
err = __carlfw_load(&fw->fw, basename, "r");
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
if (fw->hdr.name)
|
||||
hdr_file = &fw->hdr;
|
||||
else
|
||||
hdr_file = &fw->fw;
|
||||
|
||||
otus_desc = __carlfw_find_desc(hdr_file, (uint8_t *) OTUS_MAGIC,
|
||||
sizeof(*otus_desc),
|
||||
CARL9170FW_OTUS_DESC_CUR_VER);
|
||||
last_desc = __carlfw_find_desc(hdr_file, (uint8_t *) LAST_MAGIC,
|
||||
sizeof(*last_desc),
|
||||
CARL9170FW_LAST_DESC_CUR_VER);
|
||||
|
||||
if (!otus_desc || !last_desc ||
|
||||
(unsigned long) otus_desc > (unsigned long) last_desc) {
|
||||
err = -ENODATA;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
err = carlfw_parse_descs(fw, otus_desc);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
fin = (unsigned long)last_desc + sizeof(*last_desc);
|
||||
diff = fin - (unsigned long)otus_desc;
|
||||
rem = hdr_file->len - (fin - (unsigned long) hdr_file->data);
|
||||
|
||||
if (rem) {
|
||||
off = (unsigned long)otus_desc - (unsigned long)hdr_file->data;
|
||||
memmove(&hdr_file->data[off],
|
||||
((uint8_t *)last_desc) + sizeof(*last_desc), rem);
|
||||
}
|
||||
|
||||
hdr_file->len -= diff;
|
||||
hdr_file->data = realloc(hdr_file->data, hdr_file->len);
|
||||
if (!hdr_file->data && hdr_file->len) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
err = carlfw_check_crc32s(fw);
|
||||
if (err && err != -ENODATA)
|
||||
goto err_out;
|
||||
|
||||
return fw;
|
||||
|
||||
err_out:
|
||||
carlfw_release(fw);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int carlfw_apply_checksums(struct carlfw *fw)
|
||||
{
|
||||
struct carlfw_list_entry *iter;
|
||||
struct carl9170fw_chk_desc tmp = {
|
||||
CARL9170FW_FILL_DESC(CHK_MAGIC, sizeof(tmp),
|
||||
CARL9170FW_CHK_DESC_MIN_VER,
|
||||
CARL9170FW_CHK_DESC_CUR_VER) };
|
||||
struct carl9170fw_chk_desc *chk_desc = NULL;
|
||||
int err = 0;
|
||||
unsigned int len = 0, elen, max_len;
|
||||
uint32_t crc32;
|
||||
|
||||
chk_desc = carlfw_find_desc(fw, (uint8_t *) CHK_MAGIC,
|
||||
sizeof(*chk_desc),
|
||||
CARL9170FW_CHK_DESC_CUR_VER);
|
||||
if (chk_desc) {
|
||||
carlfw_desc_del(fw, &chk_desc->head);
|
||||
chk_desc = NULL;
|
||||
}
|
||||
|
||||
max_len = fw->desc_list_len;
|
||||
|
||||
crc32 = crc32_le(~0, (void *) fw->fw.data, fw->fw.len);
|
||||
tmp.fw_crc32 = cpu_to_le32(crc32);
|
||||
|
||||
/*
|
||||
* NOTE:
|
||||
*
|
||||
* The descriptor checksum is seeded with the firmware's crc32.
|
||||
* This neat trick ensures that the driver can check whenever
|
||||
* descriptor actually belongs to the firmware, or not.
|
||||
*/
|
||||
|
||||
carlfw_walk_descs(iter, fw) {
|
||||
elen = le16_to_cpu(iter->head.length);
|
||||
|
||||
if (max_len < len + elen)
|
||||
return -EMSGSIZE;
|
||||
|
||||
crc32 = crc32_le(crc32, (void *) &iter->head, elen);
|
||||
len += elen;
|
||||
}
|
||||
|
||||
tmp.hdr_crc32 = cpu_to_le32(crc32);
|
||||
|
||||
err = carlfw_desc_add_tail(fw, &tmp.head);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int carlfw_store(struct carlfw *fw)
|
||||
{
|
||||
struct carl9170fw_last_desc last_desc = {
|
||||
CARL9170FW_FILL_DESC(LAST_MAGIC, sizeof(last_desc),
|
||||
CARL9170FW_LAST_DESC_MIN_VER,
|
||||
CARL9170FW_LAST_DESC_CUR_VER) };
|
||||
|
||||
struct carlfw_list_entry *iter;
|
||||
FILE *fh;
|
||||
int err, elen;
|
||||
|
||||
err = carlfw_apply_checksums(fw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
fh = fopen(fw->fw.name, "w");
|
||||
if (!fh)
|
||||
return -errno;
|
||||
|
||||
err = fwrite(fw->fw.data, fw->fw.len, 1, fh);
|
||||
if (err != 1) {
|
||||
err = -errno;
|
||||
goto close_out;
|
||||
}
|
||||
|
||||
if (fw->hdr.name) {
|
||||
fclose(fh);
|
||||
|
||||
fh = fopen(fw->hdr.name, "w");
|
||||
}
|
||||
|
||||
carlfw_walk_descs(iter, fw) {
|
||||
elen = le16_to_cpu(iter->head.length);
|
||||
|
||||
if (elen > CARL9170FW_DESC_MAX_LENGTH) {
|
||||
err = -E2BIG;
|
||||
goto close_out;
|
||||
}
|
||||
|
||||
err = fwrite(iter->data, elen, 1, fh);
|
||||
if (err != 1) {
|
||||
err = -ferror(fh);
|
||||
goto close_out;
|
||||
}
|
||||
}
|
||||
|
||||
err = fwrite(&last_desc, sizeof(last_desc), 1, fh);
|
||||
if (err != 1) {
|
||||
err = -ferror(fh);
|
||||
goto close_out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
close_out:
|
||||
fclose(fh);
|
||||
return err;
|
||||
}
|
||||
|
||||
void *carlfw_mod_tailroom(struct carlfw *fw, ssize_t len)
|
||||
{
|
||||
size_t new_len;
|
||||
void *buf;
|
||||
|
||||
new_len = fw->fw.len + len;
|
||||
|
||||
if (!carl9170fw_size_check(new_len))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
buf = realloc(fw->fw.data, new_len);
|
||||
if (buf == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
fw->fw.len = new_len;
|
||||
fw->fw.data = buf;
|
||||
return &fw->fw.data[new_len - len];
|
||||
}
|
||||
|
||||
void *carlfw_mod_headroom(struct carlfw *fw, ssize_t len)
|
||||
{
|
||||
size_t new_len;
|
||||
void *ptr;
|
||||
|
||||
new_len = fw->fw.len + len;
|
||||
if (!carl9170fw_size_check(new_len))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (len < 0)
|
||||
memmove(fw->fw.data, &fw->fw.data[len], new_len);
|
||||
|
||||
ptr = carlfw_mod_tailroom(fw, len);
|
||||
if (IS_ERR_OR_NULL(ptr))
|
||||
return ptr;
|
||||
|
||||
if (len > 0)
|
||||
memmove(&fw->fw.data[len], &fw->fw.data[0], new_len - len);
|
||||
|
||||
return fw->fw.data;
|
||||
}
|
||||
|
||||
void *carlfw_get_fw(struct carlfw *fw, size_t *len)
|
||||
{
|
||||
*len = fw->fw.len;
|
||||
return fw->fw.data;
|
||||
}
|
||||
|
||||
unsigned int carlfw_get_descs_num(struct carlfw *fw)
|
||||
{
|
||||
return fw->desc_list_entries;
|
||||
}
|
||||
|
||||
unsigned int carlfw_get_descs_size(struct carlfw *fw)
|
||||
{
|
||||
return fw->desc_list_len;
|
||||
}
|
||||
67
carl9170fw/tools/lib/carlfw.h
Normal file
67
carl9170fw/tools/lib/carlfw.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2010-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 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; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef __CARLFW_H
|
||||
#define __CARLFW_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include "compiler.h"
|
||||
#include "fwdesc.h"
|
||||
#include "list.h"
|
||||
|
||||
struct carlfw;
|
||||
|
||||
void carlfw_release(struct carlfw *fw);
|
||||
struct carlfw *carlfw_load(const char *basename);
|
||||
int carlfw_store(struct carlfw *fw);
|
||||
void *carlfw_find_desc(struct carlfw *fw,
|
||||
const uint8_t descid[4], const unsigned int len,
|
||||
const uint8_t compatible_revision);
|
||||
|
||||
int carlfw_desc_add_tail(struct carlfw *fw,
|
||||
const struct carl9170fw_desc_head *desc);
|
||||
|
||||
int carlfw_desc_add(struct carlfw *fw,
|
||||
const struct carl9170fw_desc_head *desc,
|
||||
struct carl9170fw_desc_head *prev,
|
||||
struct carl9170fw_desc_head *next);
|
||||
|
||||
void *carlfw_desc_mod_len(struct carlfw *fw,
|
||||
struct carl9170fw_desc_head *desc,
|
||||
size_t len);
|
||||
|
||||
int carlfw_desc_add_before(struct carlfw *fw,
|
||||
const struct carl9170fw_desc_head *desc,
|
||||
struct carl9170fw_desc_head *pos);
|
||||
|
||||
void carlfw_desc_unlink(struct carlfw *fw,
|
||||
struct carl9170fw_desc_head *desc);
|
||||
|
||||
void carlfw_desc_del(struct carlfw *fw,
|
||||
struct carl9170fw_desc_head *entry);
|
||||
|
||||
void *carlfw_desc_next(struct carlfw *fw,
|
||||
struct carl9170fw_desc_head *pos);
|
||||
|
||||
void *carlfw_mod_tailroom(struct carlfw *fw, ssize_t len);
|
||||
void *carlfw_mod_headroom(struct carlfw *fw, ssize_t len);
|
||||
|
||||
void *carlfw_get_fw(struct carlfw *fw, size_t *len);
|
||||
|
||||
unsigned int carlfw_get_descs_num(struct carlfw *fw);
|
||||
unsigned int carlfw_get_descs_size(struct carlfw *fw);
|
||||
#endif /* __CARLFW_H */
|
||||
13
carl9170fw/tools/src/CMakeLists.txt
Normal file
13
carl9170fw/tools/src/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
project(tools)
|
||||
|
||||
add_custom_target(wol ALL COMMAND gcc wol.c -o wol)
|
||||
|
||||
set(tools fwinfo miniboot checksum eeprom_fix)
|
||||
|
||||
foreach(tool ${tools})
|
||||
add_executable( ${tool} ${tool}.c )
|
||||
add_definitions("-std=gnu99 -Wall -Wextra -Wshadow")
|
||||
target_link_libraries( ${tool} carlfw )
|
||||
endforeach()
|
||||
89
carl9170fw/tools/src/checksum.c
Normal file
89
carl9170fw/tools/src/checksum.c
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2010-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 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; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <error.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "carlfw.h"
|
||||
|
||||
#include "compiler.h"
|
||||
|
||||
static void checksum_help(void)
|
||||
{
|
||||
fprintf(stderr, "Usage:\n");
|
||||
fprintf(stderr, "\tchecksum FW-FILE\n");
|
||||
|
||||
fprintf(stderr, "\nDescription:\n");
|
||||
fprintf(stderr, "\tThis simple utility adds/updates various "
|
||||
"checksums.\n");
|
||||
|
||||
fprintf(stderr, "\nParameteres:\n");
|
||||
fprintf(stderr, "\t 'FW-FILE' = firmware name\n");
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *args[])
|
||||
{
|
||||
struct carlfw *fw = NULL;
|
||||
int err = 0;
|
||||
|
||||
if (argc != 2) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fw = carlfw_load(args[1]);
|
||||
if (IS_ERR_OR_NULL(fw)) {
|
||||
err = PTR_ERR(fw);
|
||||
fprintf(stderr, "Failed to open file \"%s\" (%d).\n",
|
||||
args[1], err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* No magic here, The checksum descriptor is added/update
|
||||
* automatically in a subroutine of carlfw_store().
|
||||
*
|
||||
* This tools serves as a skeleton/example.
|
||||
*/
|
||||
err = carlfw_store(fw);
|
||||
if (err) {
|
||||
fprintf(stderr, "Failed to apply checksum (%d).\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
switch (err) {
|
||||
case 0:
|
||||
fprintf(stdout, "checksum applied.\n");
|
||||
break;
|
||||
case -EINVAL:
|
||||
checksum_help();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
carlfw_release(fw);
|
||||
return err ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
326
carl9170fw/tools/src/eeprom_fix.c
Normal file
326
carl9170fw/tools/src/eeprom_fix.c
Normal file
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* Copyright 2010-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 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; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <error.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "carlfw.h"
|
||||
|
||||
#include "compiler.h"
|
||||
|
||||
static int get_val(char *str, unsigned int *val)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = sscanf(str, "%8x", val);
|
||||
if (err != 1)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_addr(char *str, unsigned int *val)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = get_val(str, val);
|
||||
if (*val & 3) {
|
||||
fprintf(stderr, "Address 0x%.8x is not a multiple of 4.\n",
|
||||
*val);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
new_fix_entry(struct carlfw *fw, struct carl9170fw_fix_entry *fix_entry)
|
||||
{
|
||||
struct carl9170fw_fix_desc *fix;
|
||||
unsigned int len;
|
||||
|
||||
len = sizeof(*fix) + sizeof(*fix_entry);
|
||||
fix = malloc(len);
|
||||
if (!fix)
|
||||
return -ENOMEM;
|
||||
|
||||
carl9170fw_fill_desc(&fix->head, (uint8_t *) FIX_MAGIC,
|
||||
cpu_to_le16(len),
|
||||
CARL9170FW_FIX_DESC_MIN_VER,
|
||||
CARL9170FW_FIX_DESC_CUR_VER);
|
||||
|
||||
memcpy(&fix->data[0], fix_entry, sizeof(*fix_entry));
|
||||
|
||||
return carlfw_desc_add_tail(fw, &fix->head);
|
||||
}
|
||||
|
||||
static struct carl9170fw_fix_entry *
|
||||
scan_for_similar_fix(struct carl9170fw_fix_desc *fix, __le32 address)
|
||||
{
|
||||
unsigned int i, entries;
|
||||
|
||||
entries = (le16_to_cpu(fix->head.length) - sizeof(*fix)) /
|
||||
sizeof(struct carl9170fw_fix_entry);
|
||||
|
||||
for (i = 0; i < entries; i++) {
|
||||
if (address == fix->data[i].address)
|
||||
return &fix->data[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
add_another_fix_entry(struct carlfw *fw, struct carl9170fw_fix_desc *fix,
|
||||
struct carl9170fw_fix_entry *fix_entry)
|
||||
{
|
||||
unsigned int entry;
|
||||
|
||||
fix = carlfw_desc_mod_len(fw, &fix->head, sizeof(*fix_entry));
|
||||
if (IS_ERR_OR_NULL(fix))
|
||||
return (int) PTR_ERR(fix);
|
||||
|
||||
entry = (le16_to_cpu(fix->head.length) - sizeof(*fix)) /
|
||||
sizeof(*fix_entry) - 1;
|
||||
|
||||
memcpy(&fix->data[entry], fix_entry, sizeof(*fix_entry));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
update_entry(char option, struct carl9170fw_fix_entry *entry,
|
||||
struct carl9170fw_fix_entry *fix)
|
||||
{
|
||||
switch (option) {
|
||||
case '=':
|
||||
entry->mask = fix->mask;
|
||||
entry->value = fix->value;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
entry->mask |= fix->mask;
|
||||
entry->value |= fix->value;
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
entry->mask &= fix->mask;
|
||||
entry->value &= fix->value;
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Unknown option: '%c'\n", option);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void user_education(void)
|
||||
{
|
||||
fprintf(stderr, "Usage:\n");
|
||||
fprintf(stderr, "\teeprom_fix FW-FILE SWITCH [ADDRESS [VALUE MASK]]\n");
|
||||
|
||||
fprintf(stderr, "\nDescription:\n");
|
||||
fprintf(stderr, "\tThis utility manage a set of overrides which "
|
||||
"commands the driver\n\tto load customized EEPROM' "
|
||||
"data for all specified addresses.\n");
|
||||
|
||||
fprintf(stderr, "\nParameters:\n");
|
||||
fprintf(stderr, "\t'FW-FILE' = firmware file [basename]\n");
|
||||
fprintf(stderr, "\t'SWITCH' = [=|d|D]\n");
|
||||
fprintf(stderr, "\t | '=' => add/set value for address\n");
|
||||
fprintf(stderr, "\t | 'D' => removes all EEPROM overrides\n");
|
||||
fprintf(stderr, "\t * 'd' => removed override for 'address'\n");
|
||||
fprintf(stderr, "\n\t'ADDRESS' = location of the EEPROM override\n");
|
||||
fprintf(stderr, "\t\t NB: must be a multiple of 4.\n");
|
||||
fprintf(stderr, "\t\t an address map can be found in eeprom.h.\n");
|
||||
fprintf(stderr, "\n\t'VALUE' = replacement value\n");
|
||||
fprintf(stderr, "\t'MASK' = mask for the value placement.\n\n");
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int
|
||||
set_fix(struct carlfw *fw, struct carl9170fw_fix_desc *fix,
|
||||
char __unused option, int __unused argc, char *args[])
|
||||
{
|
||||
struct carl9170fw_fix_entry fix_entry, *entry = NULL;
|
||||
unsigned int address, value, mask;
|
||||
int err;
|
||||
|
||||
err = get_addr(args[3], &address);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = get_val(args[4], &value);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = get_val(args[5], &mask);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
fix_entry.address = cpu_to_le32(address);
|
||||
fix_entry.value = cpu_to_le32(value);
|
||||
fix_entry.mask = cpu_to_le32(mask);
|
||||
|
||||
if (!fix) {
|
||||
err = new_fix_entry(fw, &fix_entry);
|
||||
} else {
|
||||
entry = scan_for_similar_fix(fix, fix_entry.address);
|
||||
if (entry) {
|
||||
err = update_entry(option, entry, &fix_entry);
|
||||
if (err)
|
||||
fprintf(stdout, "Overwrite old entry.\n");
|
||||
} else {
|
||||
err = add_another_fix_entry(fw, fix, &fix_entry);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
del_fix(struct carlfw *fw, struct carl9170fw_fix_desc *fix,
|
||||
char __unused option, int __unused argc, char *args[])
|
||||
{
|
||||
struct carl9170fw_fix_entry *entry = NULL;
|
||||
unsigned int address;
|
||||
unsigned long off;
|
||||
unsigned int rem_len;
|
||||
int err;
|
||||
|
||||
err = get_addr(args[3], &address);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (fix)
|
||||
entry = scan_for_similar_fix(fix, cpu_to_le32(address));
|
||||
|
||||
if (!entry) {
|
||||
fprintf(stderr, "Entry for 0x%.8x not found\n", address);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
off = (unsigned long) entry - (unsigned long) fix->data;
|
||||
rem_len = le16_to_cpu(fix->head.length) - off;
|
||||
|
||||
if (rem_len) {
|
||||
unsigned long cont;
|
||||
cont = (unsigned long) entry + sizeof(*entry);
|
||||
memmove(entry, (void *)cont, rem_len);
|
||||
}
|
||||
|
||||
fix = carlfw_desc_mod_len(fw, &fix->head, -sizeof(*entry));
|
||||
err = IS_ERR_OR_NULL(fix);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int del_all(struct carlfw *fw, struct carl9170fw_fix_desc *fix,
|
||||
char __unused option, int __unused argc, char __unused *args[])
|
||||
{
|
||||
if (!fix)
|
||||
return 0;
|
||||
|
||||
carlfw_desc_del(fw, &fix->head);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
char option;
|
||||
int argc;
|
||||
int (*func)(struct carlfw *, struct carl9170fw_fix_desc *,
|
||||
char, int, char **);
|
||||
} programm_function[] = {
|
||||
{ '=', 6, set_fix },
|
||||
{ 'O', 6, set_fix },
|
||||
{ 'A', 6, set_fix },
|
||||
{ 'd', 4, del_fix },
|
||||
{ 'D', 3, del_all },
|
||||
};
|
||||
|
||||
int main(int argc, char *args[])
|
||||
{
|
||||
struct carl9170fw_fix_desc *fix;
|
||||
struct carlfw *fw = NULL;
|
||||
unsigned int i;
|
||||
int err = 0;
|
||||
char option;
|
||||
|
||||
if (argc < 3 || argc > 6) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fw = carlfw_load(args[1]);
|
||||
if (IS_ERR_OR_NULL(fw)) {
|
||||
err = PTR_ERR(fw);
|
||||
fprintf(stderr, "Failed to open file \"%s\" (%d).\n",
|
||||
args[1], err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fix = carlfw_find_desc(fw, (uint8_t *)FIX_MAGIC, sizeof(*fix),
|
||||
CARL9170FW_FIX_DESC_CUR_VER);
|
||||
|
||||
option = args[2][0];
|
||||
for (i = 0; i < ARRAY_SIZE(programm_function); i++) {
|
||||
if (programm_function[i].option != option)
|
||||
continue;
|
||||
|
||||
if (argc != programm_function[i].argc) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = programm_function[i].func(fw, fix, option, argc, args);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(programm_function)) {
|
||||
fprintf(stderr, "Unknown option: '%c'\n",
|
||||
args[2][0]);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = carlfw_store(fw);
|
||||
if (err) {
|
||||
fprintf(stderr, "Failed to apply changes (%d).\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
carlfw_release(fw);
|
||||
|
||||
if (err) {
|
||||
if (err == -EINVAL)
|
||||
user_education();
|
||||
else
|
||||
fprintf(stderr, "%s\n", strerror(err));
|
||||
}
|
||||
|
||||
return err ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
331
carl9170fw/tools/src/fwinfo.c
Normal file
331
carl9170fw/tools/src/fwinfo.c
Normal file
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Copyright 2010-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 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; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <error.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "carlfw.h"
|
||||
|
||||
#include "fwcmd.h"
|
||||
#include "compiler.h"
|
||||
|
||||
struct feature_list {
|
||||
unsigned int id;
|
||||
char name[64];
|
||||
void (*func)(const struct carl9170fw_desc_head *, struct carlfw *fw);
|
||||
};
|
||||
|
||||
#define CHECK_FOR_FEATURE(feature_enum) \
|
||||
{ .id = feature_enum, .name = #feature_enum, .func = NULL }
|
||||
|
||||
#define CHECK_FOR_FEATURE_FUNC(feature_enum, _func) \
|
||||
{ .id = feature_enum, .name = #feature_enum, .func = _func }
|
||||
|
||||
static void show_miniboot_info(const struct carl9170fw_desc_head *head,
|
||||
struct carlfw *fw __unused)
|
||||
{
|
||||
const struct carl9170fw_otus_desc *otus = (const void *) head;
|
||||
|
||||
fprintf(stdout, "\t\t\tminiboot size: %d Bytes\n", otus->miniboot_size);
|
||||
}
|
||||
|
||||
static const struct feature_list known_otus_features_v1[] = {
|
||||
CHECK_FOR_FEATURE(CARL9170FW_DUMMY_FEATURE),
|
||||
CHECK_FOR_FEATURE_FUNC(CARL9170FW_MINIBOOT, show_miniboot_info),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_USB_INIT_FIRMWARE),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_USB_RESP_EP2),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_USB_DOWN_STREAM),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_USB_UP_STREAM),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_UNUSABLE),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_COMMAND_PHY),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_COMMAND_CAM),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_WLANTX_CAB),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_HANDLE_BACK_REQ),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_GPIO_INTERRUPT),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_PSM),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_RX_FILTER),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_WOL),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_FIXED_5GHZ_PSM),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_HW_COUNTERS),
|
||||
CHECK_FOR_FEATURE(CARL9170FW_RX_BA_FILTER),
|
||||
};
|
||||
|
||||
static void check_feature_list(const struct carl9170fw_desc_head *head,
|
||||
const __le32 bitmap,
|
||||
const struct feature_list *list,
|
||||
const unsigned int entries,
|
||||
struct carlfw *fw)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < entries; i++) {
|
||||
if (!carl9170fw_supports(bitmap, list[i].id))
|
||||
continue;
|
||||
|
||||
fprintf(stdout, "\t\t%2d = %s\n", list[i].id, list[i].name);
|
||||
if (list[i].func)
|
||||
list[i].func(head, fw);
|
||||
}
|
||||
}
|
||||
|
||||
static void show_otus_desc(const struct carl9170fw_desc_head *head,
|
||||
struct carlfw *fw)
|
||||
{
|
||||
const struct carl9170fw_otus_desc *otus = (const void *) head;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(known_otus_features_v1) != __CARL9170FW_FEATURE_NUM);
|
||||
|
||||
fprintf(stdout, "\tFirmware upload pointer: 0x%x\n",
|
||||
otus->fw_address);
|
||||
fprintf(stdout, "\tBeacon Address: %x, (reserved:%d Bytes)\n",
|
||||
le32_to_cpu(otus->bcn_addr), le16_to_cpu(otus->bcn_len));
|
||||
fprintf(stdout, "\tTX DMA chunk size:%d Bytes, TX DMA chunks:%d\n",
|
||||
otus->tx_frag_len, otus->tx_descs);
|
||||
fprintf(stdout, "\t=> %d Bytes are reserved for the TX queues\n",
|
||||
otus->tx_frag_len * otus->tx_descs);
|
||||
fprintf(stdout, "\tCommand response buffers:%d\n", otus->cmd_bufs);
|
||||
fprintf(stdout, "\tMax. RX stream block size:%d Bytes\n",
|
||||
otus->rx_max_frame_len);
|
||||
fprintf(stdout, "\tSupported Firmware Interfaces: %d\n", otus->vif_num);
|
||||
fprintf(stdout, "\tFirmware API Version: %d\n", otus->api_ver);
|
||||
fprintf(stdout, "\tSupported Features: (raw:%.08x)\n",
|
||||
le32_to_cpu(otus->feature_set));
|
||||
|
||||
check_feature_list(head, otus->feature_set, known_otus_features_v1,
|
||||
ARRAY_SIZE(known_otus_features_v1), fw);
|
||||
}
|
||||
|
||||
static void show_motd_desc(const struct carl9170fw_desc_head *head,
|
||||
struct carlfw *fw __unused)
|
||||
{
|
||||
const struct carl9170fw_motd_desc *motd = (const void *) head;
|
||||
char buf[CARL9170FW_MOTD_STRING_LEN];
|
||||
unsigned int fw_date;
|
||||
|
||||
fw_date = motd->fw_year_month_day;
|
||||
fprintf(stdout, "\tFirmware Build Date (YYYY-MM-DD): 2%03d-%02d-%02d\n",
|
||||
CARL9170FW_GET_YEAR(fw_date), CARL9170FW_GET_MONTH(fw_date),
|
||||
CARL9170FW_GET_DAY(fw_date));
|
||||
|
||||
strncpy(buf, motd->desc, CARL9170FW_MOTD_STRING_LEN);
|
||||
fprintf(stdout, "\tFirmware Text:\"%s\"\n", buf);
|
||||
|
||||
strncpy(buf, motd->release, CARL9170FW_MOTD_STRING_LEN);
|
||||
fprintf(stdout, "\tFirmware Release:\"%s\"\n", buf);
|
||||
}
|
||||
|
||||
static void show_fix_desc(const struct carl9170fw_desc_head *head,
|
||||
struct carlfw *fw __unused)
|
||||
{
|
||||
const struct carl9170fw_fix_desc *fix = (const void *) head;
|
||||
const struct carl9170fw_fix_entry *iter;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < (head->length - sizeof(*head)) / sizeof(*iter); i++) {
|
||||
iter = &fix->data[i];
|
||||
fprintf(stdout, "\t\t%d: 0x%.8x := 0x%.8x (0x%.8x)\n", i,
|
||||
le32_to_cpu(iter->address), le32_to_cpu(iter->value),
|
||||
le32_to_cpu(iter->mask));
|
||||
}
|
||||
}
|
||||
|
||||
static void show_dbg_desc(const struct carl9170fw_desc_head *head,
|
||||
struct carlfw *fw __unused)
|
||||
{
|
||||
const struct carl9170fw_dbg_desc *dbg = (const void *) head;
|
||||
|
||||
#define DBG_ADDR(_name, _reg) do { \
|
||||
unsigned int __tmp = le32_to_cpu(dbg->_reg); \
|
||||
if (__tmp) \
|
||||
fprintf(stdout, "\t\t" _name " = 0x%.8x\n", __tmp); \
|
||||
} while (0);
|
||||
|
||||
fprintf(stdout, "\tFirmware Debug Registers/Counters\n");
|
||||
DBG_ADDR("bogoclock ", bogoclock_addr);
|
||||
DBG_ADDR("counter ", counter_addr);
|
||||
DBG_ADDR("rx total ", rx_total_addr);
|
||||
DBG_ADDR("rx overrun ", rx_overrun_addr);
|
||||
DBG_ADDR("rx filer ", rx_filter);
|
||||
}
|
||||
|
||||
static void show_txsq_desc(const struct carl9170fw_desc_head *head,
|
||||
struct carlfw *fw __unused)
|
||||
{
|
||||
const struct carl9170fw_txsq_desc *txsq = (const void *) head;
|
||||
|
||||
fprintf(stdout, "\t\ttx-seq table addr: 0x%x\n",
|
||||
le32_to_cpu(txsq->seq_table_addr));
|
||||
}
|
||||
|
||||
|
||||
static const struct feature_list wol_triggers_v1[] = {
|
||||
CHECK_FOR_FEATURE(CARL9170_WOL_DISCONNECT),
|
||||
CHECK_FOR_FEATURE(CARL9170_WOL_MAGIC_PKT),
|
||||
};
|
||||
|
||||
static void show_wol_desc(const struct carl9170fw_desc_head *head,
|
||||
struct carlfw *fw __unused)
|
||||
{
|
||||
const struct carl9170fw_wol_desc *wol = (const void *) head;
|
||||
|
||||
fprintf(stdout, "\tSupported WOWLAN triggers: (raw:%.08x)\n",
|
||||
le32_to_cpu(wol->supported_triggers));
|
||||
|
||||
check_feature_list(head, wol->supported_triggers, wol_triggers_v1,
|
||||
ARRAY_SIZE(wol_triggers_v1), fw);
|
||||
}
|
||||
|
||||
static void show_chk_desc(const struct carl9170fw_desc_head *head,
|
||||
struct carlfw *fw __unused)
|
||||
{
|
||||
const struct carl9170fw_chk_desc *chk = (const void *) head;
|
||||
|
||||
fprintf(stdout, "\tFirmware Descriptor CRC32: %08x\n",
|
||||
le32_to_cpu(chk->hdr_crc32));
|
||||
fprintf(stdout, "\tFirmware Image CRC32: %08x\n",
|
||||
le32_to_cpu(chk->fw_crc32));
|
||||
}
|
||||
|
||||
static void show_last_desc(const struct carl9170fw_desc_head *head,
|
||||
struct carlfw *fw __unused)
|
||||
|
||||
{
|
||||
const struct carl9170fw_last_desc *last __unused = (const void *) head;
|
||||
|
||||
/* Nothing here */
|
||||
}
|
||||
|
||||
#define ADD_HANDLER(_magic, _func) \
|
||||
{ \
|
||||
.magic = _magic##_MAGIC, \
|
||||
.min_ver = CARL9170FW_## _magic##_DESC_CUR_VER, \
|
||||
.func = _func, \
|
||||
.size = CARL9170FW_## _magic##_DESC_SIZE, \
|
||||
}
|
||||
|
||||
static const struct {
|
||||
uint8_t magic[4];
|
||||
uint8_t min_ver;
|
||||
void (*func)(const struct carl9170fw_desc_head *, struct carlfw *);
|
||||
uint16_t size;
|
||||
} known_magics[] = {
|
||||
ADD_HANDLER(OTUS, show_otus_desc),
|
||||
ADD_HANDLER(TXSQ, show_txsq_desc),
|
||||
ADD_HANDLER(MOTD, show_motd_desc),
|
||||
ADD_HANDLER(DBG, show_dbg_desc),
|
||||
ADD_HANDLER(FIX, show_fix_desc),
|
||||
ADD_HANDLER(CHK, show_chk_desc),
|
||||
ADD_HANDLER(WOL, show_wol_desc),
|
||||
ADD_HANDLER(LAST, show_last_desc),
|
||||
};
|
||||
|
||||
static const uint8_t otus_magic[4] = { OTUS_MAGIC };
|
||||
|
||||
static void show_desc_head(struct carl9170fw_desc_head *head)
|
||||
{
|
||||
#define P(c) (isprint(c) ? c : ' ')
|
||||
|
||||
fprintf(stdout, ">\t%c%c%c%c Descriptor: size:%d, compatible:%d, "
|
||||
"version:%d\n",
|
||||
P(head->magic[0]), P(head->magic[1]), P(head->magic[2]),
|
||||
P(head->magic[3]), le16_to_cpu(head->length), head->min_ver,
|
||||
head->cur_ver);
|
||||
}
|
||||
|
||||
static void fwinfo_info(void)
|
||||
{
|
||||
fprintf(stderr, "Usage:\n");
|
||||
fprintf(stderr, "\tfwinfo FW-FILE\n");
|
||||
|
||||
fprintf(stderr, "\nDescription:\n");
|
||||
fprintf(stderr, "\tDisplay firmware descriptors information in "
|
||||
"a human readable form.\n");
|
||||
|
||||
fprintf(stderr, "\nParameteres:\n");
|
||||
fprintf(stderr, "\t 'FW-FILE' = firmware file/base-name\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *args[])
|
||||
{
|
||||
struct carlfw *fw = NULL;
|
||||
struct carl9170fw_desc_head *fw_desc;
|
||||
unsigned int i;
|
||||
int err = 0;
|
||||
size_t len;
|
||||
|
||||
if (argc != 2) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fw = carlfw_load(args[1]);
|
||||
if (IS_ERR_OR_NULL(fw)) {
|
||||
err = PTR_ERR(fw);
|
||||
fprintf(stderr, "Failed to open firmware \"%s\" (%d).\n",
|
||||
args[1], err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
carlfw_get_fw(fw, &len);
|
||||
fprintf(stdout, "General Firmware Statistics:\n");
|
||||
fprintf(stdout, "\tFirmware file size: %u Bytes\n", (unsigned int)len);
|
||||
fprintf(stdout, "\t%d Descriptors in %d Bytes\n",
|
||||
carlfw_get_descs_num(fw), carlfw_get_descs_size(fw));
|
||||
|
||||
fw_desc = NULL;
|
||||
fprintf(stdout, "\nDetailed Descriptor Description:\n");
|
||||
while ((fw_desc = carlfw_desc_next(fw, fw_desc))) {
|
||||
show_desc_head(fw_desc);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(known_magics); i++) {
|
||||
if (carl9170fw_desc_cmp(fw_desc, known_magics[i].magic,
|
||||
known_magics[i].size, known_magics[i].min_ver)) {
|
||||
known_magics[i].func(fw_desc, fw);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(known_magics))
|
||||
fprintf(stderr, "Unknown Descriptor.\n");
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
|
||||
out:
|
||||
switch (err) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case -EINVAL:
|
||||
fwinfo_info();
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "%s\n", strerror(-err));
|
||||
break;
|
||||
}
|
||||
|
||||
carlfw_release(fw);
|
||||
return err ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
200
carl9170fw/tools/src/miniboot.c
Normal file
200
carl9170fw/tools/src/miniboot.c
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright 2010-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 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; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <error.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "carlfw.h"
|
||||
|
||||
#include "compiler.h"
|
||||
|
||||
static void mini_help(void)
|
||||
{
|
||||
fprintf(stderr, "Usage:\n");
|
||||
fprintf(stderr, "\tminiboot ACTION FW-FILE [MB-FILE]\n");
|
||||
|
||||
fprintf(stderr, "\nDescription:\n");
|
||||
fprintf(stderr, "\tFirmware concatenation utility.\n");
|
||||
|
||||
fprintf(stderr, "\nParameteres:\n");
|
||||
fprintf(stderr, "\t'ACTION' = [a|d]\n");
|
||||
fprintf(stderr, "\t | 'a' = Add miniboot firmware.\n");
|
||||
fprintf(stderr, "\t * 'd' = remove miniboot firmware.\n");
|
||||
fprintf(stderr, "\t'FW-FILE' = destination for the package.\n");
|
||||
fprintf(stderr, "\t'MB-FILE' = extra firmware image.\n");
|
||||
}
|
||||
|
||||
static int add_mini(struct carlfw *fw, const char *mini)
|
||||
{
|
||||
struct stat file_stat;
|
||||
struct carl9170fw_otus_desc *otus_desc = NULL;
|
||||
FILE *m = NULL;
|
||||
char *buf = NULL;
|
||||
size_t extra;
|
||||
int err;
|
||||
|
||||
m = fopen(mini, "r");
|
||||
if (m == NULL) {
|
||||
fprintf(stderr, "Failed to open file %s (%d).\n",
|
||||
mini, errno);
|
||||
err = -errno;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = fstat(fileno(m), &file_stat);
|
||||
if (err) {
|
||||
fprintf(stderr, "Failed to query file infos from "
|
||||
"\"%s\" (%d).\n", mini, errno);
|
||||
err = -errno;
|
||||
goto fail;
|
||||
}
|
||||
extra = file_stat.st_size;
|
||||
|
||||
otus_desc = carlfw_find_desc(fw, (uint8_t *) OTUS_MAGIC,
|
||||
sizeof(*otus_desc),
|
||||
CARL9170FW_OTUS_DESC_CUR_VER);
|
||||
if (!otus_desc) {
|
||||
fprintf(stderr, "No OTUS descriptor found\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (carl9170fw_supports(otus_desc->feature_set, CARL9170FW_MINIBOOT)) {
|
||||
fprintf(stderr, "Firmware has already a miniboot image.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
otus_desc->feature_set |= cpu_to_le32(BIT(CARL9170FW_MINIBOOT));
|
||||
otus_desc->miniboot_size = cpu_to_le16(extra);
|
||||
|
||||
buf = carlfw_mod_headroom(fw, extra);
|
||||
if (IS_ERR_OR_NULL(buf)) {
|
||||
fprintf(stderr, "Unable to add miniboot image.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = fread(buf, extra, 1, m);
|
||||
if (err != 1) {
|
||||
fprintf(stderr, "Unable to load miniboot.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
carlfw_store(fw);
|
||||
fclose(m);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (m)
|
||||
fclose(m);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int del_mini(struct carlfw *fw)
|
||||
{
|
||||
struct carl9170fw_otus_desc *otus_desc = NULL;
|
||||
void *buf;
|
||||
int cut;
|
||||
|
||||
otus_desc = carlfw_find_desc(fw, (uint8_t *) OTUS_MAGIC,
|
||||
sizeof(*otus_desc),
|
||||
CARL9170FW_OTUS_DESC_CUR_VER);
|
||||
if (!otus_desc) {
|
||||
fprintf(stderr, "Firmware is not for USB devices.\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
if (!carl9170fw_supports(otus_desc->feature_set, CARL9170FW_MINIBOOT)) {
|
||||
fprintf(stderr, "Firmware has no miniboot image.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cut = le16_to_cpu(otus_desc->miniboot_size);
|
||||
|
||||
buf = carlfw_mod_headroom(fw, -cut);
|
||||
if (IS_ERR_OR_NULL(buf)) {
|
||||
fprintf(stderr, "Unable to remove miniboot.\n");
|
||||
return PTR_ERR(buf);
|
||||
}
|
||||
|
||||
otus_desc->feature_set &= cpu_to_le32(~BIT(CARL9170FW_MINIBOOT));
|
||||
otus_desc->miniboot_size = cpu_to_le16(0);
|
||||
|
||||
carlfw_store(fw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *args[])
|
||||
{
|
||||
struct carlfw *fw = NULL;
|
||||
int err;
|
||||
|
||||
if (argc < 3 || argc > 4) {
|
||||
err = -EINVAL;
|
||||
goto err_param;
|
||||
}
|
||||
|
||||
switch (args[1][0]) {
|
||||
case 'a':
|
||||
if (argc != 4)
|
||||
goto err_param;
|
||||
|
||||
fw = carlfw_load(args[2]);
|
||||
if (IS_ERR_OR_NULL(fw)) {
|
||||
err = PTR_ERR(fw);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
err = add_mini(fw, args[3]);
|
||||
break;
|
||||
case 'd':
|
||||
if (argc != 3)
|
||||
goto err_param;
|
||||
|
||||
fw = carlfw_load(args[2]);
|
||||
if (IS_ERR_OR_NULL(fw)) {
|
||||
err = PTR_ERR(fw);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
err = del_mini(fw);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto err_param;
|
||||
break;
|
||||
}
|
||||
|
||||
carlfw_release(fw);
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
err_out:
|
||||
carlfw_release(fw);
|
||||
fprintf(stderr, "miniboot action failed (%d).\n", err);
|
||||
return EXIT_FAILURE;
|
||||
|
||||
err_param:
|
||||
carlfw_release(fw);
|
||||
mini_help();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
209
carl9170fw/tools/src/wol.c
Normal file
209
carl9170fw/tools/src/wol.c
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright 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 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; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/if_ether.h> /* ETH_P_ALL */
|
||||
#include <linux/if_packet.h> /* sockaddr_ll */
|
||||
#include <linux/if.h> /* IFNAMSIZ */
|
||||
|
||||
static int monitor_init(const char *ifname)
|
||||
{
|
||||
struct sockaddr_ll ll;
|
||||
int monitor_sock;
|
||||
|
||||
memset(&ll, 0, sizeof(ll));
|
||||
ll.sll_family = AF_PACKET;
|
||||
ll.sll_ifindex = if_nametoindex(ifname);
|
||||
if (ll.sll_ifindex == 0) {
|
||||
fprintf(stderr, "Monitor interface '%s' does not exist\n", ifname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
|
||||
if (monitor_sock < 0) {
|
||||
fprintf(stderr, "socket(PF_PACKET,SOCK_RAW): %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bind(monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
|
||||
fprintf(stderr, "bind(PACKET): %s\n", strerror(errno));
|
||||
close(monitor_sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return monitor_sock;
|
||||
}
|
||||
|
||||
static int inject_frame(int s, const void *data, size_t len)
|
||||
{
|
||||
#define IEEE80211_RADIOTAP_F_FRAG 0x08
|
||||
unsigned char rtap_hdr[] = {
|
||||
0x00, 0x00, /* radiotap version */
|
||||
0x0e, 0x00, /* radiotap length */
|
||||
0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
|
||||
IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */
|
||||
0x00, /* padding */
|
||||
0x00, 0x00, /* RX and TX flags to indicate that */
|
||||
0x00, 0x00, /* this is the injected frame directly */
|
||||
};
|
||||
struct iovec iov[2] = {
|
||||
{
|
||||
.iov_base = &rtap_hdr,
|
||||
.iov_len = sizeof(rtap_hdr),
|
||||
},
|
||||
{
|
||||
.iov_base = (void *) data,
|
||||
.iov_len = len,
|
||||
}
|
||||
};
|
||||
struct msghdr msg = {
|
||||
.msg_name = NULL,
|
||||
.msg_namelen = 0,
|
||||
.msg_iov = iov,
|
||||
.msg_iovlen = 2,
|
||||
.msg_control = NULL,
|
||||
.msg_controllen = 0,
|
||||
.msg_flags = 0,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = sendmsg(s, &msg, 0);
|
||||
if (ret < 0)
|
||||
perror("sendmsg");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned char wol_magic_tmpl[30 + 6 + 16 * 6] = {
|
||||
0x08, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* RA */
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* TA */
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* SA */
|
||||
0x00, 0x00,
|
||||
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
};
|
||||
|
||||
static void prepare_wol(unsigned char *wol_magic, unsigned char *mac)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
memcpy(&wol_magic[30 + i * 6], mac, 6);
|
||||
}
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
fprintf(stderr, "Usage:\n");
|
||||
fprintf(stderr, "\twol -i monitor_dev -m DE:VI:CE:MA:CW:OL -n #num -v\n");
|
||||
|
||||
fprintf(stderr, "\nDescription:\n");
|
||||
fprintf(stderr, "\tThis utility generates a WOL packet for the"
|
||||
"given [MAC] address and tries to injects"
|
||||
"it into [monitor_dev]\n");
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#define MAC_STR "%2X:%2X:%2X:%2X:%2X:%2X"
|
||||
|
||||
#define M(a, i) ((unsigned int *)&a[i])
|
||||
#define MAC_ARG(a) M(a, 0), M(a, 1), M(a, 2), M(a, 3), M(a, 4), M(a, 5)
|
||||
|
||||
#define M2(a, i) (a[i])
|
||||
#define MAC_ARG2(a) M2(a, 0), M2(a, 1), M2(a, 2), M2(a, 3), M2(a, 4), M2(a, 5)
|
||||
|
||||
int main(int argc, char **args)
|
||||
{
|
||||
int sock, err = 0, opt, num = 10;
|
||||
unsigned char mac[ETH_ALEN];
|
||||
char dev_name[IFNAMSIZ + 1] = { 0 };
|
||||
bool has_mac = false, has_dev = false, verbose = false;
|
||||
|
||||
while ((opt = getopt(argc, args, "m:i:n:v")) != -EXIT_FAILURE) {
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
has_dev = true;
|
||||
strncpy(dev_name, optarg, IFNAMSIZ);
|
||||
break;
|
||||
case 'm':
|
||||
has_mac = true;
|
||||
err = sscanf(optarg, MAC_STR, MAC_ARG(mac)) != 6;
|
||||
if (err)
|
||||
fprintf(stderr, "invalid MAC: \"%s\"\n", optarg);
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
err = sscanf(optarg, "%d", &num) != 1;
|
||||
err |= num < 1 | num > 1000;
|
||||
if (err)
|
||||
fprintf(stderr, "invalid tries: \"%s\"\n", optarg);
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!has_mac || !has_dev || err)
|
||||
usage();
|
||||
|
||||
if (verbose)
|
||||
fprintf(stdout, "Opening monitor injection interface [%s].\n", dev_name);
|
||||
|
||||
sock = monitor_init(dev_name);
|
||||
if (sock < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (verbose)
|
||||
fprintf(stdout, "Generating %d WOL packet for ["MAC_STR"].\n", num, MAC_ARG2(mac));
|
||||
|
||||
prepare_wol(wol_magic_tmpl, mac);
|
||||
|
||||
while (num--) {
|
||||
err = inject_frame(sock, wol_magic_tmpl, sizeof(wol_magic_tmpl));
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "failed to send WOL packet.\n");
|
||||
break;
|
||||
} else if (verbose) {
|
||||
fprintf(stdout, "WOL packet sent.\n");
|
||||
}
|
||||
}
|
||||
|
||||
close(sock);
|
||||
if (err < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user