diff options
Diffstat (limited to 'carl9170fw/tools')
27 files changed, 5042 insertions, 0 deletions
diff --git a/carl9170fw/tools/CMakeLists.txt b/carl9170fw/tools/CMakeLists.txt new file mode 100644 index 0000000..51d71fa --- /dev/null +++ b/carl9170fw/tools/CMakeLists.txt @@ -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 () diff --git a/carl9170fw/tools/Kconfig b/carl9170fw/tools/Kconfig new file mode 100644 index 0000000..5e4eba2 --- /dev/null +++ b/carl9170fw/tools/Kconfig @@ -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 diff --git a/carl9170fw/tools/carlu/CMakeLists.txt b/carl9170fw/tools/carlu/CMakeLists.txt new file mode 100644 index 0000000..0ea6a05 --- /dev/null +++ b/carl9170fw/tools/carlu/CMakeLists.txt @@ -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) diff --git a/carl9170fw/tools/carlu/src/carlu.h b/carl9170fw/tools/carlu/src/carlu.h new file mode 100644 index 0000000..5e2c6bb --- /dev/null +++ b/carl9170fw/tools/carlu/src/carlu.h @@ -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 */ diff --git a/carl9170fw/tools/carlu/src/cmd.c b/carl9170fw/tools/carlu/src/cmd.c new file mode 100644 index 0000000..bb87399 --- /dev/null +++ b/carl9170fw/tools/carlu/src/cmd.c @@ -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; +} diff --git a/carl9170fw/tools/carlu/src/cmd.h b/carl9170fw/tools/carlu/src/cmd.h new file mode 100644 index 0000000..ccc9a43 --- /dev/null +++ b/carl9170fw/tools/carlu/src/cmd.h @@ -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 */ diff --git a/carl9170fw/tools/carlu/src/debug.c b/carl9170fw/tools/carlu/src/debug.c new file mode 100644 index 0000000..f8d6372 --- /dev/null +++ b/carl9170fw/tools/carlu/src/debug.c @@ -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); + } +} diff --git a/carl9170fw/tools/carlu/src/debug.h b/carl9170fw/tools/carlu/src/debug.h new file mode 100644 index 0000000..5c78056 --- /dev/null +++ b/carl9170fw/tools/carlu/src/debug.h @@ -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 */ diff --git a/carl9170fw/tools/carlu/src/fw.c b/carl9170fw/tools/carlu/src/fw.c new file mode 100644 index 0000000..fa4b388 --- /dev/null +++ b/carl9170fw/tools/carlu/src/fw.c @@ -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)); + } +} diff --git a/carl9170fw/tools/carlu/src/main.c b/carl9170fw/tools/carlu/src/main.c new file mode 100644 index 0000000..c1b3277 --- /dev/null +++ b/carl9170fw/tools/carlu/src/main.c @@ -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]); +} diff --git a/carl9170fw/tools/carlu/src/rx.c b/carl9170fw/tools/carlu/src/rx.c new file mode 100644 index 0000000..2a77334 --- /dev/null +++ b/carl9170fw/tools/carlu/src/rx.c @@ -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); +} diff --git a/carl9170fw/tools/carlu/src/test.c b/carl9170fw/tools/carlu/src/test.c new file mode 100644 index 0000000..bf49559 --- /dev/null +++ b/carl9170fw/tools/carlu/src/test.c @@ -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"); +} diff --git a/carl9170fw/tools/carlu/src/test.h b/carl9170fw/tools/carlu/src/test.h new file mode 100644 index 0000000..373ba08 --- /dev/null +++ b/carl9170fw/tools/carlu/src/test.h @@ -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 */ diff --git a/carl9170fw/tools/carlu/src/tx.c b/carl9170fw/tools/carlu/src/tx.c new file mode 100644 index 0000000..ec97502 --- /dev/null +++ b/carl9170fw/tools/carlu/src/tx.c @@ -0,0 +1,213 @@ +/* + * carlu - userspace testing utility for ar9170 devices + * + * xmit - related functions + * + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include "libusb.h" + +#include "carlu.h" +#include "debug.h" +#include "frame.h" +#include "usb.h" +#include "ieee80211.h" +#include "wlan.h" + +struct frame *carlu_alloc_frame(struct carlu *ar, unsigned int size) +{ + struct frame *tmp; + unsigned int total_len; + + total_len = ar->extra_headroom + sizeof(struct _carl9170_tx_superframe) + size; + + tmp = frame_alloc(total_len); + if (!tmp) + return NULL; + + frame_reserve(tmp, sizeof(struct _carl9170_tx_superframe) + ar->extra_headroom); + + tmp->queue = 2; + + return tmp; +} + +static int carlu_alloc_dev_mem(struct carlu *ar, + struct frame *frame) +{ + struct _carl9170_tx_superframe *txp = (void *)frame->data; + unsigned int len, chunks; + + len = roundup(frame->len, ar->dma_chunk_size); + chunks = len / ar->dma_chunk_size; + + SDL_mutexP(ar->mem_lock); + if (ar->tx_pending >= ar->dma_chunks || + ar->used_dma_chunks + chunks >= ar->dma_chunks) { + SDL_mutexV(ar->mem_lock); + return -ENOSPC; + } + + ar->used_dma_chunks += chunks; + ar->tx_pending++; + txp->s.cookie = ar->cookie++; + SDL_mutexV(ar->mem_lock); + + return 0; +} + +static void carlu_free_dev_mem(struct carlu *ar, + struct frame *frame) +{ + struct _carl9170_tx_superframe *txp = (void *)frame->data; + unsigned int len, chunks; + + len = roundup(frame->len, ar->dma_chunk_size); + chunks = len / ar->dma_chunk_size; + + SDL_mutexP(ar->mem_lock); + ar->used_dma_chunks -= chunks; + ar->tx_pending--; + SDL_mutexV(ar->mem_lock); +} + +void carlu_free_frame(struct carlu *ar __unused, + struct frame *frame) +{ + frame_free(frame); +} + +static struct frame *carlu_find_frame(struct carlu *ar, + unsigned int queue, uint8_t cookie) +{ + struct frame *frame = NULL; + + BUG_ON(queue >= __AR9170_NUM_TXQ); + BUG_ON(SDL_mutexP(ar->tx_sent_queue[queue].lock) != 0); + FRAME_WALK(frame, &ar->tx_sent_queue[queue]) { + struct _carl9170_tx_superframe *super; + + super = (void *) frame->data; + if (super->s.cookie == cookie) { + __frame_unlink(&ar->tx_sent_queue[queue], frame); + SDL_mutexV(ar->tx_sent_queue[queue].lock); + return frame; + } + } + SDL_mutexV(ar->tx_sent_queue[queue].lock); + + return NULL; +} + +static void carlu_tx_fb_cb(struct carlu *ar, + struct frame *frame) +{ + if (ar->tx_fb_cb) + ar->tx_fb_cb(ar, frame); + else + carlu_free_frame(ar, frame); + +} + +void carlu_tx_feedback(struct carlu *ar, + struct carl9170_rsp *cmd) +{ + unsigned int i, n, k, q; + struct frame *frame; + struct carlu_tx_info *tx_info; + + n = cmd->hdr.ext; + + for (i = 0; i < n; i++) { + q = (cmd->_tx_status[i].info >> CARL9170_TX_STATUS_QUEUE_S) & + CARL9170_TX_STATUS_QUEUE; + frame = carlu_find_frame(ar, q, cmd->_tx_status[i].cookie); + if (frame) { + carlu_free_dev_mem(ar, frame); + tx_info = get_tx_info(frame); + + k = (cmd->_tx_status[i].info >> CARL9170_TX_STATUS_RIX) + & CARL9170_TX_STATUS_RIX_S; + tx_info->rates[k].cnt = (cmd->_tx_status[i].info >> + CARL9170_TX_STATUS_TRIES_S) & + CARL9170_TX_STATUS_TRIES; + for (k++; k < CARL9170_TX_MAX_RATES; k++) { + tx_info->rates[k].rix = -1; + tx_info->rates[k].cnt = -1; + } + + carlu_tx_fb_cb(ar, frame); + } else { + err("Found no frame for cookie %d.\n", + cmd->_tx_status[i].cookie); + } + } +} + +int carlu_tx(struct carlu *ar, struct frame *frame) +{ + struct _carl9170_tx_superframe *txp; + unsigned int len, queue; + int cookie, err; + + len = frame->len; + + txp = (void *) frame_push(frame, sizeof(struct _carl9170_tx_superframe)); + + if (txp->s.rix) + goto err_out; + + err = carlu_alloc_dev_mem(ar, frame); + if (err) + goto err_out; + + txp->s.len = cpu_to_le16(frame->len); + + queue = (frame->queue % __AR9170_NUM_TXQ); + + SET_VAL(CARL9170_TX_SUPER_MISC_QUEUE, txp->s.misc, queue); + + txp->f.length = len + FCS_LEN; /* + I(C)V_LEN */ + + txp->f.mac_control = cpu_to_le16(AR9170_TX_MAC_HW_DURATION | + AR9170_TX_MAC_BACKOFF); + txp->f.mac_control |= cpu_to_le16(queue << AR9170_TX_MAC_QOS_S); + + txp->f.phy_control = cpu_to_le32(AR9170_TX_PHY_MOD_CCK | AR9170_TX_PHY_BW_20MHZ | + ((17 * 2) << AR9170_TX_PHY_TX_PWR_S) | + (AR9170_TX_PHY_TXCHAIN_1 << AR9170_TX_PHY_TXCHAIN_S) | + (11 << AR9170_TX_PHY_MCS_S)); + + frame_queue_tail(&ar->tx_sent_queue[queue], frame); + carlusb_tx(ar, frame); + return 0; + +err_out: + frame_pull(frame, sizeof(struct _carl9170_tx_superframe)); + return err; +} diff --git a/carl9170fw/tools/carlu/src/usb.c b/carl9170fw/tools/carlu/src/usb.c new file mode 100644 index 0000000..ebc19a9 --- /dev/null +++ b/carl9170fw/tools/carlu/src/usb.c @@ -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); +} diff --git a/carl9170fw/tools/carlu/src/usb.h b/carl9170fw/tools/carlu/src/usb.h new file mode 100644 index 0000000..66c674f --- /dev/null +++ b/carl9170fw/tools/carlu/src/usb.h @@ -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 */ diff --git a/carl9170fw/tools/include/frame.h b/carl9170fw/tools/include/frame.h new file mode 100644 index 0000000..f753fe3 --- /dev/null +++ b/carl9170fw/tools/include/frame.h @@ -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 */ diff --git a/carl9170fw/tools/include/list.h b/carl9170fw/tools/include/list.h new file mode 100644 index 0000000..1991d97 --- /dev/null +++ b/carl9170fw/tools/include/list.h @@ -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 */ diff --git a/carl9170fw/tools/lib/CMakeLists.txt b/carl9170fw/tools/lib/CMakeLists.txt new file mode 100644 index 0000000..2d139e6 --- /dev/null +++ b/carl9170fw/tools/lib/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 2.8) + +project(libcarlfw) + +set(carlfw_src carlfw.c) + +add_library( carlfw STATIC ${carlfw_src}) diff --git a/carl9170fw/tools/lib/carlfw.c b/carl9170fw/tools/lib/carlfw.c new file mode 100644 index 0000000..ce61afb --- /dev/null +++ b/carl9170fw/tools/lib/carlfw.c @@ -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; +} diff --git a/carl9170fw/tools/lib/carlfw.h b/carl9170fw/tools/lib/carlfw.h new file mode 100644 index 0000000..6e722c9 --- /dev/null +++ b/carl9170fw/tools/lib/carlfw.h @@ -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 */ diff --git a/carl9170fw/tools/src/CMakeLists.txt b/carl9170fw/tools/src/CMakeLists.txt new file mode 100644 index 0000000..3cd31fd --- /dev/null +++ b/carl9170fw/tools/src/CMakeLists.txt @@ -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() diff --git a/carl9170fw/tools/src/checksum.c b/carl9170fw/tools/src/checksum.c new file mode 100644 index 0000000..b394174 --- /dev/null +++ b/carl9170fw/tools/src/checksum.c @@ -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; +} diff --git a/carl9170fw/tools/src/eeprom_fix.c b/carl9170fw/tools/src/eeprom_fix.c new file mode 100644 index 0000000..088510e --- /dev/null +++ b/carl9170fw/tools/src/eeprom_fix.c @@ -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; +} diff --git a/carl9170fw/tools/src/fwinfo.c b/carl9170fw/tools/src/fwinfo.c new file mode 100644 index 0000000..0d5cd09 --- /dev/null +++ b/carl9170fw/tools/src/fwinfo.c @@ -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; +} diff --git a/carl9170fw/tools/src/miniboot.c b/carl9170fw/tools/src/miniboot.c new file mode 100644 index 0000000..509d82d --- /dev/null +++ b/carl9170fw/tools/src/miniboot.c @@ -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; +} diff --git a/carl9170fw/tools/src/wol.c b/carl9170fw/tools/src/wol.c new file mode 100644 index 0000000..41daba2 --- /dev/null +++ b/carl9170fw/tools/src/wol.c @@ -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; +} |