diff options
Diffstat (limited to 'carl9170fw/carlfw')
42 files changed, 7785 insertions, 0 deletions
diff --git a/carl9170fw/carlfw/CMakeLists.txt b/carl9170fw/carlfw/CMakeLists.txt new file mode 100644 index 0000000..caf9595 --- /dev/null +++ b/carl9170fw/carlfw/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required(VERSION 2.8) + +project(carl9170.fw) + +include("../extra/sh-elf-linux.cmake") +include("../config.cmake") + +set(CARLFW_CFLAGS_WARNING "-W -Wall -Wextra -Wunreachable-code -Winline -Wlogical-op -Wno-packed-bitfield-compat -Winit-self -Wshadow -Wwrite-strings -Waggregate-return -Wstrict-prototypes -Wformat=2 -Wcast-align -Wmissing-format-attribute -Wmissing-prototypes -Wtype-limits -Wmissing-declarations -Wmissing-noreturn -Wredundant-decls -Wnested-externs -Wdisabled-optimization -Wpointer-arith -Wvolatile-register-var -Waddress -Wbad-function-cast -Wunsafe-loop-optimizations") +set(CARLFW_CFLAGS_EXTRA "-mbitops -std=gnu99 -ffunction-sections -Wframe-larger-than=128 -Werror") +set(CARLFW_CFLAGS_DEF "-D__CARL9170FW__") +if (CONFIG_CARL9170FW_AGGRESSIVE_CFLAGS) + set(CARLFW_CFLAGS_AGGRESSIVE "-fomit-frame-pointer -fsee -frename-registers -ftree-vectorize -flto -fstrict-volatile-bitfields -fmodulo-sched") +endif (CONFIG_CARL9170FW_AGGRESSIVE_CFLAGS) + +include_directories (../include/linux ../include/shared ../include include) + +set(carl9170_main_src src/main.c src/wlan.c src/fw.c src/gpio.c + src/cmd.c src/uart.c src/dma.c src/hostif.c src/reboot.S + src/printf.c src/rf.c src/cam.c src/wol.c) + +set(carl9170_lib_src src/ashlsi3.S src/memcpy.S src/memset.S src/udivsi3_i4i-Os.S) +set(carl9170_usb_src usb/main.c usb/usb.c usb/fifo.c) + +set(carl9170_src ${carl9170_main_src} ${carl9170_lib_src} ${carl9170_usb_src}) + +set_source_files_properties(src/ashlsi3.S PROPERTIES LANGUAGE C) +set_source_files_properties(src/memcpy.S PROPERTIES LANGUAGE C) +set_source_files_properties(src/memset.S PROPERTIES LANGUAGE C) +set_source_files_properties(src/reboot.S PROPERTIES LANGUAGE C) +set_source_files_properties(src/udivsi3_i4i-Os.S PROPERTIES LANGUAGE C) + +add_executable(carl9170.elf ${carl9170_src}) + +set_target_properties(carl9170.elf PROPERTIES LINKER_LANGUAGE C) + +set_target_properties(carl9170.elf PROPERTIES COMPILE_FLAGS + " ${CARLFW_CFLAGS_DEF} ${CARLFW_CFLAGS_EXTRA} ${CARLFW_CFLAGS_AGGRESSIVE} ${CARLFW_CFLAGS_WARNING}") +set_target_properties(carl9170.elf PROPERTIES LINK_FLAGS "-Tcarl9170.lds") + + + +add_custom_target(firmware ALL) + +add_custom_command( + SOURCE carl9170.elf + COMMAND ${OBJCOPY} + ARGS --strip-unneeded -O binary -R .sram -R .eeprom -R .fwdsc carl9170.elf carl9170.bin + TARGET firmware + OUTPUTS carl9170.bin) + +add_custom_command( + SOURCE carl9170.elf + COMMAND ${OBJCOPY} + ARGS --strip-unneeded -O binary -j .fwdsc carl9170.elf carl9170.dsc + TARGET firmware + OUTPUTS carl9170.dsc) + +add_custom_command( + SOURCE firmware + TARGET firmware + COMMAND cat + ARGS "carl9170.bin" "carl9170.dsc" > "carl9170.fw" + DEPENDS carl9170.elf carl9170.bin carl9170.dsc + OUTPUTS carl9170.fw) + +SET_DIRECTORY_PROPERTIES( + PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "carl9170.fw") diff --git a/carl9170fw/carlfw/Kconfig b/carl9170fw/carlfw/Kconfig new file mode 100644 index 0000000..f42268c --- /dev/null +++ b/carl9170fw/carlfw/Kconfig @@ -0,0 +1,233 @@ +menu "General" + +config CARL9170FW_RELEASE_VERSION + int + default 1 + +menu "Selectable Hardware Options" + +choice + prompt "Receiver Max. Frame Length" + default CARL9170FW_RX_FRAME_LEN_8192 + + config CARL9170FW_RX_FRAME_LEN_4096 + bool "4096" + + config CARL9170FW_RX_FRAME_LEN_8192 + bool "8192" + + config CARL9170FW_RX_FRAME_LEN_16384 + bool "16384" + + config CARL9170FW_RX_FRAME_LEN_32768 + bool "32768" + +endchoice + +config CARL9170FW_RX_FRAME_LEN + int + default 4096 if CARL9170FW_RX_FRAME_LEN_4096 + default 8192 if CARL9170FW_RX_FRAME_LEN_8192 + default 16384 if CARL9170FW_RX_FRAME_LEN_16384 + default 32768 if CARL9170FW_RX_FRAME_LEN_32768 + +config CARL9170FW_GPIO_INTERRUPT + def_bool y + prompt "GPIO Software Interrupt" + ---help--- + When this option is enabled, the firmware will poll the GPIO + registers and reports to the driver whenever the GPIO state + has changed from a previous state. + + Note: This feature is necessary to monitor the WPS button, + if you have one on your device, then say Y. + +config CARL9170FW_SECURITY_ENGINE + def_bool y + prompt "Support Hardware Crypto Engine" + ---help--- + This options controls if the firmware will allow the driver + to program the security engine / CAM through a firmware + interface. + + Say Y. Unless you want to do the en- and decryption for + CCMP(AES), TKIP/WEP(RC4) in the application anyway. + +config CARL9170FW_RADIO_FUNCTIONS + def_bool y + prompt "Enable Firmware-supported Radio/RF functions" + ---help--- + Some PHY/RF functions (e.g.: AGC and Noise calibration) need + to be done in the firmware. + + Say Y, unless you really don't need the Radio/RF for + your project. + +endmenu + +menu "802.11 Firmware Features" + +config CARL9170FW_CAB_QUEUE + def_bool y + prompt "Support software-based Content after Beacon Queue" + ---help--- + This (software) queue is used to send any broad-/multi-cast buffered + frames after the next DTIM beacon. + + This feature is required for Accesspoint mode operation. + + Say Y. + +endmenu + +source "carlfw/usb/Kconfig" + +menu "Experimental, Unstable & Testing Extensions" + +config CARL9170FW_PRINTF + def_bool y + prompt "Advanced printf" + depends on CARL9170FW_DEBUG_UART || CARL9170FW_DEBUG_USB + ---help--- + Advanced printf (very useful for debugging purposes) + The formats supported by this implementation are: + 'd' 'u' 'c' 's' 'x' 'X' 'p'. + + Note: If this option is disabled, the firmware will be only + capable of reported _preformated_ string. + +config CARL9170FW_EXPERIMENTAL + def_bool y + prompt "Experimental Features" + +config CARL9170FW_WOL_OPTION + def_bool n + prompt "Wakeup on WLAN" + depends on CARL9170FW_EXPERIMENTAL + ---help--- + With this option enabled, the firmware can wake-up + suspended hosts... As long as they fully support + USB remote wakeup. + +config CARL9170FW_WOL + def_bool n + depends on CARL9170FW_WOL_OPTION + +config CARL9170FW_WOL_NL80211_TRIGGERS + def_bool n + prompt "Standard NL80211 wakeup triggers" + depends on CARL9170FW_WOL_OPTION + select CARL9170FW_WOL + ---help--- + Available triggers: + * Magic Packet(tm) pattern + * disconnect event + +config CARL9170FW_WOL_PROBE_REQUEST + def_bool n + prompt "Probe Request" + depends on CARL9170FW_WOL_OPTION + select CARL9170FW_WOL + ---help--- + Scan probe requests for a given SSID. + +config CARL9170FW_WOL_PROBE_REQUEST_SSID + string + prompt "Wakeup on WLAN SSID" + default "CARL9170_WAKEUP" + depends on CARL9170FW_WOL_PROBE_REQUEST + +config CARL9170FW_VIFS_NUM + default 1 + int + prompt "Number of additional pseudo virtual interfaces" + depends on CARL9170FW_EXPERIMENTAL + +config CARL9170FW_FW_MAC_RESET + def_bool y + prompt "Firmware MAC Chip recovery" + depends on CARL9170FW_EXPERIMENTAL + +config CARL9170FW_NOISY_MAC_RESET + def_bool n + prompt "Notify MAC RESET events" + depends on CARL9170FW_FW_MAC_RESET + +config CARL9170FW_BROKEN_FEATURES + def_bool n + prompt "Broken Featurs" + +config CARL9170FW_DEBUG + def_bool n + depends on CARL9170FW_BROKEN_FEATURES && CARL9170FW_PRINTF + prompt "Enable verbose debugging messages" + +config CARL9170FW_DEBUG_LED_HEARTBEAT + def_bool n + prompt "LED Heartbeat" + depends on CARL9170FW_BROKEN_FEATURES + ---help--- + This option conflicts with the application's LED code. + Also, it assumes that you have two LEDs, which is not + necessarily true. + +config CARL9170FW_DEBUG_UART + def_bool n + prompt "Pass debug messages through Highspeed UART" + depends on CARL9170FW_BROKEN_FEATURES + ---help--- + This option allows the firmware to send BUG/ERR/INFO/DBG and + hexdumps through the UART _as well_. However, first: you must + connect a working logger. + +config CARL9170FW_WATCHDOG_BUTTON + def_bool n + depends on CARL9170FW_BROKEN && CARL9170FW_GPIO_INTERRUPT + prompt "Trigger Watchdog by pressing the WPS button" + +choice CARL9170FW_UART_CLOCK + prompt "UART Clock" + depends on CARL9170FW_DEBUG_UART + default CARL9170FW_UART_CLOCK_40M + +config CARL9170FW_UART_CLOCK_25M + bool "25" + +config CARL9170FW_UART_CLOCK_40M + bool "40" + +endchoice + +config CARL9170FW_UNUSABLE + def_bool y + depends on CARL9170FW_BROKEN || CARL9170FW_DEBUG + +config CARL9170FW_USB_MODESWITCH + def_bool n + prompt "USB 1.1 / 2.0 switching support" + depends on CARL9170FW_BROKEN_FEATURES + ---help--- + Mostly implemented, but untested and some serious + doubts remain. + +config CARL9170FW_DMA_QUEUE_BUMP + def_bool n + prompt "Bump a stuck TX queue before doing a MAC reset" + depends on CARL9170FW_BROKEN_FEATURES + +menu "Build Options" +config CARL9170FW_AGGRESSIVE_CFLAGS + def_bool y + prompt "Enable aggressive size optimization" + ---help--- + This option adds several more optimization compiler flags, + which can greatly reduce the firmware size... at the expense + of machine-code readability. + + Say Y. Else the firmware might not fit onto the device! + +endmenu + +endmenu + +endmenu diff --git a/carl9170fw/carlfw/carl9170.lds b/carl9170fw/carlfw/carl9170.lds new file mode 100644 index 0000000..97dda68 --- /dev/null +++ b/carl9170fw/carlfw/carl9170.lds @@ -0,0 +1,59 @@ +/* + * The carl9170 firwmare gets copied into the device's + * Program RAM (pram), which has a size of 16K, but + * also has to accomodate the stack the device uses, + * which starts at the top of the 16k, so we pretend + * that we just have 16256 (16k - 128) of pram. + * + * This section documents some of the other areas + * mapped into the firmware processor's address space + * as well. + */ + +ENTRY(_start); + +MEMORY +{ + eeprom : ORIGIN = 0x000000, LENGTH = 1024k + sram : ORIGIN = 0x100000, LENGTH = 96k + uart : ORIGIN = 0x1c0000, LENGTH = 4k + timer : ORIGIN = 0x1c1000, LENGTH = 4k + vflash : ORIGIN = 0x1c2000, LENGTH = 4k + wlan : ORIGIN = 0x1c3000, LENGTH = 4k + pci2ahb : ORIGIN = 0x1c4000, LENGTH = 4k + security : ORIGIN = 0x1c5000, LENGTH = 4k + gpio : ORIGIN = 0x1d0000, LENGTH = 4k + memctl : ORIGIN = 0x1d1000, LENGTH = 4k + irqctl : ORIGIN = 0x1d2000, LENGTH = 4k + usb : ORIGIN = 0x1e1000, LENGTH = 4k + pta : ORIGIN = 0x1e2000, LENGTH = 4k + pram : ORIGIN = 0x200000, LENGTH = 16256 + bogus : ORIGIN = 0x300000, LENGTH = 8k +} + +SECTIONS +{ + .eeprom : { *(.eeprom*) } > eeprom + .sram : { *(.sram*) } > sram + + /* + * The ar9170 boot code will execute the code + * at address 0x04 from the loaded firmware as + * such we must ensure our starting routine + * is kept at that address. + */ + .padding : { + /* NOP NOP just in case */ + LONG(0x00090009) + } > pram + + .boot : { *(.boot) } > pram + /* anything else can be anywhere */ + + .text : { *(.text*) } > pram + .rodata : { *(.rodata*) } > pram + .bss : { *(.bss) } > pram + .data : { *(.data*) } > pram + + .fwdsc : { KEEP(*(.fwdsc)) } > bogus +} diff --git a/carl9170fw/carlfw/include/cam.h b/carl9170fw/carlfw/include/cam.h new file mode 100644 index 0000000..33afe03 --- /dev/null +++ b/carl9170fw/carlfw/include/cam.h @@ -0,0 +1,49 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * CAM (Security Engine) definitions + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, 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 __CARL9170FW_CAM_H +#define __CARL9170FW_CAM_H + +#include "config.h" +#include "cmd.h" + +#ifdef CONFIG_CARL9170FW_SECURITY_ENGINE + +#define ENCRY_TYPE_START_ADDR 24 +#define DEFAULT_ENCRY_TYPE 26 +#define KEY_START_ADDR 27 +#define STA_KEY_START_ADDR 155 +#define COUNTER_START_ADDR 163 +#define STA_COUNTER_START_ADDR 165 + +/* CAM */ +#define MIC_FINISH 0x1 + +void set_key(const struct carl9170_set_key_cmd *key); +void disable_key(const struct carl9170_disable_key_cmd *key); + +#endif /* CONFIG_CARL9170FW_SECURITY_ENGINE */ + +#endif /* __CARL9170FW_CAM_H */ diff --git a/carl9170fw/carlfw/include/carl9170.h b/carl9170fw/carlfw/include/carl9170.h new file mode 100644 index 0000000..cb16415 --- /dev/null +++ b/carl9170fw/carlfw/include/carl9170.h @@ -0,0 +1,222 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Firmware context 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 __CARL9170FW_CARL9170_H +#define __CARL9170FW_CARL9170_H + +#include "generated/autoconf.h" +#include "version.h" +#include "config.h" +#include "types.h" +#include "compiler.h" +#include "fwcmd.h" +#include "hw.h" +#include "dma.h" +#include "usb.h" +#include "cmd.h" + +struct carl9170_bar_ctx { + uint8_t ta[6]; + uint8_t ra[6]; + __le16 start_seq_num; + __le16 control; +}; + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE +enum carl9170_cab_trigger { + CARL9170_CAB_TRIGGER_EMPTY = 0, + CARL9170_CAB_TRIGGER_ARMED = BIT(0), + CARL9170_CAB_TRIGGER_DEFER = BIT(1), +}; +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + +enum carl9170_ep0_action { + CARL9170_EP0_NO_ACTION = 0, + CARL9170_EP0_STALL = BIT(0), + CARL9170_EP0_TRIGGER = BIT(1), +}; + +enum carl9170_mac_reset_state { + CARL9170_MAC_RESET_OFF = 0, + CARL9170_MAC_RESET_ARMED, + CARL9170_MAC_RESET_RESET, + CARL9170_MAC_RESET_FORCE, +}; + +enum carl9170_suspend_mode { + CARL9170_HOST_AWAKE = 0, + CARL9170_HOST_SUSPENDED, + CARL9170_AWAKE_HOST, +}; + +enum carl9170_phy_state { + CARL9170_PHY_OFF = 0, + CARL9170_PHY_ON +}; + +typedef void (*fw_desc_callback_t)(void *, const bool); + +/* + * This platform - being an odd 32-bit architecture - prefers to + * have 32-Bit variables. + */ + +struct firmware_context_struct { + /* timer / clocks */ + unsigned int ticks_per_usec; + unsigned int counter; /* main() cycles */ + + /* misc */ + unsigned int watchdog_enable; + unsigned int reboot; + unsigned int suspend_mode; + + struct { + /* Host Interface DMA queues */ + struct dma_queue up_queue; /* used to send frames to the host */ + struct dma_queue down_queue; /* stores incoming frames from the host */ + } pta; + + struct { + /* Hardware DMA queues */ + struct dma_queue tx_queue[__AR9170_NUM_TX_QUEUES]; /* wlan tx queue */ + struct dma_queue tx_retry; + struct dma_queue rx_queue; /* wlan rx queue */ + + /* tx aggregate scheduling */ + struct carl9170_tx_superframe *ampdu_prev[__AR9170_NUM_TX_QUEUES]; + + /* Hardware DMA queue unstuck/fix detection */ + unsigned int last_super_num[__AR9170_NUM_TX_QUEUES]; + struct carl9170_tx_superframe *last_super[__AR9170_NUM_TX_QUEUES]; + unsigned int mac_reset; + unsigned int soft_int; + + /* rx filter */ + unsigned int rx_filter; + + /* tx sequence control counters */ + unsigned int sequence[CARL9170_INTF_NUM]; + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + /* CAB */ + struct dma_queue cab_queue[CARL9170_INTF_NUM]; + unsigned int cab_queue_len[CARL9170_INTF_NUM]; + unsigned int cab_flush_time; + enum carl9170_cab_trigger cab_flush_trigger[CARL9170_INTF_NUM]; +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + + /* tx status */ + unsigned int tx_status_pending, + tx_status_head_idx, + tx_status_tail_idx; + struct carl9170_tx_status tx_status_cache[CARL9170_TX_STATUS_NUM]; + + /* internal descriptor for use within the service routines */ + struct dma_desc *fw_desc; + unsigned int fw_desc_available; + void *fw_desc_data; + fw_desc_callback_t fw_desc_callback; + + /* BA(R) Request Handler */ + struct carl9170_bar_ctx ba_cache[CONFIG_CARL9170FW_BACK_REQS_NUM]; + unsigned int ba_tail_idx, + ba_head_idx, + queued_ba; + + unsigned int queued_bar; + } wlan; + + struct { + unsigned int config, + interface_setting, + alternate_interface_setting, + device_feature; + enum carl9170_ep0_action ep0_action; + + void *ep0_txrx_buffer; + unsigned int ep0_txrx_len, + ep0_txrx_pos; + + struct ar9170_usb_config *cfg_desc; + struct ar9170_usb_config *os_cfg_desc; + + /* + * special buffers for command & response handling + * + * the firmware uses a sort of ring-buffer to communicate + * to the host. + */ + unsigned int int_pending, + int_desc_available, + int_head_index, + int_tail_index; + struct dma_desc *int_desc; + struct carl9170_rsp int_buf[CARL9170_INT_RQ_CACHES]; + +#ifdef CONFIG_CARL9170FW_DEBUG_USB + /* USB printf */ + unsigned int put_index; + uint8_t put_buffer[CARL9170_MAX_CMD_PAYLOAD_LEN]; +#endif /* CONFIG_CARL9170FW_DEBUG_USB */ + + } usb; + + struct { +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + /* (cached) ar9170_rf_init */ + + /* PHY/RF state */ + unsigned int frequency; + unsigned int ht_settings; + + enum carl9170_phy_state state; + struct carl9170_psm psm; +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ + } phy; + + unsigned int tally_clock; + struct carl9170_tally_rsp tally; + unsigned int tx_time; + +#ifdef CONFIG_CARL9170FW_WOL + struct { + struct carl9170_wol_cmd cmd; + unsigned int last_beacon; + unsigned int lost_null; + unsigned int last_null; + bool wake_up; + } wol; +#endif /* CONFIG_CARL9170FW_WOL */ + +#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT + struct carl9170_gpio cached_gpio_state; +#endif /*CONFIG_CARL9170FW_GPIO_INTERRUPT */ +}; + +/* + * global firmware context struct. + * + * NOTE: This struct will zeroed out in start() + */ +extern struct firmware_context_struct fw; +#endif /* __CARL9170FW_CARL9170_H */ diff --git a/carl9170fw/carlfw/include/cmd.h b/carl9170fw/carlfw/include/cmd.h new file mode 100644 index 0000000..54dd350 --- /dev/null +++ b/carl9170fw/carlfw/include/cmd.h @@ -0,0 +1,55 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Firmware command interface definition + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, 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 __CARL9170FW_CMD_H +#define __CARL9170FW_CMD_H + +#include "config.h" +#include "compiler.h" +#include "types.h" + +#include "fwcmd.h" + +static inline void __check(void) +{ + BUILD_BUG_ON(sizeof(struct carl9170_cmd) != CARL9170_MAX_CMD_LEN); + BUILD_BUG_ON(sizeof(struct carl9170_rsp) != CARL9170_MAX_CMD_LEN); + BUILD_BUG_ON(sizeof(struct carl9170_set_key_cmd) != CARL9170_SET_KEY_CMD_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_disable_key_cmd) != CARL9170_DISABLE_KEY_CMD_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_rf_init) != CARL9170_RF_INIT_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_rf_init_result) != CARL9170_RF_INIT_RESULT_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_psm) != CARL9170_PSM_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_tsf_rsp) != CARL9170_TSF_RSP_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_bcn_ctrl_cmd) != CARL9170_BCN_CTRL_CMD_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_tx_status) != CARL9170_TX_STATUS_SIZE); + BUILD_BUG_ON(sizeof(struct _carl9170_tx_status) != CARL9170_TX_STATUS_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_gpio) != CARL9170_GPIO_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_rx_filter_cmd) != CARL9170_RX_FILTER_CMD_SIZE); + BUILD_BUG_ON(sizeof(struct carl9170_wol_cmd) != CARL9170_WOL_CMD_SIZE); +} + +void handle_cmd(struct carl9170_rsp *resp); + +#endif /* __CARL9170FW_CMD_H */ diff --git a/carl9170fw/carlfw/include/config.h b/carl9170fw/carlfw/include/config.h new file mode 100644 index 0000000..e7c358d --- /dev/null +++ b/carl9170fw/carlfw/include/config.h @@ -0,0 +1,64 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "generated/autoconf.h" +#include "version.h" +#include "types.h" +#include "compiler.h" +#include "fwcmd.h" +#include "hw.h" + +#ifndef __CARL9170FW_CONFIG_H +#define __CARL9170FW_CONFIG_H + +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + +#if GCC_VERSION < 40400 +# error "This firmware will not work if it is compiled with gcc versions < 4.4" +# error "See: http://gcc.gnu.org/gcc-4.4/changes.html / Caveats No. 4" +#endif + +#if ((defined CONFIG_CARL9170FW_PRINTF) && \ + (!defined CONFIG_CARL9170FW_DEBUG_USB) && \ + (!defined CONFIG_CARL9170FW_DEBUG_UART)) +# warning "You have disabled all debug message transports." +# warning "However CONFIG_CARL9170FW_PRINTF is still set..." +# warning "Which is a waste of firmware space, if you ask me." +#endif + +#define CARL9170_TX_STATUS_NUM (CARL9170_RSP_TX_STATUS_NUM) +#define CARL9170_INT_RQ_CACHES 16 +#define AR9170_INT_MAGIC_HEADER_SIZE 12 +#define CARL9170_TBTT_DELTA (CARL9170_PRETBTT_KUS + 1) + +#define CARL9170_GPIO_MASK (AR9170_GPIO_PORT_WPS_BUTTON_PRESSED) + +#ifdef CONFIG_CARL9170FW_VIFS_NUM +#define CARL9170_INTF_NUM (1 + CONFIG_CARL9170FW_VIFS_NUM) +#else +#define CARL9170_INTF_NUM (1) +#endif /* CONFIG_CARL9170FW_VIFS_NUM */ + +#define CONFIG_CARL9170FW_BACK_REQS_NUM 4 + +static inline void __config_check(void) +{ + BUILD_BUG_ON(!CARL9170_TX_STATUS_NUM); + BUILD_BUG_ON(CARL9170_INTF_NUM < 1); + BUILD_BUG_ON(CARL9170_INTF_NUM >= AR9170_MAX_VIRTUAL_MAC); +} + +#endif /* __CARL9170FW_CONFIG_H */ diff --git a/carl9170fw/carlfw/include/dma.h b/carl9170fw/carlfw/include/dma.h new file mode 100644 index 0000000..8f3a9df --- /dev/null +++ b/carl9170fw/carlfw/include/dma.h @@ -0,0 +1,349 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * This module contains DMA descriptor related definitions. + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, 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 __CARL9170FW_DMA_H +#define __CARL9170FW_DMA_H + +#include "config.h" +#include "types.h" +#include "compiler.h" +#include "hw.h" +#include "ieee80211.h" +#include "wlan.h" + +struct dma_desc { + volatile uint16_t status; /* Descriptor status */ + volatile uint16_t ctrl; /* Descriptor control */ + volatile uint16_t dataSize; /* Data size */ + volatile uint16_t totalLen; /* Total length */ + struct dma_desc *lastAddr; /* Last address of this chain */ + union { + uint8_t *_dataAddr; /* Data buffer address */ + void *dataAddr; + } __packed; + struct dma_desc *nextAddr; /* Next TD address */ +} __packed __aligned(4); + +/* Up, Dn, 5x Tx, retry, Rx, [USB Int], (CAB), FW */ +#define AR9170_TERMINATOR_NUMBER_B 10 + +#define AR9170_TERMINATOR_NUMBER_INT 1 + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE +#define AR9170_TERMINATOR_NUMBER_CAB CARL9170_INTF_NUM +#else +#define AR9170_TERMINATOR_NUMBER_CAB 0 +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + +#define AR9170_TERMINATOR_NUMBER (AR9170_TERMINATOR_NUMBER_B + \ + AR9170_TERMINATOR_NUMBER_INT + \ + AR9170_TERMINATOR_NUMBER_CAB) + +#define AR9170_BLOCK_SIZE (256 + 64) + +#define AR9170_DESCRIPTOR_SIZE (sizeof(struct dma_desc)) + +struct ar9170_tx_ba_frame { + struct ar9170_tx_hwdesc hdr; + struct ieee80211_ba ba; +} __packed; + +struct carl9170_tx_ba_superframe { + struct carl9170_tx_superdesc s; + struct ar9170_tx_ba_frame f; +} __packed; + +struct ar9170_tx_null_frame { + struct ar9170_tx_hwdesc hdr; + struct ieee80211_hdr null; +} __packed; + +struct carl9170_tx_null_superframe { + struct carl9170_tx_superdesc s; + struct ar9170_tx_null_frame f; +} __packed; + +#define CARL9170_BA_BUFFER_LEN (__roundup(sizeof(struct carl9170_tx_ba_superframe), 16)) +#define CARL9170_RSP_BUFFER_LEN AR9170_BLOCK_SIZE + +struct carl9170_sram_reserved { + union { + uint32_t buf[CARL9170_BA_BUFFER_LEN / sizeof(uint32_t)]; + struct carl9170_tx_ba_superframe ba; + } ba; + + union { + uint32_t buf[CARL9170_MAX_CMD_LEN / sizeof(uint32_t)]; + struct carl9170_cmd cmd; + +#ifdef CONFIG_CARL9170FW_WOL + struct carl9170_tx_null_superframe null; +#endif /* CONFIG_CARL9170FW_WOL */ + } cmd; + + union { + uint32_t buf[CARL9170_RSP_BUFFER_LEN / sizeof(uint32_t)]; + struct carl9170_rsp rsp; + } rsp; + + union { + uint32_t buf[CARL9170_INTF_NUM][AR9170_MAC_BCN_LENGTH_MAX / sizeof(uint32_t)]; + } bcn; +}; + +/* + * Memory layout in RAM: + * + * 0x100000 +-- + * | terminator descriptors (dma_desc) + * | - Up (to USB host) + * | - Down (from USB host) + * | - TX (5x, to wifi) + * | - AMPDU TX retry + * | - RX (from wifi) + * | - CAB Queue + * | - FW cmd & req descriptor + * | - BlockAck descriptor + * | total: AR9170_TERMINATOR_NUMBER + * +-- + * | block descriptors (dma_desc) + * | (AR9170_BLOCK_NUMBER) + * AR9170_BLOCK_BUFFER_BASE +-- align to multiple of 64 + * | block buffers (AR9170_BLOCK_SIZE each) + * | (AR9170_BLOCK_NUMBER) + * approx. 0x117c00 +-- + * | BA buffer (128 bytes) + * +-- + * | CMD buffer (128 bytes) + * | - used as NULLFRAME buffer (128 bytes) for WOL + * +-- + * | RSP buffer (320 bytes) + * +-- + * | BEACON buffer (256 bytes) + * +-- + * | unaccounted space / padding + * +-- + * 0x18000 + */ + +#define CARL9170_SRAM_RESERVED (sizeof(struct carl9170_sram_reserved)) + +#define AR9170_FRAME_MEMORY_SIZE (AR9170_SRAM_SIZE - CARL9170_SRAM_RESERVED) + +#define BLOCK_ALIGNMENT 64 + +#define NONBLOCK_DESCRIPTORS_SIZE \ + (AR9170_DESCRIPTOR_SIZE * (AR9170_TERMINATOR_NUMBER)) + +#define NONBLOCK_DESCRIPTORS_SIZE_ALIGNED \ + (ALIGN(NONBLOCK_DESCRIPTORS_SIZE, BLOCK_ALIGNMENT)) + +#define AR9170_BLOCK_NUMBER ((AR9170_FRAME_MEMORY_SIZE - NONBLOCK_DESCRIPTORS_SIZE_ALIGNED) / \ + (AR9170_BLOCK_SIZE + AR9170_DESCRIPTOR_SIZE)) + +struct ar9170_data_block { + uint8_t data[AR9170_BLOCK_SIZE]; +}; + +struct ar9170_dma_memory { + struct dma_desc terminator[AR9170_TERMINATOR_NUMBER]; + struct dma_desc block[AR9170_BLOCK_NUMBER]; + struct ar9170_data_block data[AR9170_BLOCK_NUMBER] __aligned(BLOCK_ALIGNMENT); + struct carl9170_sram_reserved reserved __aligned(BLOCK_ALIGNMENT); +}; + +extern struct ar9170_dma_memory dma_mem; + +#define AR9170_DOWN_BLOCK_RATIO 2 +#define AR9170_RX_BLOCK_RATIO 1 +/* Tx 16*2 = 32 packets => 32*(5*320) */ +#define AR9170_TX_BLOCK_NUMBER (AR9170_BLOCK_NUMBER * AR9170_DOWN_BLOCK_RATIO / \ + (AR9170_RX_BLOCK_RATIO + AR9170_DOWN_BLOCK_RATIO)) +#define AR9170_RX_BLOCK_NUMBER (AR9170_BLOCK_NUMBER - AR9170_TX_BLOCK_NUMBER) + +/* Error code */ +#define AR9170_ERR_FS_BIT 1 +#define AR9170_ERR_LS_BIT 2 +#define AR9170_ERR_OWN_BITS 3 +#define AR9170_ERR_DATA_SIZE 4 +#define AR9170_ERR_TOTAL_LEN 5 +#define AR9170_ERR_DATA 6 +#define AR9170_ERR_SEQ 7 +#define AR9170_ERR_LEN 8 + +/* Status bits definitions */ +/* Own bits definitions */ +#define AR9170_OWN_BITS 0x3 +#define AR9170_OWN_BITS_S 0 +#define AR9170_OWN_BITS_SW 0x0 +#define AR9170_OWN_BITS_HW 0x1 +#define AR9170_OWN_BITS_SE 0x2 + +/* Control bits definitions */ +#define AR9170_CTRL_TXFAIL 1 +#define AR9170_CTRL_BAFAIL 2 +#define AR9170_CTRL_FAIL (AR9170_CTRL_TXFAIL | AR9170_CTRL_BAFAIL) + +/* First segament bit */ +#define AR9170_CTRL_LS_BIT 0x100 +/* Last segament bit */ +#define AR9170_CTRL_FS_BIT 0x200 + +struct dma_queue { + struct dma_desc *head; + struct dma_desc *terminator; +}; + +#define DESC_PAYLOAD(a) ((void *)a->dataAddr) +#define DESC_PAYLOAD_OFF(a, offset) ((void *)((unsigned long)(a->_dataAddr) + offset)) + +struct dma_desc *dma_unlink_head(struct dma_queue *queue); +void dma_init_descriptors(void); +void dma_reclaim(struct dma_queue *q, struct dma_desc *desc); +void dma_put(struct dma_queue *q, struct dma_desc *desc); + +static inline __inline bool is_terminator(struct dma_queue *q, struct dma_desc *desc) +{ + return q->terminator == desc; +} + +static inline __inline bool queue_empty(struct dma_queue *q) +{ + return q->head == q->terminator; +} + +/* + * Get a completed packet with # descriptors. Return the first + * descriptor and pointer the head directly by lastAddr->nextAddr + */ +static inline __inline struct dma_desc *dma_dequeue_bits(struct dma_queue *q, + uint16_t bits) +{ + struct dma_desc *desc = NULL; + + if ((q->head->status & AR9170_OWN_BITS) == bits) + desc = dma_unlink_head(q); + + return desc; +} + +static inline __inline struct dma_desc *dma_dequeue_not_bits(struct dma_queue *q, + uint16_t bits) +{ + struct dma_desc *desc = NULL; + + /* AR9170_OWN_BITS_HW will be filtered out here too. */ + if ((q->head->status & AR9170_OWN_BITS) != bits) + desc = dma_unlink_head(q); + + return desc; +} + +#define for_each_desc_bits(desc, queue, bits) \ + while ((desc = dma_dequeue_bits(queue, bits))) + +#define for_each_desc_not_bits(desc, queue, bits) \ + while ((desc = dma_dequeue_not_bits(queue, bits))) + +#define for_each_desc(desc, queue) \ + while ((desc = dma_unlink_head(queue))) + +#define __for_each_desc_bits(desc, queue, bits) \ + for (desc = (queue)->head; \ + (desc != (queue)->terminator && \ + (desc->status & AR9170_OWN_BITS) == bits); \ + desc = desc->lastAddr->nextAddr) + +#define __while_desc_bits(desc, queue, bits) \ + for (desc = (queue)->head; \ + (!queue_empty(queue) && \ + (desc->status & AR9170_OWN_BITS) == bits); \ + desc = (queue)->head) + +#define __for_each_desc_continue(desc, queue) \ + for (; desc != (queue)->terminator; \ + desc = (desc)->lastAddr->nextAddr) + +#define __for_each_desc(desc, queue) \ + for (desc = (queue)->head; \ + desc != (queue)->terminator; \ + desc = (desc)->lastAddr->nextAddr) + +#define __for_each_desc_safe(desc, tmp, queue) \ + for (desc = (queue)->head, tmp = desc->lastAddr->nextAddr; \ + desc != (queue)->terminator; \ + desc = tmp, tmp = tmp->lastAddr->nextAddr) + +#define __while_subdesc(desc, queue) \ + for (desc = (queue)->head; \ + desc != (queue)->terminator; \ + desc = (desc)->nextAddr) + +static inline __inline unsigned int queue_len(struct dma_queue *q) +{ + struct dma_desc *desc; + unsigned int i = 0; + + __while_subdesc(desc, q) + i++; + + return i; +} + +/* + * rearm a completed packet, so it will be processed agian. + */ +static inline __inline void dma_rearm(struct dma_desc *desc) +{ + /* Set OWN bit to HW */ + desc->status = ((desc->status & (~AR9170_OWN_BITS)) | + AR9170_OWN_BITS_HW); +} + +static inline __inline void dma_fix_downqueue(struct dma_desc *desc) +{ + desc->status = AR9170_OWN_BITS_HW; + desc->ctrl = 0; + desc->dataSize = 0; + desc->totalLen = AR9170_BLOCK_SIZE; + desc->lastAddr = desc; +} + +static inline void __check_desc(void) +{ + struct ar9170_dma_memory mem; + BUILD_BUG_ON(sizeof(struct ar9170_data_block) != AR9170_BLOCK_SIZE); + BUILD_BUG_ON(sizeof(struct dma_desc) != 20); + + BUILD_BUG_ON(sizeof(mem) > AR9170_SRAM_SIZE); + + BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, ba.buf) & (BLOCK_ALIGNMENT - 1)); + BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, cmd.buf) & (BLOCK_ALIGNMENT - 1)); + BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, rsp.buf) & (BLOCK_ALIGNMENT - 1)); + BUILD_BUG_ON(offsetof(struct carl9170_sram_reserved, bcn.buf) & (BLOCK_ALIGNMENT - 1)); + BUILD_BUG_ON(sizeof(struct carl9170_tx_null_superframe) > CARL9170_MAX_CMD_LEN); +} + +#endif /* __CARL9170FW_DMA_H */ diff --git a/carl9170fw/carlfw/include/fwdsc.h b/carl9170fw/carlfw/include/fwdsc.h new file mode 100644 index 0000000..936bfed --- /dev/null +++ b/carl9170fw/carlfw/include/fwdsc.h @@ -0,0 +1,50 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Firmware 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 __CARL9170FW_FWDSC_H +#define __CARL9170FW_FWDSC_H + +#include "config.h" +#include "compiler.h" +#include "types.h" +#include "fwdesc.h" + +struct carl9170_firmware_descriptor { + struct carl9170fw_otus_desc otus; + struct carl9170fw_txsq_desc txsq; +#ifdef CONFIG_CARL9170FW_WOL + struct carl9170fw_wol_desc wol; +#endif /* CONFIG_CARL9170FW_WOL */ + struct carl9170fw_motd_desc motd; + struct carl9170fw_dbg_desc dbg; + struct carl9170fw_last_desc last; +} __packed; + +extern const struct carl9170_firmware_descriptor carl9170fw_desc; + +static inline void __check_fw(void) +{ + BUILD_BUG_ON(sizeof(carl9170fw_desc) & 0x3); + BUILD_BUG_ON(sizeof(carl9170fw_desc) > CARL9170FW_DESC_MAX_LENGTH); +} + +#endif /* __CARL9170FW_FWDSC_H */ diff --git a/carl9170fw/carlfw/include/gpio.h b/carl9170fw/carlfw/include/gpio.h new file mode 100644 index 0000000..97c1927 --- /dev/null +++ b/carl9170fw/carlfw/include/gpio.h @@ -0,0 +1,45 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * GPIO definitions + * + * 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 __CARL9170FW_GPIO_H +#define __CARL9170FW_GPIO_H + +#include "config.h" +#include "hw.h" +#include "io.h" + +static inline __inline void led_init(void) +{ + set(AR9170_GPIO_REG_PORT_TYPE, 3); +} + +static inline __inline void led_set(const unsigned int ledstate) +{ + set(AR9170_GPIO_REG_PORT_DATA, ledstate); +} + +#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT + +void gpio_timer(void); + +#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */ +#endif /* __CARL9170FW_GPIO_H */ diff --git a/carl9170fw/carlfw/include/hostif.h b/carl9170fw/carlfw/include/hostif.h new file mode 100644 index 0000000..8f9f139 --- /dev/null +++ b/carl9170fw/carlfw/include/hostif.h @@ -0,0 +1,47 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * HostIF definition + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, 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 __CARL9170FW_HOSTIF_H +#define __CARL9170FW_HOSTIF_H + +#include "config.h" +#include "compiler.h" +#include "types.h" +#include "hw.h" +#include "io.h" + +static inline __inline void down_trigger(void) +{ + set(AR9170_PTA_REG_DN_DMA_TRIGGER, 1); +} + +static inline __inline void up_trigger(void) +{ + set(AR9170_PTA_REG_UP_DMA_TRIGGER, 1); +} + +void handle_host_interface(void); + +#endif /* __CARL9170FW_HOSTIF_H */ diff --git a/carl9170fw/carlfw/include/io.h b/carl9170fw/carlfw/include/io.h new file mode 100644 index 0000000..4ccdc6e --- /dev/null +++ b/carl9170fw/carlfw/include/io.h @@ -0,0 +1,153 @@ +/* + * 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 __CARL9170FW_IO_H +#define __CARL9170FW_IO_H + +#include "config.h" +#include "types.h" +#include "compiler.h" + +static inline __inline uint8_t readb(const volatile void *addr) +{ + return *(const volatile uint8_t *) addr; +} + +static inline __inline uint16_t readw(const volatile void *addr) +{ + return *(const volatile uint16_t *) addr; +} + +static inline __inline volatile void *readp(const volatile void *addr) +{ + return *(volatile void **) addr; +} + +static inline __inline uint32_t readl(const volatile void *addr) +{ + return *(const volatile unsigned int *) addr; +} + +static inline __inline void writeb(volatile void *addr, const volatile uint8_t val) +{ + *(volatile uint8_t *) addr = val; +} + +static inline __inline void writew(volatile void *addr, const volatile uint16_t val) +{ + *(volatile uint16_t *) addr = val; +} + +static inline __inline void writel(volatile void *addr, const volatile uint32_t val) +{ + *(volatile uint32_t *) addr = val; +} + +static inline __inline void __orl(volatile void *addr, const volatile uint32_t val) +{ + *(volatile uint32_t *) addr |= val; +} + +static inline __inline void __andl(volatile void *addr, const volatile uint32_t val) +{ + *(volatile uint32_t *) addr &= val; +} + +static inline __inline void __xorl(volatile void *addr, const volatile uint32_t val) +{ + *(volatile uint32_t *) addr ^= val; +} + +static inline __inline void __incl(volatile void *addr) +{ + (*(volatile uint32_t *)addr)++; +} + +static inline __inline uint32_t readl_async(const volatile void *addr) +{ + uint32_t i = 0, read, tmp; + + read = readl(addr); + do { + tmp = read; + tmp = readl(addr); + i++; + } while (tmp != read && i <= 10); + + return read; +} + +static inline __inline void set(const volatile uint32_t addr, const volatile uint32_t val) +{ + writel((volatile void *) addr, val); +} + +static inline __inline void orl(volatile uint32_t addr, const volatile uint32_t val) +{ + __orl((volatile void *) addr, val); +} + +static inline __inline void xorl(const volatile uint32_t addr, const volatile uint32_t val) +{ + __xorl((volatile void *) addr, val); +} + +static inline __inline void andl(const volatile uint32_t addr, const volatile uint32_t val) +{ + __andl((volatile void *) addr, val); +} + +static inline __inline void incl(const volatile uint32_t addr) +{ + __incl((volatile void *) addr); +} + +static inline __inline uint32_t get(const volatile uint32_t addr) +{ + return readl((volatile void *) addr); +} + +static inline __inline volatile void *getp(const volatile uint32_t addr) +{ + return readp((const volatile void *) addr); +} + +static inline __inline uint32_t get_async(const volatile uint32_t addr) +{ + return readl_async((const volatile void *) addr); +} + +static inline __inline void setb(const volatile uint32_t addr, const volatile uint8_t val) +{ + writeb((volatile void *) addr, val); +} + +static inline __inline uint8_t getb(const volatile uint32_t addr) +{ + return readb((const volatile void *) addr); +} + +static inline __inline void andb(const volatile uint32_t addr, const volatile uint8_t val) +{ + setb(addr, getb(addr) & val); +} + +static inline __inline void orb(const volatile uint32_t addr, const volatile uint32_t val) +{ + setb(addr, getb(addr) | val); +} + +#endif /* __CARL9170FW_IO_H */ diff --git a/carl9170fw/carlfw/include/printf.h b/carl9170fw/carlfw/include/printf.h new file mode 100644 index 0000000..dd4559e --- /dev/null +++ b/carl9170fw/carlfw/include/printf.h @@ -0,0 +1,107 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * printf and his friends... + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, 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 __CARL9170FW_PRINTF_H +#define __CARL9170FW_PRINTF_H + +#include <stdarg.h> +#include <string.h> +#include "config.h" +#include "carl9170.h" +#include "uart.h" +#include "fwcmd.h" + +#ifdef CONFIG_CARL9170FW_PRINTF +void __attribute__((format (printf, 1, 2))) tfp_printf(const char *fmt, ...); + +#define printf tfp_printf + +#else +void __attribute__((format (printf, 1, 2))) min_printf(const char *fmt, ...); + +#define printf min_printf +#endif /* CONFIG_CARL9170FW_PRINTF */ + +#define PRINT(fmt, args...) \ + do { \ + printf(fmt, ## args); \ + } while (0) + +#define INFO(fmt, args...) PRINT(fmt, ## args) + +#define ERR(fmt, args...) PRINT(CARL9170_ERR_MAGIC fmt, ## args) + +#ifdef CONFIG_CARL9170FW_DEBUG +#define DBG(fmt, args...) PRINT(fmt, ## args) +#else +#define DBG(...) do { } while (0); +#endif + +/* + * NB: even though the MACRO is called "stall". It isn't supposed + * to stall since this will render the device unresponsive, until + * someone pulls the plug. + */ +#define STALL() + +#define BUG(fmt, args...) \ + do { \ + PRINT(CARL9170_BUG_MAGIC" %s()@%d \"" fmt "\"" , \ + __func__, __LINE__, ## args); \ + STALL() \ + } while (0); + +#define BUG_ON(condition) \ + ({ \ + int __ret = !!(condition); \ + if (unlikely(!!(__ret))) \ + BUG(#condition); \ + (__ret); \ + }) + +static inline __inline void putcharacter(const char c __unused) +{ +#ifdef CONFIG_CARL9170FW_DEBUG_USB + usb_putc(c); +#endif /* CONFIG_CARL9170FW_DEBUG_USB */ + +#ifdef CONFIG_CARL9170FW_DEBUG_UART + uart_putc(c); +#endif /* CONFIG_CARL9170FW_DEBUG_UART */ +} + +static inline __inline void print_hex_dump(const void *buf __unused, int len __unused) +{ +#ifdef CONFIG_CARL9170FW_DEBUG_USB + usb_print_hex_dump(buf, len); +#endif /* CONFIG_CARL9170FW_DEBUG_USB */ + +#ifdef CONFIG_CARL9170FW_DEBUG_UART + uart_print_hex_dump(buf, len); +#endif /* CONFIG_CARL9170FW_DEBUG_UART */ +} + +#endif /* __CARL9170FW_PRINTF_H */ + diff --git a/carl9170fw/carlfw/include/rf.h b/carl9170fw/carlfw/include/rf.h new file mode 100644 index 0000000..4419bcb --- /dev/null +++ b/carl9170fw/carlfw/include/rf.h @@ -0,0 +1,37 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * RF routine definitions + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, 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 __CARL9170FW_RF_H +#define __CARL9170FW_RF_H + +#include "config.h" + +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS +void rf_notify_set_channel(void); +void rf_cmd(const struct carl9170_cmd *cmd, struct carl9170_rsp *resp); +void rf_psm(void); +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ + +#endif /* __CARL9170FW_RF_H */ diff --git a/carl9170fw/carlfw/include/rom.h b/carl9170fw/carlfw/include/rom.h new file mode 100644 index 0000000..922c5c8 --- /dev/null +++ b/carl9170fw/carlfw/include/rom.h @@ -0,0 +1,82 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * ROM layout + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, 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 __CARL9170FW_ROM_H +#define __CARL9170FW_ROM_H + +#include "types.h" +#include "config.h" +#include "compiler.h" +#include "usb.h" +#include "eeprom.h" + +struct ar9170_hwtype { + /* 0x00001370 */ + uint8_t data[4]; + + /* 0x00001374 */ + struct ar9170_led_mode led_mode[AR9170_NUM_LEDS]; + + /* 0x00001378 */ + uint8_t nulldata[2]; + + struct { + /* 0x0000137a */ + struct usb_device_descriptor device_desc; + + /* 0x0000138c */ + uint8_t string0_desc[4]; + + /* 0x00001390 */ + uint8_t string1_desc[32]; + + /* 0x000013b0 */ + uint8_t string2_desc[48]; + + /* 0x000013e0 */ + uint8_t string3_desc[32]; + } usb; +} __packed; + +struct ar9170_rom { + /* 0x00000000 */ + uint32_t *irq_table[2]; + + /* 0x00000008 */ + uint8_t bootcode[4968]; + + /* 0x00001370 */ + struct ar9170_hwtype hw; + + /* 0x00001400 */ + uint8_t data[512]; + + /* eeprom */ + struct ar9170_eeprom sys; +} __packed; + +static const struct ar9170_rom rom __section(eeprom); + +#endif /* __CARL9170FW_ROM_H */ diff --git a/carl9170fw/carlfw/include/timer.h b/carl9170fw/carlfw/include/timer.h new file mode 100644 index 0000000..1c1c6cd --- /dev/null +++ b/carl9170fw/carlfw/include/timer.h @@ -0,0 +1,88 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Clock, Timer & Timing + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, 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 __CARL9170FW_TIMER_H +#define __CARL9170FW_TIMER_H + +#include "config.h" + +enum cpu_clock_t { + AHB_40MHZ_OSC = 0, + AHB_20_22MHZ = 1, + AHB_40_44MHZ = 2, + AHB_80_88MHZ = 3 +}; + +static inline __inline uint32_t get_clock_counter(void) +{ + return (get(AR9170_TIMER_REG_CLOCK_HIGH) << 16) | get(AR9170_TIMER_REG_CLOCK_LOW); +} + +/* + * works only up to 97 secs [44 MHz] or 107 secs for 40 MHz + * Also, the delay wait will be affected by 2.4GHz<->5GHz + * band changes. + */ +static inline __inline bool is_after_msecs(const uint32_t t0, const uint32_t msecs) +{ + return ((get_clock_counter() - t0) / 1000) > (msecs * fw.ticks_per_usec); +} + +/* + * Note: Be careful with [u]delay. They won't service the + * hardware watchdog timer. It might trigger if you + * wait long enough. Also they don't terminate if sec is + * above 97 sec [44MHz] or more than 107 sec [40MHz]. + */ +static inline __inline void delay(const uint32_t msec) +{ + uint32_t t1, t2, dt, wt; + + wt = msec * fw.ticks_per_usec; + + t1 = get_clock_counter(); + while (1) { + t2 = get_clock_counter(); + dt = (t2 - t1) / 1000; + if (dt >= wt) + break; + } +} + +static inline __inline void udelay(const uint32_t usec) +{ + uint32_t t1, t2, dt; + + t1 = get_clock_counter(); + while (1) { + t2 = get_clock_counter(); + dt = (t2 - t1); + if (dt >= (usec * fw.ticks_per_usec)) + break; + } +} + +void clock_set(enum cpu_clock_t _clock, bool on); +#endif /* __CARL9170FW_TIMER_H */ diff --git a/carl9170fw/carlfw/include/uart.h b/carl9170fw/carlfw/include/uart.h new file mode 100644 index 0000000..1cfa29d --- /dev/null +++ b/carl9170fw/carlfw/include/uart.h @@ -0,0 +1,37 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * UART functions definition + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, 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 __CARL9170FW_UART_H +#define __CARL9170FW_UART_H + +#include "config.h" + +#ifdef CONFIG_CARL9170FW_DEBUG_UART +void uart_putc(const char c); +void uart_print_hex_dump(const void *buf, const int len); +void uart_init(void); +#endif /* CONFIG_CARL9170FW_DEBUG_UART */ + +#endif /* __CARL9170FW_UART_H */ diff --git a/carl9170fw/carlfw/include/usb.h b/carl9170fw/carlfw/include/usb.h new file mode 100644 index 0000000..cea1d4d --- /dev/null +++ b/carl9170fw/carlfw/include/usb.h @@ -0,0 +1,191 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * USB definitions + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, 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 __CARL9170FW_USB_H +#define __CARL9170FW_USB_H + +#include "config.h" +#include "types.h" +#include "io.h" +#include "hw.h" +#include "ch9.h" + +struct ar9170_usb_config { + struct usb_config_descriptor cfg; + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor ep[AR9170_USB_NUM_EXTRA_EP]; +} __packed; + +static inline __inline bool usb_detect_highspeed(void) +{ + return !!(getb(AR9170_USB_REG_MAIN_CTRL) & + AR9170_USB_MAIN_CTRL_HIGHSPEED); +} + +static inline __inline bool usb_configured(void) +{ + return !!(getb(AR9170_USB_REG_DEVICE_ADDRESS) & + AR9170_USB_DEVICE_ADDRESS_CONFIGURE); +} + +static inline __inline void usb_enable_remote_wakeup(void) +{ + orb(AR9170_USB_REG_MAIN_CTRL, AR9170_USB_MAIN_CTRL_REMOTE_WAKEUP); +} + +static inline __inline void usb_disable_remote_wakeup(void) +{ + andb(AR9170_USB_REG_MAIN_CTRL, ~AR9170_USB_MAIN_CTRL_REMOTE_WAKEUP); +} + +static inline __inline void usb_enable_global_int(void) +{ + orb(AR9170_USB_REG_MAIN_CTRL, AR9170_USB_MAIN_CTRL_ENABLE_GLOBAL_INT); +} + +static inline __inline void usb_trigger_out(void) +{ + andb(AR9170_USB_REG_INTR_MASK_BYTE_4, + (uint8_t) ~AR9170_USB_INTR_DISABLE_OUT_INT); +} + +static inline __inline void usb_reset_out(void) +{ + orb(AR9170_USB_REG_INTR_MASK_BYTE_4, AR9170_USB_INTR_DISABLE_OUT_INT); +} + +static inline __inline void usb_trigger_in(void) +{ + andb(AR9170_USB_REG_INTR_MASK_BYTE_6, ~AR9170_USB_INTR_DISABLE_IN_INT); +} + +static inline __inline void usb_reset_in(void) +{ + orb(AR9170_USB_REG_INTR_MASK_BYTE_6, AR9170_USB_INTR_DISABLE_IN_INT); +} + +static inline __inline void usb_ep3_xfer_done(void) +{ + orb(AR9170_USB_REG_EP3_BYTE_COUNT_HIGH, 0x08); +} + +static inline __inline void usb_suspend_ack(void) +{ + /* + * uP must do-over everything it should handle + * and do before into the suspend mode + */ + andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(2)); +} + +static inline __inline void usb_resume_ack(void) +{ + /* + * uP must do-over everything it should handle + * and do before into the suspend mode + */ + + andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(3)); +} + +static inline __inline void usb_reset_ack(void) +{ + andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(1)); +} + +static inline __inline void usb_data_out0Byte(void) +{ + andb(AR9170_USB_REG_INTR_SOURCE_7, (uint8_t) ~BIT(7)); +} + +static inline __inline void usb_data_in0Byte(void) +{ + andb(AR9170_USB_REG_INTR_SOURCE_7, ~BIT(6)); +} + +static inline __inline void usb_stop_down_queue(void) +{ + andl(AR9170_USB_REG_DMA_CTL, ~AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE); +} + +static inline __inline void usb_start_down_queue(void) +{ + orl(AR9170_USB_REG_DMA_CTL, AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE); +} + +static inline __inline void usb_clear_input_ep_toggle(unsigned int ep) +{ + andl(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (ep << 1), + ~AR9170_USB_EP_IN_TOGGLE); +} + +static inline __inline void usb_set_input_ep_toggle(unsigned int ep) +{ + orl(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (ep << 1), + AR9170_USB_EP_IN_TOGGLE); +} + +static inline __inline void usb_clear_output_ep_toggle(unsigned int ep) +{ + andl(AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH + (ep << 1), + ~AR9170_USB_EP_OUT_TOGGLE); +} + +static inline __inline void usb_set_output_ep_toggle(unsigned int ep) +{ + orl(AR9170_USB_REG_EP_OUT_MAX_SIZE_HIGH + (ep << 1), + AR9170_USB_EP_OUT_TOGGLE); +} + +static inline void usb_structure_check(void) +{ + BUILD_BUG_ON(sizeof(struct usb_config_descriptor) != USB_DT_CONFIG_SIZE); + BUILD_BUG_ON(sizeof(struct usb_device_descriptor) != USB_DT_DEVICE_SIZE); + BUILD_BUG_ON(sizeof(struct usb_endpoint_descriptor) != USB_DT_ENDPOINT_SIZE); + BUILD_BUG_ON(sizeof(struct usb_interface_descriptor) != USB_DT_INTERFACE_SIZE); +} + +void __noreturn jump_to_bootcode(void); + +void send_cmd_to_host(const uint8_t len, const uint8_t type, + const uint8_t ext, const uint8_t *body); + +void usb_init(void); +void usb_ep0rx(void); +void usb_ep0tx(void); +void usb_ep0setup(void); +void handle_usb(void); + +void usb_timer(void); +void usb_putc(const char c); +void usb_print_hex_dump(const void *buf, int len); + +void usb_init_highspeed_fifo_cfg(void); +void usb_init_fullspeed_fifo_cfg(void); + +void __noreturn start(void); +void __noreturn reboot(void); + +#endif /* __CARL9170FW_USB_H */ diff --git a/carl9170fw/carlfw/include/usb_fifo.h b/carl9170fw/carlfw/include/usb_fifo.h new file mode 100644 index 0000000..5d84c88 --- /dev/null +++ b/carl9170fw/carlfw/include/usb_fifo.h @@ -0,0 +1,244 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * USB definitions + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, 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 __CARL9170FW_USB_FIFO_H +#define __CARL9170FW_USB_FIFO_H + +#include "config.h" + +#define MASK_F0 0xf0 + +/* Block Size define */ +#define BLK512BYTE 1 +#define BLK1024BYTE 2 + +#define BLK64BYTE 1 +#define BLK128BYTE 2 + +/* Block toggle number define */ +#define SINGLE_BLK 1 +#define DOUBLE_BLK 2 +#define TRIBLE_BLK 3 + +/* Endpoint transfer type */ +#define TF_TYPE_ISOCHRONOUS 1 +#define TF_TYPE_BULK 2 +#define TF_TYPE_INTERRUPT 3 + +/* Endpoint or FIFO direction define */ +#define DIRECTION_IN 0 +#define DIRECTION_OUT 1 + +#define HS_C1_I0_A0_EP1_MAX_PACKET 512 +#define HS_C1_I0_A0_EP1_bInterval 0 + +#define HS_C1_I0_A0_EP_NUMBER 0x04 +#define HS_C1_I0_A0_EP_LENGTH (EP_LENGTH * HS_C1_I0_A0_EP_NUMBER) +#define HS_C1_I0_ALT_LENGTH (HS_C1_I0_A0_EP_LENGTH) +#define HS_C1_INTERFACE_LENGTH (HS_C1_I0_ALT_LENGTH) + +#define HS_C1_CONFIG_TOTAL_LENGTH (CONFIG_LENGTH + INTERFACE_LENGTH + HS_C1_INTERFACE_LENGTH) +#define FS_C1_CONFIG_TOTAL_LENGTH (CONFIG_LENGTH + INTERFACE_LENGTH + FS_C1_INTERFACE_LENGTH) + +#define FS_C1_I0_A0_EP1_MAX_PACKET 64 +/* #define FS_C1_I0_A0_EP1_bInterval HS_C1_I0_A0_EP1_bInterval */ + +#define HS_CONFIGURATION_NUMBER 1 +#define FS_CONFIGURATION_NUMBER 1 + +#define fDOUBLE_BUF 1 +#define fDOUBLE_BUF_IN 0 + +#define fFLASH_DISK 0 +#define fENABLE_ISO 0 + +#define HS_C1_INTERFACE_NUMBER 0x01 +#define HS_C1 0x01 +#define HS_C1_iConfiguration 0x00 +#define HS_C1_bmAttribute 0x80 + +#define HS_C1_iMaxPower 0xFA + +/* Interface 0 */ +#define HS_C1_I0_ALT_NUMBER 0X01 +/* AlternateSetting 0 */ +#define HS_C1_I0_A0_bInterfaceNumber 0x00 +#define HS_C1_I0_A0_bAlternateSetting 0x00 +/* JWEI 2003/07/14 */ +#define HS_C1_I0_A0_EP_NUMBER 0x04 +#define HS_C1_I0_A0_bInterfaceClass 0xff +#define HS_C1_I0_A0_bInterfaceSubClass 0x00 +#define HS_C1_I0_A0_bInterfaceProtocol 0x00 +#define HS_C1_I0_A0_iInterface 0x00 + +/* EP 1 */ +#define HS_C1_I0_A0_EP1_BLKSIZE 512 +#define HS_C1_I0_A0_EP1_BLKNO DOUBLE_BLK +#define HS_C1_I0_A0_EP1_DIRECTION DIRECTION_OUT +#define HS_C1_I0_A0_EP1_TYPE TF_TYPE_BULK + +#define HS_C1_I0_A0_EP1_MAX_PACKET 512 +#define HS_C1_I0_A0_EP1_bInterval 0 + +/* EP 2 */ +#define HS_C1_I0_A0_EP2_BLKSIZE 512 +/* JWEI 2003/08/20 */ +#define HS_C1_I0_A0_EP2_BLKNO SINGLE_BLK +#define HS_C1_I0_A0_EP2_DIRECTION DIRECTION_IN +#define HS_C1_I0_A0_EP2_TYPE TF_TYPE_BULK +#define HS_C1_I0_A0_EP2_MAX_PACKET 512 +#define HS_C1_I0_A0_EP2_bInterval 0 + +/* EP 3 */ +#define HS_C1_I0_A0_EP3_BLKSIZE 64 +#define HS_C1_I0_A0_EP3_BLKNO SINGLE_BLK +#define HS_C1_I0_A0_EP3_DIRECTION DIRECTION_IN +#define HS_C1_I0_A0_EP3_TYPE TF_TYPE_INTERRUPT +#define HS_C1_I0_A0_EP3_MAX_PACKET 0x0040 +#define HS_C1_I0_A0_EP3_bInterval 01 + +/* + * Note: HS Bulk type require max pkt size = 512 + * ==> must use Interrupt type for max pkt size = 64 + */ + +/* EP 4 */ +#define HS_C1_I0_A0_EP4_BLKSIZE 64 +#define HS_C1_I0_A0_EP4_BLKNO SINGLE_BLK +#define HS_C1_I0_A0_EP4_DIRECTION DIRECTION_OUT +#define HS_C1_I0_A0_EP4_TYPE TF_TYPE_INTERRUPT +#define HS_C1_I0_A0_EP4_MAX_PACKET 0x0040 +#define HS_C1_I0_A0_EP4_bInterval 01 + +#define HS_C1_I0_A0_EP_LENGTH (EP_LENGTH * HS_C1_I0_A0_EP_NUMBER) +/* EP 1 */ +#define HS_C1_I0_A0_EP1_FIFO_START 0 +#define HS_C1_I0_A0_EP1_FIFO_NO (HS_C1_I0_A0_EP1_BLKNO * HS_C1_I0_A0_EP1_BLKSIZE) +#define HS_C1_I0_A0_EP1_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP1_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP1_BLKNO - 1) << 2) | HS_C1_I0_A0_EP1_TYPE) +#define HS_C1_I0_A0_EP1_FIFO_MAP (((1 - HS_C1_I0_A0_EP1_DIRECTION) << 4) | 1) +#define HS_C1_I0_A0_EP1_MAP (HS_C1_I0_A0_EP1_FIFO_START | (HS_C1_I0_A0_EP1_FIFO_START << 4) | (MASK_F0 >> (4*HS_C1_I0_A0_EP1_DIRECTION))) + +/* EP 2 */ +#define HS_C1_I0_A0_EP2_FIFO_START (uint8_t)(HS_C1_I0_A0_EP1_FIFO_START + HS_C1_I0_A0_EP1_FIFO_NO) +#define HS_C1_I0_A0_EP2_FIFO_NO (uint8_t)(HS_C1_I0_A0_EP2_BLKNO * HS_C1_I0_A0_EP2_BLKSIZE) +#define HS_C1_I0_A0_EP2_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP2_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP2_BLKNO - 1) << 2) | HS_C1_I0_A0_EP2_TYPE) +#define HS_C1_I0_A0_EP2_FIFO_MAP (uint8_t)(((1 - HS_C1_I0_A0_EP2_DIRECTION) << 4) | 2) +#define HS_C1_I0_A0_EP2_MAP (uint8_t)(HS_C1_I0_A0_EP2_FIFO_START | (HS_C1_I0_A0_EP2_FIFO_START << 4) | (MASK_F0 >> (4*HS_C1_I0_A0_EP2_DIRECTION))) + +/* EP 3 */ +#define HS_C1_I0_A0_EP3_FIFO_START 14 +#define HS_C1_I0_A0_EP3_FIFO_NO (HS_C1_I0_A0_EP3_BLKNO * HS_C1_I0_A0_EP3_BLKSIZE) +#define HS_C1_I0_A0_EP3_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP3_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP3_BLKNO - 1) << 2) | HS_C1_I0_A0_EP3_TYPE) +#define HS_C1_I0_A0_EP3_FIFO_MAP (uint8_t)(((1 - HS_C1_I0_A0_EP3_DIRECTION) << 4) | 3) +#define HS_C1_I0_A0_EP3_MAP (uint8_t)(HS_C1_I0_A0_EP3_FIFO_START | (HS_C1_I0_A0_EP3_FIFO_START << 4) | (MASK_F0 >> (4*HS_C1_I0_A0_EP3_DIRECTION))) + +/* EP 4 */ +#define HS_C1_I0_A0_EP4_FIFO_START (HS_C1_I0_A0_EP3_FIFO_START + HS_C1_I0_A0_EP3_FIFO_NO) +#define HS_C1_I0_A0_EP4_FIFO_NO (HS_C1_I0_A0_EP4_BLKNO * HS_C1_I0_A0_EP4_BLKSIZE) +#define HS_C1_I0_A0_EP4_FIFO_CONFIG (uint8_t)(0x80 | ((HS_C1_I0_A0_EP4_BLKSIZE - 1) << 4) | ((HS_C1_I0_A0_EP4_BLKNO - 1) << 2) | HS_C1_I0_A0_EP4_TYPE) +#define HS_C1_I0_A0_EP4_FIFO_MAP (((1 - HS_C1_I0_A0_EP4_DIRECTION) << 4) | 4) +#define HS_C1_I0_A0_EP4_MAP (uint8_t)(HS_C1_I0_A0_EP4_FIFO_START | (HS_C1_I0_A0_EP4_FIFO_START << 4) | (MASK_F0 >> (4*HS_C1_I0_A0_EP4_DIRECTION))) + +/* Configuration 1 */ +#define FS_C1_INTERFACE_NUMBER 0x01 +#define FS_C1 0x01 +#define FS_C1_iConfiguration 0x00 +#define FS_C1_bmAttribute 0x80 +#define FS_C1_iMaxPower 0xfa + +/* Interface 0 */ +#define FS_C1_I0_ALT_NUMBER 0x01 +/* AlternateSetting 0x00 */ +#define FS_C1_I0_A0_bInterfaceNumber 0x00 +#define FS_C1_I0_A0_bAlternateSetting 0x00 +#define FS_C1_I0_A0_EP_NUMBER 0x04 +#define FS_C1_I0_A0_bInterfaceClass 0xff +#define FS_C1_I0_A0_bInterfaceSubClass 0x00 +#define FS_C1_I0_A0_bInterfaceProtocol 0x00 + +/* EP 1 */ +#define FS_C1_I0_A0_EP1_BLKSIZE 512 +/* JWEI 2003/05/19 */ +#define FS_C1_I0_A0_EP1_BLKNO DOUBLE_BLK +#define FS_C1_I0_A0_EP1_DIRECTION DIRECTION_OUT +#define FS_C1_I0_A0_EP1_TYPE TF_TYPE_BULK +#define FS_C1_I0_A0_EP1_MAX_PACKET 64 +#define FS_C1_I0_A0_EP1_bInterval 0 + +/* EP 2 */ +#define FS_C1_I0_A0_EP2_BLKSIZE 512 +/* JWEI 2003/08/20 */ +#define FS_C1_I0_A0_EP2_BLKNO SINGLE_BLK +#define FS_C1_I0_A0_EP2_DIRECTION DIRECTION_IN +#define FS_C1_I0_A0_EP2_TYPE TF_TYPE_BULK +#define FS_C1_I0_A0_EP2_MAX_PACKET 64 +#define FS_C1_I0_A0_EP2_bInterval 0 + +/* EP 3 */ +#define FS_C1_I0_A0_EP3_BLKSIZE 64 +#define FS_C1_I0_A0_EP3_BLKNO SINGLE_BLK +#define FS_C1_I0_A0_EP3_DIRECTION DIRECTION_IN +#define FS_C1_I0_A0_EP3_TYPE TF_TYPE_INTERRUPT +#define FS_C1_I0_A0_EP3_MAX_PACKET 0x0040 +#define FS_C1_I0_A0_EP3_bInterval 1 + +/* EP 4 */ +#define FS_C1_I0_A0_EP4_BLKSIZE 64 +#define FS_C1_I0_A0_EP4_BLKNO SINGLE_BLK +#define FS_C1_I0_A0_EP4_DIRECTION DIRECTION_OUT +#define FS_C1_I0_A0_EP4_TYPE TF_TYPE_BULK +#define FS_C1_I0_A0_EP4_MAX_PACKET 0x0040 +#define FS_C1_I0_A0_EP4_bInterval 0 + +#define FS_C1_I0_A0_EP_LENGTH (EP_LENGTH * FS_C1_I0_A0_EP_NUMBER) +/* EP 1 */ +#define FS_C1_I0_A0_EP1_FIFO_START 0 +#define FS_C1_I0_A0_EP1_FIFO_NO (uint8_t)(FS_C1_I0_A0_EP1_BLKNO * FS_C1_I0_A0_EP1_BLKSIZE) +#define FS_C1_I0_A0_EP1_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP1_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP1_BLKNO - 1) << 2) | FS_C1_I0_A0_EP1_TYPE) +#define FS_C1_I0_A0_EP1_FIFO_MAP (uint8_t)(((1 - FS_C1_I0_A0_EP1_DIRECTION) << 4) | 1) +#define FS_C1_I0_A0_EP1_MAP (uint8_t)(FS_C1_I0_A0_EP1_FIFO_START | (FS_C1_I0_A0_EP1_FIFO_START << 4) | (MASK_F0 >> (4*FS_C1_I0_A0_EP1_DIRECTION))) + +/* EP 2 */ +#define FS_C1_I0_A0_EP2_FIFO_START (uint8_t)(FS_C1_I0_A0_EP1_FIFO_START + FS_C1_I0_A0_EP1_FIFO_NO) +#define FS_C1_I0_A0_EP2_FIFO_NO (uint8_t)(FS_C1_I0_A0_EP2_BLKNO * FS_C1_I0_A0_EP2_BLKSIZE) +#define FS_C1_I0_A0_EP2_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP2_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP2_BLKNO - 1) << 2) | FS_C1_I0_A0_EP2_TYPE) +#define FS_C1_I0_A0_EP2_FIFO_MAP (uint8_t)(((1 - FS_C1_I0_A0_EP2_DIRECTION) << 4) | 2) +#define FS_C1_I0_A0_EP2_MAP (uint8_t)(FS_C1_I0_A0_EP2_FIFO_START | (FS_C1_I0_A0_EP2_FIFO_START << 4) | (MASK_F0 >> (4*FS_C1_I0_A0_EP2_DIRECTION))) + +/* EP 3 */ +#define FS_C1_I0_A0_EP3_FIFO_START 14 +#define FS_C1_I0_A0_EP3_FIFO_NO (uint8_t)(FS_C1_I0_A0_EP3_BLKNO * FS_C1_I0_A0_EP3_BLKSIZE) +#define FS_C1_I0_A0_EP3_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP3_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP3_BLKNO - 1) << 2) | FS_C1_I0_A0_EP3_TYPE) +#define FS_C1_I0_A0_EP3_FIFO_MAP (uint8_t)(((1 - FS_C1_I0_A0_EP3_DIRECTION) << 4) | 3) +#define FS_C1_I0_A0_EP3_MAP (uint8_t)(FS_C1_I0_A0_EP3_FIFO_START | (FS_C1_I0_A0_EP3_FIFO_START << 4) | (MASK_F0 >> (4*FS_C1_I0_A0_EP3_DIRECTION))) + +/* EP 4 */ +#define FS_C1_I0_A0_EP4_FIFO_START (uint8_t)(FS_C1_I0_A0_EP3_FIFO_START + FS_C1_I0_A0_EP3_FIFO_NO) +#define FS_C1_I0_A0_EP4_FIFO_NO (uint8_t)(FS_C1_I0_A0_EP4_BLKNO * FS_C1_I0_A0_EP4_BLKSIZE) +#define FS_C1_I0_A0_EP4_FIFO_CONFIG (uint8_t)(0x80 | ((FS_C1_I0_A0_EP4_BLKSIZE - 1) << 4) | ((FS_C1_I0_A0_EP4_BLKNO - 1) << 2) | FS_C1_I0_A0_EP4_TYPE) +#define FS_C1_I0_A0_EP4_FIFO_MAP (uint8_t)(((1 - FS_C1_I0_A0_EP4_DIRECTION) << 4) | 4) +#define FS_C1_I0_A0_EP4_MAP (uint8_t)(FS_C1_I0_A0_EP4_FIFO_START | (FS_C1_I0_A0_EP4_FIFO_START << 4) | (MASK_F0 >> (4*FS_C1_I0_A0_EP4_DIRECTION))) + +#endif /* __CARL9170FW_USB_FIFO_H */ diff --git a/carl9170fw/carlfw/include/wl.h b/carl9170fw/carlfw/include/wl.h new file mode 100644 index 0000000..dd5b0a8 --- /dev/null +++ b/carl9170fw/carlfw/include/wl.h @@ -0,0 +1,296 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * WLAN + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, 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 __CARL9170FW_WLAN_H +#define __CARL9170FW_WLAN_H + +#include "config.h" +#include "carl9170.h" +#include "io.h" + +struct ieee80211_hdr; + +static inline __inline void set_wlan_txq_dma_addr(const unsigned int q, const uint32_t v) +{ + set(AR9170_MAC_REG_DMA_TXQ_ADDR + (q << 3), v); +} + +static inline __inline void set_wlan_txq_dma_curr_addr(const unsigned int q, const uint32_t v) +{ + set(AR9170_MAC_REG_DMA_TXQ_CURR_ADDR + (q << 3), v); +} + +static inline __inline volatile struct dma_desc *get_wlan_txq_dma_addr(const unsigned int q) +{ + return getp(AR9170_MAC_REG_DMA_TXQ_ADDR + (q << 3)); +} + +static inline __inline volatile struct dma_desc *get_wlan_txq_addr(const unsigned int q) +{ + return getp(AR9170_MAC_REG_DMA_TXQ_CURR_ADDR + (q << 3)); +} + +static inline __inline volatile struct dma_desc *get_wlan_txq_last_addr(const unsigned int q) +{ + return getp(AR9170_MAC_REG_DMA_TXQ_LAST_ADDR + (q << 2)); +} + +static inline __inline void wlan_trigger(const uint32_t queue_bit) +{ + set(AR9170_MAC_REG_DMA_TRIGGER, queue_bit); +} + +static inline __inline uint8_t ar9170_get_rx_macstatus_status(struct dma_desc *desc) +{ + return *((uint8_t *) DESC_PAYLOAD_OFF(desc->lastAddr, + (unsigned int) desc->lastAddr->dataSize - 1)); +} + +static inline __inline uint8_t ar9170_get_rx_macstatus_error(struct dma_desc *desc) +{ + unsigned int offset; + + if (desc->lastAddr->dataSize == 1) { + while (desc->lastAddr != desc->nextAddr) + desc = desc->nextAddr; + + offset = (unsigned int) (desc->dataSize - 1); + } else { + desc = desc->lastAddr; + offset = desc->dataSize - + (sizeof(struct ar9170_rx_macstatus) - + offsetof(struct ar9170_rx_macstatus, error)); + } + + return *((uint8_t *) DESC_PAYLOAD_OFF(desc, offset)); +} + +static inline __inline struct ieee80211_hdr *ar9170_get_rx_i3e(struct dma_desc *desc) +{ + if (!((ar9170_get_rx_macstatus_status(desc) & + AR9170_RX_STATUS_MPDU) & AR9170_RX_STATUS_MPDU_LAST)) { + return (void *)(DESC_PAYLOAD_OFF(desc, + offsetof(struct ar9170_rx_frame_head, i3e))); + } else { + return (void *)(DESC_PAYLOAD_OFF(desc, + offsetof(struct ar9170_rx_frame_tail, i3e))); + } +} + +static inline __inline struct ar9170_rx_head *ar9170_get_rx_head(struct dma_desc *desc) +{ + if (!((ar9170_get_rx_macstatus_status(desc) & + AR9170_RX_STATUS_MPDU) & AR9170_RX_STATUS_MPDU_LAST)) { + return (void *)((uint8_t *)DESC_PAYLOAD(desc) + + offsetof(struct ar9170_rx_frame_head, phy_head)); + } else { + return (void *) NULL; + } +} + +static inline __inline uint32_t ar9170_rx_to_phy(struct dma_desc *rx) +{ + struct ar9170_tx_hw_phy_control phy; + struct ar9170_rx_head *head; + uint8_t mac_status; + + phy.set = 0; + + head = ar9170_get_rx_head(rx); + if (!head) + return le32_to_cpu(phy.set); + + mac_status = ar9170_get_rx_macstatus_status(rx); + + phy.modulation = mac_status & AR9170_RX_STATUS_MODULATION; + phy.chains = AR9170_TX_PHY_TXCHAIN_1; + + switch (phy.modulation) { + case AR9170_RX_STATUS_MODULATION_CCK: + if (mac_status & AR9170_RX_STATUS_SHORT_PREAMBLE) + phy.preamble = 1; + + switch (head->plcp[0]) { + case AR9170_RX_PHY_RATE_CCK_2M: + phy.mcs = AR9170_TX_PHY_RATE_CCK_2M; + break; + + case AR9170_RX_PHY_RATE_CCK_5M: + phy.mcs = AR9170_TX_PHY_RATE_CCK_5M; + break; + + case AR9170_RX_PHY_RATE_CCK_11M: + phy.mcs = AR9170_TX_PHY_RATE_CCK_11M; + break; + + case AR9170_RX_PHY_RATE_CCK_1M: + default: + phy.mcs = AR9170_TX_PHY_RATE_CCK_1M; + break; + + } + break; + + case AR9170_RX_STATUS_MODULATION_DUPOFDM: + case AR9170_RX_STATUS_MODULATION_OFDM: + phy.mcs = head->plcp[0] & 0xf; + break; + + case AR9170_RX_STATUS_MODULATION_HT: + if (head->plcp[3] & 0x80) + phy.bandwidth = 2; + + if (head->plcp[6] & 0x80) + phy.short_gi = 1; + + /* TODO: Enable both chains for MCS > 7 */ + phy.mcs = head->plcp[6] & 0x7; + break; + } + + return le32_to_cpu(phy.set); +} + +static inline __inline unsigned int ar9170_get_rx_mpdu_len(struct dma_desc *desc) +{ + /* + * WARNING: you have to check the error bits in macstatus first! + */ + + unsigned int mpdu_len = desc->totalLen; + + mpdu_len -= sizeof(struct ar9170_rx_macstatus); + + switch (ar9170_get_rx_macstatus_status(desc) & AR9170_RX_STATUS_MPDU) { + case AR9170_RX_STATUS_MPDU_LAST: + mpdu_len -= sizeof(struct ar9170_rx_phystatus); + break; + + case AR9170_RX_STATUS_MPDU_SINGLE: + mpdu_len -= sizeof(struct ar9170_rx_phystatus); + + case AR9170_RX_STATUS_MPDU_FIRST: + mpdu_len -= sizeof(struct ar9170_rx_head); + break; + + case AR9170_RX_STATUS_MPDU_MIDDLE: + default: + break; + } + + return mpdu_len; +} + +static inline __inline bool ar9170_tx_length_check(const uint16_t len) +{ + return len > (sizeof(struct carl9170_tx_superframe) + 24 + + FCS_LEN); +} + +static inline __inline struct carl9170_tx_superframe *get_super(struct dma_desc *desc) +{ + return container_of(DESC_PAYLOAD(desc), struct carl9170_tx_superframe, + f); +} + +static inline __inline struct carl9170_tx_superframe *__get_super(struct dma_desc *desc) +{ + return DESC_PAYLOAD(desc); +} + +static inline __inline void hide_super(struct dma_desc *desc) +{ + desc->dataAddr = (uint8_t *) + (((unsigned long)(DESC_PAYLOAD(desc)) + + offsetof(struct carl9170_tx_superframe, f))); + + desc->dataSize -= sizeof(struct carl9170_tx_superdesc); + desc->totalLen -= sizeof(struct carl9170_tx_superdesc); +} + +static inline __inline void unhide_super(struct dma_desc *desc) +{ + desc->dataAddr = (uint8_t *) get_super(desc); + desc->dataSize += sizeof(struct carl9170_tx_superdesc); + desc->totalLen += sizeof(struct carl9170_tx_superdesc); +} + +static inline __inline __hot void read_tsf(uint32_t *tsf) +{ + /* + * "According to the [hardware] documentation: + * > when TSF_LOW is read, TSF_HI is automatically concurrently + * > copied into a temporary register so that an immediate read + * > of TSF_HI will get the value that was present when TSF_LOW + * > was read. " + * + * (David H. Lynch Jr. - mail from 2010-05-22) + * http://permalink.gmane.org/gmane.linux.kernel.wireless.general/51249 + */ + + tsf[0] = get(AR9170_MAC_REG_TSF_L); + tsf[1] = get(AR9170_MAC_REG_TSF_H); +} + +/* This function will only work on uint32_t-aligned pointers! */ +static inline bool compare_ether_address(const void *_d0, const void *_d1) +{ + const uint32_t *d0 = _d0; + const uint32_t *d1 = _d1; + + /* BUG_ON((unsigned long)d0 & 3 || (unsigned long)d1 & 3)) */ + return !((d0[0] ^ d1[0]) | (unsigned short)(d0[1] ^ d1[1])); +} + +void wlan_tx(struct dma_desc *desc); +void wlan_tx_fw(struct carl9170_tx_superdesc *super, fw_desc_callback_t cb); +void wlan_timer(void); +void handle_wlan(void); + +void wlan_cab_flush_queue(const unsigned int vif); +void wlan_modify_beacon(const unsigned int vif, + const unsigned int bcn_addr, + const unsigned int bcn_len); + +void wlan_tx_complete(struct carl9170_tx_superframe *super, bool txs); +void wlan_prepare_wol(void); + +static inline void __check_wlantx(void) +{ + BUILD_BUG_ON(CARL9170_TX_SUPERDESC_LEN & 3); + BUILD_BUG_ON(sizeof(struct carl9170_tx_superdesc) != CARL9170_TX_SUPERDESC_LEN); + BUILD_BUG_ON(sizeof(struct _carl9170_tx_superdesc) != CARL9170_TX_SUPERDESC_LEN); + BUILD_BUG_ON(sizeof(struct _carl9170_tx_superframe) != CARL9170_TX_SUPERFRAME_LEN); + BUILD_BUG_ON((offsetof(struct carl9170_tx_superframe, f) & 3) != 0); + BUILD_BUG_ON(offsetof(struct _carl9170_tx_superframe, f) != + (offsetof(struct _carl9170_tx_superframe, f))); + BUILD_BUG_ON(sizeof(struct ar9170_tx_hwdesc) != AR9170_TX_HWDESC_LEN); + BUILD_BUG_ON(sizeof(struct _ar9170_tx_hwdesc) != AR9170_TX_HWDESC_LEN); + BUILD_BUG_ON(sizeof(struct ar9170_rx_head) != AR9170_RX_HEAD_LEN); + BUILD_BUG_ON(sizeof(struct ar9170_rx_phystatus) != AR9170_RX_PHYSTATUS_LEN); + BUILD_BUG_ON(sizeof(struct ar9170_rx_macstatus) != AR9170_RX_MACSTATUS_LEN); +} + +#endif /* __CARL9170FW_WLAN_H */ diff --git a/carl9170fw/carlfw/include/wol.h b/carl9170fw/carlfw/include/wol.h new file mode 100644 index 0000000..829400a --- /dev/null +++ b/carl9170fw/carlfw/include/wol.h @@ -0,0 +1,67 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * WakeUp on WLAN definitions + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, 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 __CARL9170FW_WOL_H +#define __CARL9170FW_WOL_H + +#include "config.h" +#include "compiler.h" +#include "types.h" + +#include "fwcmd.h" + +#ifdef CONFIG_CARL9170FW_WOL + +struct ieee80211_hdr; + +void wol_prepare(void); +void wol_janitor(void); +void wol_rx(const unsigned int rx_filter __unused, + const struct ieee80211_hdr *hdr __unused, + const unsigned int len __unused); +void wol_cmd(const struct carl9170_wol_cmd *cmd); + +#else + +static inline void wol_cmd(const struct carl9170_wol_cmd *cmd __unused) +{ +} + +static inline void wol_prepare(void) +{ +} + +static inline void wol_janitor(void) +{ +} + +static inline void wol_rx(const unsigned int rx_filter __unused, + const struct ieee80211_hdr *hdr __unused, + const unsigned int len __unused) +{ +} +#endif /* CONFIG_CARL9170FW_WOL */ + +#endif /* __CARL9170FW_CMD_H */ diff --git a/carl9170fw/carlfw/src/ashlsi3.S b/carl9170fw/carlfw/src/ashlsi3.S new file mode 100644 index 0000000..593d902 --- /dev/null +++ b/carl9170fw/carlfw/src/ashlsi3.S @@ -0,0 +1,193 @@ +/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, + 2004, 2005, 2006 + Free Software Foundation, Inc. + +This file 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, or (at your option) any +later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +This file is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +!! libgcc routines for the Renesas / SuperH SH CPUs. +!! Contributed by Steve Chamberlain. +!! sac@cygnus.com + +!! ashiftrt_r4_x, ___ashrsi3, ___ashlsi3, ___lshrsi3 routines +!! recoded in assembly by Toshiyasu Morita +!! tm@netcom.com + +/* SH2 optimizations for ___ashrsi3, ___ashlsi3, ___lshrsi3 and + ELF local label prefixes by J"orn Rennecke + amylaar@cygnus.com */ + +! +! __ashlsi3 +! +! Entry: +! +! r4: Value to shift +! r5: Shifts +! +! Exit: +! +! r0: Result +! +! Destroys: +! +! (none) +! + .global ___ashlsi3 + + .align 2 +___ashlsi3: + mov #31,r0 + and r0,r5 + mova ashlsi3_table,r0 + mov.b @(r0,r5),r5 +#ifdef __sh1__ + add r5,r0 + jmp @r0 +#else + braf r5 +#endif + mov r4,r0 + + .align 2 +ashlsi3_table: + .byte ashlsi3_0-ashlsi3_table + .byte ashlsi3_1-ashlsi3_table + .byte ashlsi3_2-ashlsi3_table + .byte ashlsi3_3-ashlsi3_table + .byte ashlsi3_4-ashlsi3_table + .byte ashlsi3_5-ashlsi3_table + .byte ashlsi3_6-ashlsi3_table + .byte ashlsi3_7-ashlsi3_table + .byte ashlsi3_8-ashlsi3_table + .byte ashlsi3_9-ashlsi3_table + .byte ashlsi3_10-ashlsi3_table + .byte ashlsi3_11-ashlsi3_table + .byte ashlsi3_12-ashlsi3_table + .byte ashlsi3_13-ashlsi3_table + .byte ashlsi3_14-ashlsi3_table + .byte ashlsi3_15-ashlsi3_table + .byte ashlsi3_16-ashlsi3_table + .byte ashlsi3_17-ashlsi3_table + .byte ashlsi3_18-ashlsi3_table + .byte ashlsi3_19-ashlsi3_table + .byte ashlsi3_20-ashlsi3_table + .byte ashlsi3_21-ashlsi3_table + .byte ashlsi3_22-ashlsi3_table + .byte ashlsi3_23-ashlsi3_table + .byte ashlsi3_24-ashlsi3_table + .byte ashlsi3_25-ashlsi3_table + .byte ashlsi3_26-ashlsi3_table + .byte ashlsi3_27-ashlsi3_table + .byte ashlsi3_28-ashlsi3_table + .byte ashlsi3_29-ashlsi3_table + .byte ashlsi3_30-ashlsi3_table + .byte ashlsi3_31-ashlsi3_table + +ashlsi3_6: + shll2 r0 +ashlsi3_4: + shll2 r0 +ashlsi3_2: + rts + shll2 r0 + +ashlsi3_7: + shll2 r0 +ashlsi3_5: + shll2 r0 +ashlsi3_3: + shll2 r0 +ashlsi3_1: + rts + shll r0 + +ashlsi3_14: + shll2 r0 +ashlsi3_12: + shll2 r0 +ashlsi3_10: + shll2 r0 +ashlsi3_8: + rts + shll8 r0 + +ashlsi3_15: + shll2 r0 +ashlsi3_13: + shll2 r0 +ashlsi3_11: + shll2 r0 +ashlsi3_9: + shll8 r0 + rts + shll r0 + +ashlsi3_22: + shll2 r0 +ashlsi3_20: + shll2 r0 +ashlsi3_18: + shll2 r0 +ashlsi3_16: + rts + shll16 r0 + +ashlsi3_23: + shll2 r0 +ashlsi3_21: + shll2 r0 +ashlsi3_19: + shll2 r0 +ashlsi3_17: + shll16 r0 + rts + shll r0 + +ashlsi3_30: + shll2 r0 +ashlsi3_28: + shll2 r0 +ashlsi3_26: + shll2 r0 +ashlsi3_24: + shll16 r0 + rts + shll8 r0 + +ashlsi3_31: + shll2 r0 +ashlsi3_29: + shll2 r0 +ashlsi3_27: + shll2 r0 +ashlsi3_25: + shll16 r0 + shll8 r0 + rts + shll r0 + +ashlsi3_0: + rts + nop diff --git a/carl9170fw/carlfw/src/cam.c b/carl9170fw/carlfw/src/cam.c new file mode 100644 index 0000000..7c0e61c --- /dev/null +++ b/carl9170fw/carlfw/src/cam.c @@ -0,0 +1,161 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Security Engine + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "cam.h" + +#ifdef CONFIG_CARL9170FW_SECURITY_ENGINE +static void disable_cam_user(const uint16_t userId) +{ + if (userId <= 31) + andl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L, (~((uint32_t) 1 << userId))); + else if (userId <= 63) + andl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H, (~((uint32_t) 1 << (userId - 32)))); +} + +static void enable_cam_user(const uint16_t userId) +{ + if (userId <= 31) + orl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L, (((uint32_t) 1) << userId)); + else if (userId <= 63) + orl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H, (((uint32_t) 1) << (userId - 32))); +} + +static void wait_for_cam_read_ready(void) +{ + while ((get(AR9170_MAC_REG_CAM_STATE) & AR9170_MAC_CAM_STATE_READ_PENDING) == 0) { + /* + * wait + */ + } +} + +static void wait_for_cam_write_ready(void) +{ + while ((get(AR9170_MAC_REG_CAM_STATE) & AR9170_MAC_CAM_STATE_WRITE_PENDING) == 0) { + /* + * wait some more + */ + } +} + +static void HW_CAM_Avail(void) +{ + uint32_t tmpValue; + + do { + tmpValue = get(AR9170_MAC_REG_CAM_MODE); + } while (tmpValue & AR9170_MAC_CAM_HOST_PENDING); +} + +static void HW_CAM_Write128(const uint32_t address, const uint32_t *data) +{ + HW_CAM_Avail(); + + set(AR9170_MAC_REG_CAM_DATA0, data[0]); + set(AR9170_MAC_REG_CAM_DATA1, data[1]); + set(AR9170_MAC_REG_CAM_DATA2, data[2]); + set(AR9170_MAC_REG_CAM_DATA3, data[3]); + + set(AR9170_MAC_REG_CAM_ADDR, address | AR9170_MAC_CAM_ADDR_WRITE); + + wait_for_cam_write_ready(); +} + +static void HW_CAM_Read128(const uint32_t address, uint32_t *data) +{ + + HW_CAM_Avail(); + set(AR9170_MAC_REG_CAM_ADDR, address); + + wait_for_cam_read_ready(); + HW_CAM_Avail(); + data[0] = get(AR9170_MAC_REG_CAM_DATA0); + data[1] = get(AR9170_MAC_REG_CAM_DATA1); + data[2] = get(AR9170_MAC_REG_CAM_DATA2); + data[3] = get(AR9170_MAC_REG_CAM_DATA3); +} + +void set_key(const struct carl9170_set_key_cmd *key) +{ + uint32_t data[4]; + uint16_t row, wordId, nibbleId, i; + + if (key->user > (AR9170_CAM_MAX_USER + 3)) + return ; + + if (key->keyId > 1) + return ; + + /* Disable Key */ + disable_cam_user(key->user); + + /* Set encrypt type */ + if (key->user >= AR9170_CAM_MAX_USER) { + /* default */ + row = DEFAULT_ENCRY_TYPE; + wordId = 0; + nibbleId = (key->user - AR9170_CAM_MAX_USER) & 0x7; + } else { + row = ENCRY_TYPE_START_ADDR + (key->user >> 5); + wordId = (key->user >> 3) & 0x3; + nibbleId = key->user & 0x7; + } + + HW_CAM_Read128(row, data); + data[wordId] &= (~(0xf << ((uint32_t) nibbleId * 4))); + data[wordId] |= (key->type << ((uint32_t) nibbleId * 4)); + HW_CAM_Write128(row, data); + + /* Set MAC address */ + if (key->user < AR9170_CAM_MAX_USER) { + uint16_t byteId; + wordId = (key->user >> 2) & 0x3; + byteId = key->user & 0x3; + row = (key->user >> 4) * 6; + + for (i = 0; i < 6; i++) { + HW_CAM_Read128(row + i, data); + data[wordId] &= (~(0xff << ((uint32_t) byteId * 8))); + data[wordId] |= (key->macAddr[i] << ((uint32_t) byteId * 8)); + HW_CAM_Write128(row + i, data); + } + } + + /* Set key */ + row = KEY_START_ADDR + (key->user * 2) + key->keyId; + + HW_CAM_Write128(row, key->key); + + /* Enable Key */ + enable_cam_user(key->user); +} + +void disable_key(const struct carl9170_disable_key_cmd *key) +{ + disable_cam_user(key->user); +} + +#endif /* CONFIG_CARL9170FW_SECURITY_ENGINE */ diff --git a/carl9170fw/carlfw/src/cmd.c b/carl9170fw/carlfw/src/cmd.c new file mode 100644 index 0000000..5382083 --- /dev/null +++ b/carl9170fw/carlfw/src/cmd.c @@ -0,0 +1,154 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Code to handle commands from the host driver. + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "io.h" +#include "cam.h" +#include "rf.h" +#include "printf.h" +#include "timer.h" +#include "wl.h" +#include "wol.h" + +void handle_cmd(struct carl9170_rsp *resp) +{ + struct carl9170_cmd *cmd = &dma_mem.reserved.cmd.cmd; + unsigned int i; + + /* copies cmd, len and extra fields */ + resp->hdr.len = cmd->hdr.len; + resp->hdr.cmd = cmd->hdr.cmd; + resp->hdr.ext = cmd->hdr.ext; + resp->hdr.seq |= cmd->hdr.seq; + + switch (cmd->hdr.cmd & ~CARL9170_CMD_ASYNC_FLAG) { + case CARL9170_CMD_RREG: + for (i = 0; i < (cmd->hdr.len / 4); i++) + resp->rreg_res.vals[i] = get(cmd->rreg.regs[i]); + break; + + case CARL9170_CMD_WREG: + resp->hdr.len = 0; + for (i = 0; i < (cmd->hdr.len / 8); i++) + set(cmd->wreg.regs[i].addr, cmd->wreg.regs[i].val); + break; + + case CARL9170_CMD_ECHO: + memcpy(resp->echo.vals, cmd->echo.vals, cmd->hdr.len); + break; + + case CARL9170_CMD_SWRST: +#ifdef CONFIG_CARL9170FW_FW_MAC_RESET + /* + * Command has no payload, so the response + * has no payload either. + * resp->hdr.len = 0; + */ + fw.wlan.mac_reset = CARL9170_MAC_RESET_FORCE; +#endif /* CONFIG_CARL9170FW_FW_MAC_RESET */ + break; + + case CARL9170_CMD_REBOOT: + /* + * resp->len = 0; + */ + fw.reboot = 1; + break; + + case CARL9170_CMD_READ_TSF: + resp->hdr.len = 8; + read_tsf((uint32_t *)resp->tsf.tsf); + break; + + case CARL9170_CMD_RX_FILTER: + resp->hdr.len = 0; + fw.wlan.rx_filter = cmd->rx_filter.rx_filter; + break; + + case CARL9170_CMD_WOL: + wol_cmd(&cmd->wol); + break; + + case CARL9170_CMD_TALLY: + resp->hdr.len = sizeof(struct carl9170_tally_rsp); + memcpy(&resp->tally, &fw.tally, sizeof(struct carl9170_tally_rsp)); + resp->tally.tick = fw.ticks_per_usec; + memset(&fw.tally, 0, sizeof(struct carl9170_tally_rsp)); + break; + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + case CARL9170_CMD_BCN_CTRL: + resp->hdr.len = 0; + + if (cmd->bcn_ctrl.mode & CARL9170_BCN_CTRL_CAB_TRIGGER) { + wlan_modify_beacon(cmd->bcn_ctrl.vif_id, + cmd->bcn_ctrl.bcn_addr, cmd->bcn_ctrl.bcn_len); + set(AR9170_MAC_REG_BCN_ADDR, cmd->bcn_ctrl.bcn_addr); + set(AR9170_MAC_REG_BCN_LENGTH, cmd->bcn_ctrl.bcn_len); + set(AR9170_MAC_REG_BCN_CTRL, AR9170_BCN_CTRL_READY); + } else { + wlan_cab_flush_queue(cmd->bcn_ctrl.vif_id); + fw.wlan.cab_flush_trigger[cmd->bcn_ctrl.vif_id] = CARL9170_CAB_TRIGGER_EMPTY; + } + break; +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + +#ifdef CONFIG_CARL9170FW_SECURITY_ENGINE + case CARL9170_CMD_EKEY: + resp->hdr.len = 0; + set_key(&cmd->setkey); + break; + + case CARL9170_CMD_DKEY: + resp->hdr.len = 0; + disable_key(&cmd->disablekey); + break; +#endif /* CONFIG_CARL9170FW_SECURITY_ENGINE */ + +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + case CARL9170_CMD_FREQUENCY: + case CARL9170_CMD_RF_INIT: + rf_cmd(cmd, resp); + break; + + case CARL9170_CMD_FREQ_START: + /* + * resp->hdr.len = 0; + */ + rf_notify_set_channel(); + break; + + case CARL9170_CMD_PSM: + resp->hdr.len = 0; + fw.phy.psm.state = le32_to_cpu(cmd->psm.state); + rf_psm(); + break; +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ + + default: + BUG("Unknown command %x\n", cmd->hdr.cmd); + break; + } +} diff --git a/carl9170fw/carlfw/src/dma.c b/carl9170fw/carlfw/src/dma.c new file mode 100644 index 0000000..9b83fcf --- /dev/null +++ b/carl9170fw/carlfw/src/dma.c @@ -0,0 +1,255 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * DMA descriptor handling functions + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "wl.h" +#include "printf.h" + +struct ar9170_dma_memory dma_mem __section(sram); + +static void copy_dma_desc(struct dma_desc *dst, + struct dma_desc *src) +{ + memcpy(dst, src, sizeof(struct dma_desc)); +} + +static void clear_descriptor(struct dma_desc *d) +{ + d->status = AR9170_OWN_BITS_SW; + d->ctrl = 0; + d->dataSize = 0; + d->totalLen = 0; + d->lastAddr = d; + d->dataAddr = NULL; + d->nextAddr = d; +} + +static void fill_descriptor(struct dma_desc *d, uint16_t size, uint8_t *data) +{ + d->status = AR9170_OWN_BITS_SW; + d->ctrl = 0; + d->dataSize = size; + d->totalLen = 0; + d->lastAddr = d; + d->dataAddr = data; + d->nextAddr = NULL; +} + +static void init_queue(struct dma_queue *q, struct dma_desc *d) +{ + q->head = q->terminator = d; +} + +/* + * - Init up_queue, down_queue, tx_queue[5], rx_queue. + * - Setup descriptors and data buffer address. + * - Ring descriptors rx_queue and down_queue by dma_reclaim(). + * + * NOTE: LastAddr tempary point (same) to nextAddr after initialize. + * Because LastAddr is don't care in function dma_reclaim(). + */ +void dma_init_descriptors(void) +{ + unsigned int i, j; + + for (i = 0; i < ARRAY_SIZE(dma_mem.terminator); i++) + clear_descriptor(&dma_mem.terminator[i]); + + /* Assign terminators to DMA queues */ + i = 0; + init_queue(&fw.pta.up_queue, &dma_mem.terminator[i++]); + init_queue(&fw.pta.down_queue, &dma_mem.terminator[i++]); + for (j = 0; j < __AR9170_NUM_TX_QUEUES; j++) + init_queue(&fw.wlan.tx_queue[j], &dma_mem.terminator[i++]); + init_queue(&fw.wlan.tx_retry, &dma_mem.terminator[i++]); + init_queue(&fw.wlan.rx_queue, &dma_mem.terminator[i++]); + fw.usb.int_desc = &dma_mem.terminator[i++]; + fw.wlan.fw_desc = &dma_mem.terminator[i++]; + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + for (j = 0; j < CARL9170_INTF_NUM; j++) + init_queue(&fw.wlan.cab_queue[j], &dma_mem.terminator[i++]); +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + + BUG_ON(AR9170_TERMINATOR_NUMBER != i); + + DBG("Blocks:%d [tx:%d, rx:%d] Terminators:%d/%d\n", + AR9170_BLOCK_NUMBER, AR9170_TX_BLOCK_NUMBER, + AR9170_RX_BLOCK_NUMBER, AR9170_TERMINATOR_NUMBER, i); + + /* Init descriptors and memory blocks */ + for (i = 0; i < AR9170_BLOCK_NUMBER; i++) { + fill_descriptor(&dma_mem.block[i], AR9170_BLOCK_SIZE, dma_mem.data[i].data); + + if (i < AR9170_TX_BLOCK_NUMBER) + dma_reclaim(&fw.pta.down_queue, &dma_mem.block[i]); + else + dma_reclaim(&fw.wlan.rx_queue, &dma_mem.block[i]); + } + + /* Set DMA address registers */ + set(AR9170_PTA_REG_DN_DMA_ADDRH, (uint32_t) fw.pta.down_queue.head >> 16); + set(AR9170_PTA_REG_DN_DMA_ADDRL, (uint32_t) fw.pta.down_queue.head & 0xffff); + set(AR9170_PTA_REG_UP_DMA_ADDRH, (uint32_t) fw.pta.up_queue.head >> 16); + set(AR9170_PTA_REG_UP_DMA_ADDRL, (uint32_t) fw.pta.up_queue.head & 0xffff); + + for (i = 0; i < __AR9170_NUM_TX_QUEUES; i++) + set_wlan_txq_dma_addr(i, (uint32_t) fw.wlan.tx_queue[i].head); + + set(AR9170_MAC_REG_DMA_RXQ_ADDR, (uint32_t) fw.wlan.rx_queue.head); + fw.usb.int_desc->dataSize = AR9170_BLOCK_SIZE; + fw.usb.int_desc->dataAddr = (void *) &dma_mem.reserved.rsp; + + memset(DESC_PAYLOAD(fw.usb.int_desc), 0xff, + AR9170_INT_MAGIC_HEADER_SIZE); + memset(DESC_PAYLOAD_OFF(fw.usb.int_desc, AR9170_INT_MAGIC_HEADER_SIZE), + 0, AR9170_BLOCK_SIZE - AR9170_INT_MAGIC_HEADER_SIZE); + + /* rsp is now available for use */ + fw.usb.int_desc_available = 1; + + memset(DESC_PAYLOAD(fw.wlan.fw_desc), 0, 128); + fw.wlan.fw_desc_available = 1; +} + +/* + * Free descriptor. + * + * Exchange the terminator and the first descriptor of the packet + * for hardware ascy... + */ +void dma_reclaim(struct dma_queue *q, struct dma_desc *desc) +{ + struct dma_desc *tmpDesc, *last; + struct dma_desc tdesc; + + /* 1. Set OWN bit to HW for all TDs to be added, clear ctrl and size */ + tmpDesc = desc; + last = desc->lastAddr; + + while (1) { + tmpDesc->status = AR9170_OWN_BITS_HW; + tmpDesc->ctrl = 0; + tmpDesc->totalLen = 0; + tmpDesc->dataSize = AR9170_BLOCK_SIZE; + + /* TODO : Exception handle */ + + tmpDesc->lastAddr = tmpDesc; + + if (tmpDesc == last) + break; + + tmpDesc = tmpDesc->nextAddr; + } + + /* 2. Next address of Last TD to be added = first TD */ + tmpDesc->nextAddr = desc; + + /* Link first TD to self */ + desc->lastAddr = q->terminator; + + /* 3. Copy first TD to be added to TTD */ + copy_dma_desc(&tdesc, desc); + + /* 4. Initialize new terminator */ + clear_descriptor(desc); + + /* 5. Copy TTD to last TD */ + tdesc.status = 0; + copy_dma_desc((void *)q->terminator, (void *)&tdesc); + q->terminator->status |= AR9170_OWN_BITS_HW; + + /* Update terminator pointer */ + q->terminator = desc; +} + +/* + * Put a complete packet into the tail of the Queue q. + * Exchange the terminator and the first descriptor of the packet + * for hardware ascy... + */ +void dma_put(struct dma_queue *q, struct dma_desc *desc) +{ + struct dma_desc *tmpDesc; + struct dma_desc tdesc; + + tmpDesc = desc; + + while (1) { + /* update totalLen */ + tmpDesc->totalLen = desc->totalLen; + + /* 1. Set OWN bit to HW for all TDs to be added */ + tmpDesc->status = AR9170_OWN_BITS_HW; + /* TODO : Exception handle */ + + tmpDesc->lastAddr = desc->lastAddr; + + if (desc->lastAddr == tmpDesc) + break; + + tmpDesc = tmpDesc->nextAddr; + } + + /* 2. Next address of Last TD to be added = first TD */ + desc->lastAddr->nextAddr = desc; + + /* If there is only one descriptor, update pointer of last descriptor */ + if (desc->lastAddr == desc) + desc->lastAddr = q->terminator; + + /* 3. Copy first TD to be added to TTD */ + copy_dma_desc(&tdesc, desc); + + /* 4. Initialize new terminator */ + clear_descriptor(desc); + + /* 5. Copy TTD to last TD */ + tdesc.status &= (~AR9170_OWN_BITS); + copy_dma_desc((void *)q->terminator, (void *)&tdesc); + q->terminator->status |= AR9170_OWN_BITS_HW; + + /* Update terminator pointer */ + q->terminator = desc; +} + +struct dma_desc *dma_unlink_head(struct dma_queue *queue) +{ + struct dma_desc *desc; + + if (queue_empty(queue)) + return NULL; + + desc = queue->head; + + queue->head = desc->lastAddr->nextAddr; + + /* poison nextAddr address */ + desc->lastAddr->nextAddr = desc->lastAddr; + desc->lastAddr->lastAddr = desc->lastAddr; + + return desc; +} diff --git a/carl9170fw/carlfw/src/fw.c b/carl9170fw/carlfw/src/fw.c new file mode 100644 index 0000000..5bde675 --- /dev/null +++ b/carl9170fw/carlfw/src/fw.c @@ -0,0 +1,116 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Firmware descriptor + * + * 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. + */ +#include "carl9170.h" +#include "fwdsc.h" + +#define FILL(small, big, more...) \ + .small = { \ + CARL9170FW_FILL_DESC(big##_MAGIC, \ + sizeof(struct carl9170fw_## small##_desc), \ + CARL9170FW_## big##_DESC_MIN_VER, \ + CARL9170FW_## big##_DESC_CUR_VER), \ + more \ + } + +const struct carl9170_firmware_descriptor __section(fwdsc) carl9170fw_desc = { + FILL(otus, OTUS, + .feature_set = cpu_to_le32(BIT(CARL9170FW_DUMMY_FEATURE) | + BIT(CARL9170FW_USB_RESP_EP2) | + BIT(CARL9170FW_HANDLE_BACK_REQ) | + BIT(CARL9170FW_RX_FILTER) | + BIT(CARL9170FW_HW_COUNTERS) | + BIT(CARL9170FW_RX_BA_FILTER) | + BIT(CARL9170FW_USB_INIT_FIRMWARE) | +#ifdef CONFIG_CARL9170FW_USB_UP_STREAM + BIT(CARL9170FW_USB_UP_STREAM) | +#endif /* CONFIG_CARL9170FW_USB_UP_STREAM */ +#ifdef CONFIG_CARL9170FW_USB_DOWN_STREAM + BIT(CARL9170FW_USB_DOWN_STREAM) | +#endif /* CONFIG_CARL9170FW_USB_DOWN_STREAM */ +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + BIT(CARL9170FW_COMMAND_PHY) | + BIT(CARL9170FW_PSM) | + BIT(CARL9170FW_FIXED_5GHZ_PSM) | +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ +#ifdef CONFIG_CARL9170FW_SECURITY_ENGINE + BIT(CARL9170FW_COMMAND_CAM) | +#endif /* CONFIG_CARL9170FW_SECURITY_ENGINE */ +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + BIT(CARL9170FW_WLANTX_CAB) | +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ +#ifdef CONFIG_CARL9170FW_UNUSABLE + BIT(CARL9170FW_UNUSABLE) | +#endif /* CONFIG_CARL9170FW_UNUSABLE */ +#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT + BIT(CARL9170FW_GPIO_INTERRUPT) | +#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */ +#ifdef CONFIG_CARL9170FW_WOL + BIT(CARL9170FW_WOL) | +#endif /* CONFIG_CARL9170FW_WOL */ + (0)), + + .miniboot_size = cpu_to_le16(0), + .tx_descs = AR9170_TX_BLOCK_NUMBER, + .cmd_bufs = CARL9170_INT_RQ_CACHES, + .rx_max_frame_len = cpu_to_le16(CONFIG_CARL9170FW_RX_FRAME_LEN), + .tx_frag_len = cpu_to_le16(AR9170_BLOCK_SIZE), + .fw_address = cpu_to_le32(AR9170_PRAM_OFFSET), + .bcn_addr = (__le32) cpu_to_le32(&dma_mem.reserved.bcn), + .bcn_len = (__le16) cpu_to_le16(sizeof(dma_mem.reserved.bcn)), + .vif_num = CARL9170_INTF_NUM, + .api_ver = CONFIG_CARL9170FW_RELEASE_VERSION, + ), + + FILL(txsq, TXSQ, + .seq_table_addr = cpu_to_le32(&fw.wlan.sequence), + ), + +#ifdef CONFIG_CARL9170FW_WOL + FILL(wol, WOL, + .supported_triggers = BIT(CARL9170_WOL_DISCONNECT) | + BIT(CARL9170_WOL_MAGIC_PKT), + ), +#endif /* CONFIG_CARL9170FW_WOL */ + + + FILL(motd, MOTD, + .fw_year_month_day = cpu_to_le32( + CARL9170FW_SET_DAY(CARL9170FW_VERSION_DAY) + + CARL9170FW_SET_MONTH(CARL9170FW_VERSION_MONTH) + + CARL9170FW_SET_YEAR(CARL9170FW_VERSION_YEAR)), + .desc = "Community AR9170 Linux", + .release = CARL9170FW_VERSION_GIT), + + FILL(dbg, DBG, + .bogoclock_addr = cpu_to_le32(0), + .counter_addr = cpu_to_le32(&fw.counter), + .rx_total_addr = cpu_to_le32(0), + .rx_overrun_addr = cpu_to_le32(0), + .rx_filter = cpu_to_le32(&fw.wlan.rx_filter), + ), + + FILL(last, LAST), +}; + +#undef FILL + +struct firmware_context_struct fw; diff --git a/carl9170fw/carlfw/src/gpio.c b/carl9170fw/carlfw/src/gpio.c new file mode 100644 index 0000000..85cd81f --- /dev/null +++ b/carl9170fw/carlfw/src/gpio.c @@ -0,0 +1,52 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * GPIO interrupt service + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "gpio.h" + +#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT +void gpio_timer(void) +{ + uint32_t cur; + + cur = get(AR9170_GPIO_REG_PORT_DATA) & CARL9170_GPIO_MASK; + + if (cur != fw.cached_gpio_state.gpio) { + fw.cached_gpio_state.gpio = cur; + + send_cmd_to_host(sizeof(struct carl9170_gpio), + CARL9170_RSP_GPIO, 0x00, + (uint8_t *)&fw.cached_gpio_state); + +# ifdef CONFIG_CARL9170FW_WATCHDOG_BUTTON + for (;;) { + /* + * Loop forever... Until the watchdog triggers. + */ + } +# endif /* CONFIG_CARL9170FW_WATCHDOG_BUTTON */ + } +} +#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */ diff --git a/carl9170fw/carlfw/src/hostif.c b/carl9170fw/carlfw/src/hostif.c new file mode 100644 index 0000000..e445d5f --- /dev/null +++ b/carl9170fw/carlfw/src/hostif.c @@ -0,0 +1,165 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Host interface routines + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "hostif.h" +#include "printf.h" +#include "wl.h" + +static bool length_check(struct dma_desc *desc) +{ + volatile struct carl9170_tx_superframe *super = __get_super(desc); + + if (unlikely(desc->totalLen < sizeof(struct carl9170_tx_superdesc))) + return false; + + /* + * check if the DMA is complete, or clipped. + * + * NB: The hardware aligns the descriptor length to + * a 4 byte boundary. This makes the direct comparison + * difficult, or unnecessary complex for a hot-path. + */ + if (unlikely(super->s.len > desc->totalLen)) + return false; + + return true; +} + +static void handle_download(void) +{ + struct dma_desc *desc; + + /* + * Under normal conditions, all completed descs should have + * the AR9170_OWN_BITS_SE status flag set. + * However there seems to be a undocumented case where the flag + * is _SW ( handle_download_exception ) + */ + + for_each_desc_not_bits(desc, &fw.pta.down_queue, AR9170_OWN_BITS_HW) { + if (unlikely((length_check(desc) == false))) { + /* + * There is no easy way of telling what was lost. + * + * Therefore we just reclaim the data. + * The driver has to have some sort frame + * timeout mechanism. + */ + + wlan_tx_complete(__get_super(desc), false); + dma_reclaim(&fw.pta.down_queue, desc); + down_trigger(); + } else { + wlan_tx(desc); + } + } + +#ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT + xorl(AR9170_GPIO_REG_PORT_DATA, 2); +#endif /* CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT */ +} + +static void handle_upload(void) +{ + struct dma_desc *desc; + + for_each_desc_not_bits(desc, &fw.pta.up_queue, AR9170_OWN_BITS_HW) { + /* + * BIG FAT NOTE: + * + * DO NOT compare the descriptor addresses. + */ + if (DESC_PAYLOAD(desc) == (void *) &dma_mem.reserved.rsp) { + fw.usb.int_desc = desc; + fw.usb.int_desc_available = 1; + } else { + dma_reclaim(&fw.wlan.rx_queue, desc); + wlan_trigger(AR9170_DMA_TRIGGER_RXQ); + } + } + +#ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT + xorl(AR9170_GPIO_REG_PORT_DATA, 2); +#endif /* CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT */ +} + +static void handle_download_exception(void) +{ + struct dma_desc *desc, *target; + + /* actually, the queue should be stopped by now? */ + usb_stop_down_queue(); + + target = (void *)((get(AR9170_PTA_REG_DN_CURR_ADDRH) << 16) | + get(AR9170_PTA_REG_DN_CURR_ADDRL)); + + /* + * Put "forgotten" packets from the head of the queue, back + * to the current position + */ + __while_desc_bits(desc, &fw.pta.down_queue, AR9170_OWN_BITS_HW) { + if (desc == target) + break; + + dma_reclaim(&fw.pta.down_queue, + dma_unlink_head(&fw.pta.down_queue)); + } + + __for_each_desc_continue(desc, &fw.pta.down_queue) { + if ((desc->status & AR9170_OWN_BITS) == AR9170_OWN_BITS_SW) + dma_fix_downqueue(desc); + } + + + usb_start_down_queue(); + + down_trigger(); +} + +/* handle interrupts from DMA chip */ +void handle_host_interface(void) +{ + uint32_t pta_int; + + pta_int = get(AR9170_PTA_REG_INT_FLAG); + +#define HANDLER(intr, flag, func) \ + do { \ + if ((intr & flag) != 0) { \ + func(); \ + } \ + } while (0) + + HANDLER(pta_int, AR9170_PTA_INT_FLAG_DN, handle_download); + + HANDLER(pta_int, AR9170_PTA_INT_FLAG_UP, handle_upload); + + /* This is just guesswork and MAGIC */ + pta_int = get(AR9170_PTA_REG_DMA_STATUS); + HANDLER(pta_int, 0x1, handle_download_exception); + +#undef HANDLER +} diff --git a/carl9170fw/carlfw/src/main.c b/carl9170fw/carlfw/src/main.c new file mode 100644 index 0000000..17cbaf9 --- /dev/null +++ b/carl9170fw/carlfw/src/main.c @@ -0,0 +1,256 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * initialization and main() loop + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "timer.h" +#include "hostif.h" +#include "printf.h" +#include "gpio.h" +#include "wl.h" +#include "rf.h" +#include "usb.h" + +#define AR9170_WATCH_DOG_TIMER 0x100 + +static void timer_init(const unsigned int timer, const unsigned int interval) +{ + /* Set timer to periodic mode */ + orl(AR9170_TIMER_REG_CONTROL, BIT(timer)); + + /* Set time interval */ + set(AR9170_TIMER_REG_TIMER0 + (timer << 2), interval - 1); + + /* Clear timer interrupt flag */ + orl(AR9170_TIMER_REG_INTERRUPT, BIT(timer)); +} + +void clock_set(enum cpu_clock_t clock_, bool on) +{ + /* + * Word of Warning! + * This setting does more than just mess with the CPU Clock. + * So watch out, if you need _stable_ timer interrupts. + */ +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + if (fw.phy.frequency < 3000000) + set(AR9170_PWR_REG_PLL_ADDAC, 0x5163); + else + set(AR9170_PWR_REG_PLL_ADDAC, 0x5143); +#else + set(AR9170_PWR_REG_PLL_ADDAC, 0x5163); +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ + + fw.ticks_per_usec = GET_VAL(AR9170_PWR_PLL_ADDAC_DIV, + get(AR9170_PWR_REG_PLL_ADDAC)); + + set(AR9170_PWR_REG_CLOCK_SEL, (uint32_t) ((on ? 0x70 : 0x600) | clock_)); + + switch (clock_) { + case AHB_20_22MHZ: + fw.ticks_per_usec >>= 1; + case AHB_40MHZ_OSC: + case AHB_40_44MHZ: + fw.ticks_per_usec >>= 1; + case AHB_80_88MHZ: + break; + } +} + +static void init(void) +{ + led_init(); + +#ifdef CONFIG_CARL9170FW_DEBUG_UART + uart_init(); +#endif /* CONFIG_CARL9170FW_DEBUG_UART */ + + /* 25/50/100ms timer (depends on cpu clock) */ + timer_init(0, 50000); + + /* USB init */ + usb_init(); + + /* initialize DMA memory */ + memset(&dma_mem, 0, sizeof(dma_mem)); + + /* fill DMA rings */ + dma_init_descriptors(); + + /* clear all interrupt */ + set(AR9170_MAC_REG_INT_CTRL, 0xffff); + + orl(AR9170_MAC_REG_AFTER_PNP, 1); + + /* Init watch dog control flag */ + fw.watchdog_enable = 1; + + set(AR9170_TIMER_REG_WATCH_DOG, AR9170_WATCH_DOG_TIMER); + +#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT + fw.cached_gpio_state.gpio = get(AR9170_GPIO_REG_PORT_DATA) & + CARL9170_GPIO_MASK; +#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */ + + /* this will get the downqueue moving. */ + down_trigger(); +} + +static void handle_fw(void) +{ + if (fw.watchdog_enable == 1) + set(AR9170_TIMER_REG_WATCH_DOG, AR9170_WATCH_DOG_TIMER); + + if (fw.reboot) + reboot(); +} + +static void timer0_isr(void) +{ + wlan_timer(); + +#ifdef CONFIG_CARL9170FW_GPIO_INTERRUPT + gpio_timer(); +#endif /* CONFIG_CARL9170FW_GPIO_INTERRUPT */ + +#ifdef CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT + set(AR9170_GPIO_REG_PORT_DATA, get(AR9170_GPIO_REG_PORT_DATA) ^ 1); +#endif /* CONFIG_CARL9170FW_DEBUG_LED_HEARTBEAT */ +} + +static void handle_timer(void) +{ + uint32_t intr; + + intr = get(AR9170_TIMER_REG_INTERRUPT); + + /* ACK timer interrupt */ + set(AR9170_TIMER_REG_INTERRUPT, intr); + +#define HANDLER(intr, flag, func) \ + do { \ + if ((intr & flag) != 0) { \ + intr &= ~flag; \ + func(); \ + } \ + } while (0) + + HANDLER(intr, BIT(0), timer0_isr); + + if (intr) + DBG("Unhandled Timer Event %x", (unsigned int) intr); + +#undef HANDLER +} + +static void tally_update(void) +{ + unsigned int boff, time, delta; + + time = get_clock_counter(); + if (fw.phy.state == CARL9170_PHY_ON) { + delta = (time - fw.tally_clock); + + fw.tally.active += delta; + + boff = get(AR9170_MAC_REG_BACKOFF_STATUS); + if (boff & AR9170_MAC_BACKOFF_TX_PE) + fw.tally.tx_time += delta; + if (boff & AR9170_MAC_BACKOFF_CCA) + fw.tally.cca += delta; + } + + fw.tally_clock = time; + fw.counter++; +} + +static void __noreturn main_loop(void) +{ + /* main loop */ + while (1) { + handle_fw(); + + /* + * Due to frame order persevation, the wlan subroutines + * must be executed before handle_host_interface. + */ + handle_wlan(); + + handle_host_interface(); + + handle_usb(); + + handle_timer(); + + tally_update(); + } +} + +/* + * The bootcode will work with the device driver to load the firmware + * onto the device's Program SRAM. The Program SRAM has a size of 16 KB + * and also contains the stack, which grows down from 0x204000. + * + * The Program SRAM starts at address 0x200000 on the device. + * The firmware entry point (0x200004) is located in boot.S. + * we put _start() there with the linker script carl9170.lds. + */ + +void __section(boot) start(void) +{ + clock_set(AHB_40MHZ_OSC, true); + + /* watchdog magic pattern check */ + if ((get(AR9170_PWR_REG_WATCH_DOG_MAGIC) & 0xffff0000) == 0x12340000) { + /* watch dog warm start */ + incl(AR9170_PWR_REG_WATCH_DOG_MAGIC); + usb_trigger_out(); + } else if ((get(AR9170_PWR_REG_WATCH_DOG_MAGIC) & 0xffff0000) == 0x98760000) { + /* suspend/resume */ + } + + /* write the magic pattern for watch dog */ + andl(AR9170_PWR_REG_WATCH_DOG_MAGIC, 0xFFFF); + orl(AR9170_PWR_REG_WATCH_DOG_MAGIC, 0x12340000); + + init(); + +#ifdef CONFIG_CARL9170FW_DEBUG + + BUG("TEST BUG"); + BUG_ON(0x2b || !0x2b); + INFO("INFO MESSAGE"); + + /* a set of unique characters to detect transfer data corruptions */ + DBG("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" + " ~`!1@2#3$4%%5^6&7*8(9)0_-+={[}]|\\:;\"'<,>.?/"); +#endif /* CONFIG_CARL9170FW_DEBUG */ + + /* + * Tell the host, that the firmware has booted and is + * now ready to process requests. + */ + send_cmd_to_host(0, CARL9170_RSP_BOOT, 0x00, NULL); + main_loop(); +} diff --git a/carl9170fw/carlfw/src/memcpy.S b/carl9170fw/carlfw/src/memcpy.S new file mode 100644 index 0000000..9539a6b --- /dev/null +++ b/carl9170fw/carlfw/src/memcpy.S @@ -0,0 +1,228 @@ +/* $Id: memcpy.S,v 1.3 2001/07/27 11:50:52 gniibe Exp $ + * + * "memcpy" implementation of SuperH + * + * Copyright (C) 1999 Niibe Yutaka + * + */ + +/* + * void *memcpy(void *dst, const void *src, size_t n); + * No overlap between the memory of DST and of SRC are assumed. + */ + + .globl _memcpy + .align 2 + _memcpy: + tst r6,r6 + bt/s 9f ! if n=0, do nothing + mov r4,r0 + sub r4,r5 ! From here, r5 has the distance to r0 + add r6,r0 ! From here, r0 points the end of copying point + mov #12,r1 + cmp/gt r6,r1 + bt/s 7f ! if it's too small, copy a byte at once + add #-1,r5 + add #1,r5 + ! From here, r6 is free + ! + ! r4 --> [ ... ] DST [ ... ] SRC + ! [ ... ] [ ... ] + ! : : + ! r0 --> [ ... ] r0+r5 --> [ ... ] + ! + ! + mov r5,r1 + mov #3,r2 + and r2,r1 + shll2 r1 + mov r0,r3 ! Save the value on R0 to R3 + mova jmptable,r0 + add r1,r0 + mov.l @r0,r1 + jmp @r1 + mov r3,r0 ! and back to R0 + .balign 4 +jmptable: + .long case0 + .long case1 + .long case2 + .long case3 + + ! copy a byte at once +7: mov r4,r2 + add #1,r2 +8: + cmp/hi r2,r0 + mov.b @(r0,r5),r1 + bt/s 8b ! while (r0>r2) + mov.b r1,@-r0 +9: + rts + nop + +case0: + ! + ! GHIJ KLMN OPQR --> GHIJ KLMN OPQR + ! + ! First, align to long word boundary + mov r0,r3 + and r2,r3 + tst r3,r3 + bt/s 2f + add #-4,r5 + add #3,r5 +1: dt r3 + mov.b @(r0,r5),r1 + bf/s 1b + mov.b r1,@-r0 + ! + add #-3,r5 +2: ! Second, copy a long word at once + mov r4,r2 + add #7,r2 +3: mov.l @(r0,r5),r1 + cmp/hi r2,r0 + bt/s 3b + mov.l r1,@-r0 + ! + ! Third, copy a byte at once, if necessary + cmp/eq r4,r0 + bt/s 9b + add #3,r5 + bra 8b + add #-6,r2 + +case1: + ! + ! GHIJ KLMN OPQR --> ...G HIJK LMNO PQR. + ! + ! First, align to long word boundary + mov r0,r3 + and r2,r3 + tst r3,r3 + bt/s 2f + add #-1,r5 +1: dt r3 + mov.b @(r0,r5),r1 + bf/s 1b + mov.b r1,@-r0 + ! +2: ! Second, read a long word and write a long word at once + mov.l @(r0,r5),r1 + add #-4,r5 + mov r4,r2 + add #7,r2 + ! +#ifdef __LITTLE_ENDIAN__ +3: mov r1,r3 ! RQPO + shll16 r3 + shll8 r3 ! Oxxx + mov.l @(r0,r5),r1 ! NMLK + mov r1,r6 + shlr8 r6 ! xNML + or r6,r3 ! ONML + cmp/hi r2,r0 + bt/s 3b + mov.l r3,@-r0 +#else +3: mov r1,r3 ! OPQR + shlr16 r3 + shlr8 r3 ! xxxO + mov.l @(r0,r5),r1 ! KLMN + mov r1,r6 + shll8 r6 ! LMNx + or r6,r3 ! LMNO + cmp/hi r2,r0 + bt/s 3b + mov.l r3,@-r0 +#endif + ! + ! Third, copy a byte at once, if necessary + cmp/eq r4,r0 + bt/s 9b + add #4,r5 + bra 8b + add #-6,r2 + +case2: + ! + ! GHIJ KLMN OPQR --> ..GH IJKL MNOP QR.. + ! + ! First, align to word boundary + tst #1,r0 + bt/s 2f + add #-1,r5 + mov.b @(r0,r5),r1 + mov.b r1,@-r0 + ! +2: ! Second, read a word and write a word at once + add #-1,r5 + mov r4,r2 + add #3,r2 + ! +3: mov.w @(r0,r5),r1 + cmp/hi r2,r0 + bt/s 3b + mov.w r1,@-r0 + ! + ! Third, copy a byte at once, if necessary + cmp/eq r4,r0 + bt/s 9b + add #1,r5 + mov.b @(r0,r5),r1 + rts + mov.b r1,@-r0 + +case3: + ! + ! GHIJ KLMN OPQR --> .GHI JKLM NOPQ R... + ! + ! First, align to long word boundary + mov r0,r3 + and r2,r3 + tst r3,r3 + bt/s 2f + add #-1,r5 +1: dt r3 + mov.b @(r0,r5),r1 + bf/s 1b + mov.b r1,@-r0 + ! +2: ! Second, read a long word and write a long word at once + add #-2,r5 + mov.l @(r0,r5),r1 + add #-4,r5 + mov r4,r2 + add #7,r2 + ! +#ifdef __LITTLE_ENDIAN__ +3: mov r1,r3 ! RQPO + shll8 r3 ! QPOx + mov.l @(r0,r5),r1 ! NMLK + mov r1,r6 + shlr16 r6 + shlr8 r6 ! xxxN + or r6,r3 ! QPON + cmp/hi r2,r0 + bt/s 3b + mov.l r3,@-r0 +#else +3: mov r1,r3 ! OPQR + shlr8 r3 ! xOPQ + mov.l @(r0,r5),r1 ! KLMN + mov r1,r6 + shll16 r6 + shll8 r6 ! Nxxx + or r6,r3 ! NOPQ + cmp/hi r2,r0 + bt/s 3b + mov.l r3,@-r0 +#endif + ! + ! Third, copy a byte at once, if necessary + cmp/eq r4,r0 + bt/s 9b + add #6,r5 + bra 8b + add #-6,r2 diff --git a/carl9170fw/carlfw/src/memset.S b/carl9170fw/carlfw/src/memset.S new file mode 100644 index 0000000..d39c8a6 --- /dev/null +++ b/carl9170fw/carlfw/src/memset.S @@ -0,0 +1,58 @@ +/* $Id: memset.S,v 1.1 2000/04/14 16:49:01 mjd Exp $ + * + * "memset" implementation of SuperH + * + * Copyright (C) 1999 Niibe Yutaka + * + */ + +/* + * void *memset(void *s, int c, size_t n); + */ + + .globl _memset + .align 2 + _memset: + tst r6,r6 + bt/s 5f ! if n=0, do nothing + add r6,r4 + mov #12,r0 + cmp/gt r6,r0 + bt/s 4f ! if it's too small, set a byte at once + mov r4,r0 + and #3,r0 + cmp/eq #0,r0 + bt/s 2f ! It's aligned + sub r0,r6 +1: + dt r0 + bf/s 1b + mov.b r5,@-r4 +2: ! make VVVV + extu.b r5,r5 + swap.b r5,r0 ! V0 + or r0,r5 ! VV + swap.w r5,r0 ! VV00 + or r0,r5 ! VVVV + ! + mov r6,r0 + shlr2 r0 + shlr r0 ! r0 = r6 >> 3 +3: + dt r0 + mov.l r5,@-r4 ! set 8-byte at once + bf/s 3b + mov.l r5,@-r4 + ! + mov #7,r0 + and r0,r6 + tst r6,r6 + bt 5f + ! fill bytes +4: + dt r6 + bf/s 4b + mov.b r5,@-r4 +5: + rts + mov r4,r0 diff --git a/carl9170fw/carlfw/src/printf.c b/carl9170fw/carlfw/src/printf.c new file mode 100644 index 0000000..3ff05c4 --- /dev/null +++ b/carl9170fw/carlfw/src/printf.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2004,2008 Kustaa Nyholm + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "carl9170.h" +#include "printf.h" + +#ifdef CONFIG_CARL9170FW_PRINTF +static char *bf; +static char buf[12]; +static unsigned int num; +static char uc; +static char zs; + +static void out(const char c) +{ + *bf++ = c; +} + +static void outDgt(const char dgt) +{ + out(dgt + (dgt < 10 ? '0' : (uc ? 'A' : 'a') - 10)); + zs = 1; +} + +static void divOut(const unsigned int d) +{ + unsigned char dgt = 0; + + while (num >= d) { + num -= d; + dgt++; + } + + if (zs || dgt > 0) + outDgt(dgt); +} + +void tfp_printf(const char *fmt, ...) +{ + va_list va; + char *p; + unsigned int i; + char ch; + + va_start(va, fmt); + + while ((ch = *(fmt++))) { + if (ch != '%') { + putcharacter(ch); + } else { + char lz = 0; + char w = 0; + ch = *(fmt++); + + if (ch == '0') { + ch = *(fmt++); + lz = 1; + } + + if (ch >= '0' && ch <= '9') { + w = 0; + while (ch >= '0' && ch <= '9') { + w = (((w << 2) + w) << 1) + ch - '0'; + ch = *fmt++; + } + } + + bf = buf; + p = bf; + zs = 0; + + switch (ch) { + case 0: + goto abort; + + case 'u': + case 'd': + num = va_arg(va, unsigned int); + if (ch == 'd' && (int) num < 0) { + num = -(int)num; + out('-'); + } + + for (i = 100000000; i != 1; i /= 10) + divOut(i); + + outDgt(num); + break; + + case 'p': + case 'x': + case 'X': + uc = ch == 'X'; + num = va_arg(va, unsigned int); + for (i = 0x10000000; i != 0x1; i >>= 4) + divOut(i); + + outDgt(num); + break; + + case 'c': + out((char)(va_arg(va, int))); + break; + + case 's': + p = va_arg(va, char*); + break; + case '%': + out('%'); + break; + + default: + break; + } + + *bf = 0; + bf = p; + while (*bf++ && w > 0) + w--; + + while (w-- > 0) + putcharacter(lz ? '0' : ' '); + + while ((ch = *p++)) + putcharacter(ch); + } + } + +abort: + putcharacter('\0'); + va_end(va); +} + +#else + +void min_printf(const char *fmt, ...) +{ + char ch; + + do { + ch = *(fmt++); + putcharacter(ch); + } while (ch); +} + +#endif /* CONFIG_CARL9170FW_PRINTF */ diff --git a/carl9170fw/carlfw/src/reboot.S b/carl9170fw/carlfw/src/reboot.S new file mode 100644 index 0000000..14c1813 --- /dev/null +++ b/carl9170fw/carlfw/src/reboot.S @@ -0,0 +1,11 @@ + .globl _jump_to_bootcode + .type _jump_to_bootcode, @function + _jump_to_bootcode: + mov.l stack_start, r0 + mov.l @r0, sp + mov.l eeprom_start, r0 + mov.l @r0, r0 + jmp @r0 + .align 4 +stack_start: .long 0x00000004 +eeprom_start: .long 0x00000000 diff --git a/carl9170fw/carlfw/src/rf.c b/carl9170fw/carlfw/src/rf.c new file mode 100644 index 0000000..e031dd8 --- /dev/null +++ b/carl9170fw/carlfw/src/rf.c @@ -0,0 +1,277 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * PHY and RF functions + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "timer.h" +#include "printf.h" +#include "rf.h" +#include "shared/phy.h" + +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS +static void set_channel_end(void) +{ + /* Manipulate CCA threshold to resume transmission */ + set(AR9170_PHY_REG_CCA_THRESHOLD, 0x0); + /* Disable Virtual CCA */ + andl(AR9170_MAC_REG_QOS_PRIORITY_VIRTUAL_CCA, + ~AR9170_MAC_VIRTUAL_CCA_ALL); + + fw.phy.state = CARL9170_PHY_ON; +} + +void rf_notify_set_channel(void) +{ + /* Manipulate CCA threshold to stop transmission */ + set(AR9170_PHY_REG_CCA_THRESHOLD, 0x300); + /* Enable Virtual CCA */ + orl(AR9170_MAC_REG_QOS_PRIORITY_VIRTUAL_CCA, + AR9170_MAC_VIRTUAL_CCA_ALL); + + /* reset CCA stats */ + fw.tally.active = 0; + fw.tally.cca = 0; + fw.tally.tx_time = 0; + fw.phy.state = CARL9170_PHY_OFF; +} + +/* + * Update delta slope coeff man and exp + */ +static void hw_turn_off_dyn(const uint32_t delta_slope_coeff_exp, + const uint32_t delta_slope_coeff_man, + const uint32_t delta_slope_coeff_exp_shgi, + const uint32_t delta_slope_coeff_man_shgi) +{ + uint32_t tmp; + + tmp = get_async(AR9170_PHY_REG_TIMING3) & 0x00001fff; + tmp |= (delta_slope_coeff_man << AR9170_PHY_TIMING3_DSC_MAN_S) & + AR9170_PHY_TIMING3_DSC_MAN; + tmp |= (delta_slope_coeff_exp << AR9170_PHY_TIMING3_DSC_EXP_S) & + AR9170_PHY_TIMING3_DSC_EXP; + + set(AR9170_PHY_REG_TIMING3, tmp); + + tmp = (delta_slope_coeff_man_shgi << AR9170_PHY_HALFGI_DSC_MAN_S) & + AR9170_PHY_HALFGI_DSC_MAN; + + tmp |= (delta_slope_coeff_exp_shgi << AR9170_PHY_HALFGI_DSC_EXP_S) & + AR9170_PHY_HALFGI_DSC_EXP; + + set(AR9170_PHY_REG_HALFGI, tmp); +} + +static void program_ADDAC(void) +{ + /* ??? Select Internal ADDAC ??? (is external radio) */ + set(AR9170_PHY_REG_ADC_SERIAL_CTL, AR9170_PHY_ADC_SCTL_SEL_EXTERNAL_RADIO); + + delay(10); + + set(0x1c589c, 0x00000000); /*# 7-0 */ + set(0x1c589c, 0x00000000); /*# 15-8 */ + set(0x1c589c, 0x00000000); /*# 23-16 */ + set(0x1c589c, 0x00000000); /*# 31- */ + + set(0x1c589c, 0x00000000); /*# 39- */ + set(0x1c589c, 0x00000000); /*# 47- */ + set(0x1c589c, 0x00000000); /*# 55- [48]:doubles the xtalosc bias current */ + set(0x1c589c, 0x00000000); /*# 63- */ + + set(0x1c589c, 0x00000000); /*# 71- */ + set(0x1c589c, 0x00000000); /*# 79- */ + set(0x1c589c, 0x00000000); /*# 87- */ + set(0x1c589c, 0x00000000); /*# 95- */ + + set(0x1c589c, 0x00000000); /*# 103- */ + set(0x1c589c, 0x00000000); /*# 111- */ + set(0x1c589c, 0x00000000); /*# 119- */ + set(0x1c589c, 0x00000000); /*# 127- */ + + set(0x1c589c, 0x00000000); /*# 135- */ + set(0x1c589c, 0x00000000); /*# 143- */ + set(0x1c589c, 0x00000000); /*# 151- */ + set(0x1c589c, 0x00000030); /*# 159- #[158:156]=xlnabufmode */ + + set(0x1c589c, 0x00000004); /*# 167- [162]:disable clkp_driver to flow */ + set(0x1c589c, 0x00000000); /*# 175- */ + set(0x1c589c, 0x00000000); /*# 183-176 */ + set(0x1c589c, 0x00000000); /*# 191-184 */ + + set(0x1c589c, 0x00000000); /*# 199- */ + set(0x1c589c, 0x00000000); /*# 207- */ + set(0x1c589c, 0x00000000); /*# 215- */ + set(0x1c589c, 0x00000000); /*# 223- */ + + set(0x1c589c, 0x00000000); /*# 231- */ + set(0x1c58c4, 0x00000000); /*# 233-232 */ + + delay(10); + + /* Select External Flow ???? (is internal addac??) */ + set(AR9170_PHY_REG_ADC_SERIAL_CTL, AR9170_PHY_ADC_SCTL_SEL_INTERNAL_ADDAC); +} + +static uint32_t AGC_calibration(uint32_t loop) +{ + uint32_t wrdata; + uint32_t ret; + +#define AGC_CAL_NF (AR9170_PHY_AGC_CONTROL_CAL | AR9170_PHY_AGC_CONTROL_NF) + + wrdata = get_async(AR9170_PHY_REG_AGC_CONTROL) | AGC_CAL_NF; + set(AR9170_PHY_REG_AGC_CONTROL, wrdata); + + ret = get_async(AR9170_PHY_REG_AGC_CONTROL) & AGC_CAL_NF; + + /* sitesurvey : 100 ms / current connected 200 ms */ + while ((ret != 0) && loop--) { + udelay(100); + + ret = get_async(AR9170_PHY_REG_AGC_CONTROL) & AGC_CAL_NF; + } + + /* return the AGC/Noise calibration state to the driver */ + return ret; +} + +#define EIGHTY_FLAG (CARL9170FW_PHY_HT_ENABLE | CARL9170FW_PHY_HT_DYN2040) + +static uint32_t rf_init(const uint32_t delta_slope_coeff_exp, + const uint32_t delta_slope_coeff_man, + const uint32_t delta_slope_coeff_exp_shgi, + const uint32_t delta_slope_coeff_man_shgi, + const uint32_t finiteLoopCount, + const bool initialize) +{ + uint32_t ret; + + hw_turn_off_dyn(delta_slope_coeff_exp, + delta_slope_coeff_man, + delta_slope_coeff_exp_shgi, + delta_slope_coeff_man_shgi); + + if (initialize) { + /* Real Chip */ + program_ADDAC(); + + /* inverse chain 0 <-> chain 2 */ + set(AR9170_PHY_REG_ANALOG_SWAP, AR9170_PHY_ANALOG_SWAP_AB); + + /* swap chain 0 and chain 2 */ + set(AR9170_PHY_REG_ANALOG_SWAP, AR9170_PHY_ANALOG_SWAP_AB | + AR9170_PHY_ANALOG_SWAP_ALT_CHAIN); + + /* Activate BB */ + set(AR9170_PHY_REG_ACTIVE, AR9170_PHY_ACTIVE_EN); + delay(10); + } + + ret = AGC_calibration(finiteLoopCount); + + set_channel_end(); + return ret; +} + +void rf_cmd(const struct carl9170_cmd *cmd, struct carl9170_rsp *resp) +{ + uint32_t ret; + + fw.phy.ht_settings = cmd->rf_init.ht_settings; + fw.phy.frequency = cmd->rf_init.freq; + + /* + * Is the clock controlled by the PHY? + */ + if ((fw.phy.ht_settings & EIGHTY_FLAG) == EIGHTY_FLAG) + clock_set(AHB_80_88MHZ, true); + else + clock_set(AHB_40_44MHZ, true); + + ret = rf_init(le32_to_cpu(cmd->rf_init.delta_slope_coeff_exp), + le32_to_cpu(cmd->rf_init.delta_slope_coeff_man), + le32_to_cpu(cmd->rf_init.delta_slope_coeff_exp_shgi), + le32_to_cpu(cmd->rf_init.delta_slope_coeff_man_shgi), + le32_to_cpu(cmd->rf_init.finiteLoopCount), + cmd->hdr.cmd == CARL9170_CMD_RF_INIT); + + resp->hdr.len = sizeof(struct carl9170_rf_init_result); + resp->rf_init_res.ret = cpu_to_le32(ret); +} + +void rf_psm(void) +{ + u32 bank3; + + if (fw.phy.psm.state == CARL9170_PSM_SOFTWARE) { + /* not enabled by the driver */ + return; + } + + if (fw.phy.psm.state & CARL9170_PSM_SLEEP) { + fw.phy.psm.state &= ~CARL9170_PSM_SLEEP; + + /* disable all agc gain and offset updates to a2 */ + set(AR9170_PHY_REG_TEST2, 0x8000000); + + /* power down ADDAC */ + set(AR9170_PHY_REG_ADC_CTL, + AR9170_PHY_ADC_CTL_OFF_PWDDAC | + AR9170_PHY_ADC_CTL_OFF_PWDADC | + 0xa0000000); + + /* Synthesizer off + RX off */ + bank3 = 0x00400018; + + fw.phy.state = CARL9170_PHY_OFF; + } else { + /* advance to the next PSM step */ + fw.phy.psm.state--; + + if (fw.phy.psm.state == CARL9170_PSM_WAKE) { + /* wake up ADDAC */ + set(AR9170_PHY_REG_ADC_CTL, + AR9170_PHY_ADC_CTL_OFF_PWDDAC | + AR9170_PHY_ADC_CTL_OFF_PWDADC); + + /* enable all agc gain and offset updates to a2 */ + set(AR9170_PHY_REG_TEST2, 0x0); + + /* Synthesizer on + RX on */ + bank3 = 0x01420098; + + fw.phy.state = CARL9170_PHY_ON; + } else { + return ; + } + } + + if (fw.phy.frequency < 3000000) + bank3 |= 0x00800000; + + set(0x1c58f0, bank3); +} + +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ diff --git a/carl9170fw/carlfw/src/uart.c b/carl9170fw/carlfw/src/uart.c new file mode 100644 index 0000000..58f6486 --- /dev/null +++ b/carl9170fw/carlfw/src/uart.c @@ -0,0 +1,76 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * UART debug interface functions. + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "uart.h" +#include "io.h" + +#ifdef CONFIG_CARL9170FW_DEBUG_UART +void uart_putc(const char c) +{ + set(AR9170_UART_REG_TX_HOLDING, c); + + while (get(AR9170_UART_REG_LINE_STATUS) & + AR9170_UART_LINE_STS_TX_FIFO_ALMOST_EMPTY) { + /* + * wait until the byte has made it + */ + } +} + +void uart_print_hex_dump(const void *buf, const int len) +{ + unsigned int offset = 0; + + uart_putc('H'); + uart_putc('D'); + uart_putc(':'); + + while (len > 0) { + uart_putc(*((uint8_t *) buf + offset)); + offset++; + } +} + +void uart_init(void) +{ + unsigned int timeout = 0; + +#ifdef CONFIG_CARL9170FW_UART_CLOCK_25M + set(AR9170_UART_REG_DIVISOR_LSB, 0xc); +#elif CONFIG_CARL9170FW_UART_CLOCK_40M + set(AR9170_UART_REG_DIVISOR_LSB, 0x14); /* 40 MHz */ + set(AR9170_UART_REG_REMAINDER, 0xb38e); +#else +#error "Unsupported UART clock" +#endif /* CARL9170FW_UART_CLOCK_25M */ + + while (get(AR9170_UART_REG_LINE_STATUS) & + AR9170_UART_LINE_STS_TRANSMITTER_EMPTY) { + if (timeout++ >= 10000) + break; + } +} +#endif /* CONFIG_CARL9170FW_DEBUG_UART */ diff --git a/carl9170fw/carlfw/src/udivsi3_i4i-Os.S b/carl9170fw/carlfw/src/udivsi3_i4i-Os.S new file mode 100644 index 0000000..7bbec4c --- /dev/null +++ b/carl9170fw/carlfw/src/udivsi3_i4i-Os.S @@ -0,0 +1,149 @@ +/* Copyright (C) 2006 Free Software Foundation, Inc. + +This file 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, or (at your option) any +later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +This file is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ + +/* Moderately Space-optimized libgcc routines for the Renesas SH / + STMicroelectronics ST40 CPUs. + Contributed by J"orn Rennecke joern.rennecke@st.com. */ + +/* Size: 186 bytes jointly for udivsi3_i4i and sdivsi3_i4i + sh4-200 run times: + udiv small divisor: 55 cycles + udiv large divisor: 52 cycles + sdiv small divisor, positive result: 59 cycles + sdiv large divisor, positive result: 56 cycles + sdiv small divisor, negative result: 65 cycles (*) + sdiv large divisor, negative result: 62 cycles (*) + (*): r2 is restored in the rts delay slot and has a lingering latency + of two more cycles. */ + .balign 4 + .global ___udivsi3_i4i + .global ___udivsi3_i4 + .set ___udivsi3_i4, ___udivsi3_i4i + .type ___udivsi3_i4i, @function + .type ___sdivsi3_i4i, @function +___udivsi3_i4i: + sts pr,r1 + mov.l r4,@-r15 + extu.w r5,r0 + cmp/eq r5,r0 + swap.w r4,r0 + shlr16 r4 + bf/s large_divisor + div0u + mov.l r5,@-r15 + shll16 r5 +sdiv_small_divisor: + div1 r5,r4 + bsr div6 + div1 r5,r4 + div1 r5,r4 + bsr div6 + div1 r5,r4 + xtrct r4,r0 + xtrct r0,r4 + bsr div7 + swap.w r4,r4 + div1 r5,r4 + bsr div7 + div1 r5,r4 + xtrct r4,r0 + mov.l @r15+,r5 + swap.w r0,r0 + mov.l @r15+,r4 + jmp @r1 + rotcl r0 +div7: + div1 r5,r4 +div6: + div1 r5,r4; div1 r5,r4; div1 r5,r4 + div1 r5,r4; div1 r5,r4; rts; div1 r5,r4 + +divx3: + rotcl r0 + div1 r5,r4 + rotcl r0 + div1 r5,r4 + rotcl r0 + rts + div1 r5,r4 + +large_divisor: + mov.l r5,@-r15 +sdiv_large_divisor: + xor r4,r0 + .rept 4 + rotcl r0 + bsr divx3 + div1 r5,r4 + .endr + mov.l @r15+,r5 + mov.l @r15+,r4 + jmp @r1 + rotcl r0 + + .global __sdivsi3_i4i + .global __sdivsi3_i4 + .global __sdivsi3 + .set __sdivsi3_i4, __sdivsi3_i4i + .set __sdivsi3, __sdivsi3_i4i +__sdivsi3_i4i: + mov.l r4,@-r15 + cmp/pz r5 + mov.l r5,@-r15 + bt/s pos_divisor + cmp/pz r4 + neg r5,r5 + extu.w r5,r0 + bt/s neg_result + cmp/eq r5,r0 + neg r4,r4 +pos_result: + swap.w r4,r0 + bra sdiv_check_divisor + sts pr,r1 +pos_divisor: + extu.w r5,r0 + bt/s pos_result + cmp/eq r5,r0 + neg r4,r4 +neg_result: + mova negate_result,r0 + ; + mov r0,r1 + swap.w r4,r0 + lds r2,macl + sts pr,r2 +sdiv_check_divisor: + shlr16 r4 + bf/s sdiv_large_divisor + div0u + bra sdiv_small_divisor + shll16 r5 + .balign 4 +negate_result: + neg r0,r0 + jmp @r2 + sts macl,r2 diff --git a/carl9170fw/carlfw/src/wlan.c b/carl9170fw/carlfw/src/wlan.c new file mode 100644 index 0000000..d3328d6 --- /dev/null +++ b/carl9170fw/carlfw/src/wlan.c @@ -0,0 +1,1211 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Interface to the WLAN part of the chip + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "shared/phy.h" +#include "hostif.h" +#include "timer.h" +#include "wl.h" +#include "printf.h" +#include "rf.h" +#include "linux/ieee80211.h" +#include "wol.h" + +static void wlan_txunstuck(unsigned int queue) +{ + set_wlan_txq_dma_addr(queue, ((uint32_t) fw.wlan.tx_queue[queue].head) | 1); +} + +#ifdef CONFIG_CARL9170FW_DMA_QUEUE_BUMP +static void wlan_txupdate(unsigned int queue) +{ + set_wlan_txq_dma_addr(queue, ((uint32_t) fw.wlan.tx_queue[queue].head)); +} + +static void wlan_dma_bump(unsigned int qidx) +{ + unsigned int offset = qidx; + uint32_t status, trigger; + + status = get(AR9170_MAC_REG_DMA_STATUS) >> 12; + trigger = get(AR9170_MAC_REG_DMA_TRIGGER) >> 12; + + while (offset != 0) { + status >>= 4; + trigger >>= 4; + offset--; + } + + status &= 0xf; + trigger &= 0xf; + + if ((trigger == 0xa) && (status == 0x8)) { + DBG("UNSTUCK"); + wlan_txunstuck(qidx); + } else { + DBG("UPDATE"); + wlan_txupdate(qidx); + } +} +#endif /* CONFIG_CARL9170FW_DMA_QUEUE_BUMP */ + +#ifdef CONFIG_CARL9170FW_DEBUG +static void wlan_dump_queue(unsigned int qidx) +{ + + struct dma_desc *desc; + struct carl9170_tx_superframe *super; + int entries = 0; + + __for_each_desc(desc, &fw.wlan.tx_queue[qidx]) { + super = get_super(desc); + DBG("%d: %p s:%x c:%x tl:%x ds:%x n:%p l:%p ", entries, desc, + desc->status, desc->ctrl, desc->totalLen, + desc->dataSize, desc->nextAddr, desc->lastAddr); + + DBG("c:%x tr:%d ri:%d l:%x m:%x p:%x fc:%x", + super->s.cookie, super->s.cnt, super->s.rix, + super->f.hdr.length, super->f.hdr.mac.set, + (unsigned int) le32_to_cpu(super->f.hdr.phy.set), + super->f.data.i3e.frame_control); + + entries++; + } + + desc = get_wlan_txq_addr(qidx); + + DBG("Queue: %d: te:%d td:%d h:%p c:%p t:%p", + qidx, entries, queue_len(&fw.wlan.tx_queue[qidx]), + fw.wlan.tx_queue[qidx].head, + desc, fw.wlan.tx_queue[qidx].terminator); + + DBG("HW: t:%x s:%x ac:%x c:%x", + (unsigned int) get(AR9170_MAC_REG_DMA_TRIGGER), + (unsigned int) get(AR9170_MAC_REG_DMA_STATUS), + (unsigned int) get(AR9170_MAC_REG_AMPDU_COUNT), + (unsigned int) get(AR9170_MAC_REG_DMA_TXQX_ADDR_CURR)); +} +#endif /* CONFIG_CARL9170FW_DEBUG */ + +static void wlan_send_buffered_tx_status(void) +{ + unsigned int len; + + while (fw.wlan.tx_status_pending) { + len = min((unsigned int)fw.wlan.tx_status_pending, + CARL9170_RSP_TX_STATUS_NUM); + len = min(len, CARL9170_TX_STATUS_NUM - fw.wlan.tx_status_head_idx); + + /* + * rather than memcpy each individual request into a large buffer, + * we _splice_ them all together. + * + * The only downside is however that we have to be careful around + * the edges of the tx_status_cache. + * + * Note: + * Each tx_status is about 2 bytes. However every command package + * must have a size which is a multiple of 4. + */ + + send_cmd_to_host((len * sizeof(struct carl9170_tx_status) + 3) & ~3, + CARL9170_RSP_TXCOMP, len, (void *) + &fw.wlan.tx_status_cache[fw.wlan.tx_status_head_idx]); + + fw.wlan.tx_status_pending -= len; + fw.wlan.tx_status_head_idx += len; + fw.wlan.tx_status_head_idx %= CARL9170_TX_STATUS_NUM; + } +} + +static struct carl9170_tx_status *wlan_get_tx_status_buffer(void) +{ + struct carl9170_tx_status *tmp; + + tmp = &fw.wlan.tx_status_cache[fw.wlan.tx_status_tail_idx++]; + fw.wlan.tx_status_tail_idx %= CARL9170_TX_STATUS_NUM; + + if (fw.wlan.tx_status_pending == CARL9170_TX_STATUS_NUM) + wlan_send_buffered_tx_status(); + + fw.wlan.tx_status_pending++; + + return tmp; +} + +/* generate _aggregated_ tx_status for the host */ +void wlan_tx_complete(struct carl9170_tx_superframe *super, + bool txs) +{ + struct carl9170_tx_status *status; + + status = wlan_get_tx_status_buffer(); + + /* + * The *unique* cookie and AC_ID is used by the driver for + * frame lookup. + */ + status->cookie = super->s.cookie; + status->queue = super->s.queue; + super->s.cookie = 0; + + /* + * This field holds the number of tries of the rate in + * the rate index field (rix). + */ + status->rix = super->s.rix; + status->tries = super->s.cnt; + status->success = (txs) ? 1 : 0; +} + +static bool wlan_tx_consume_retry(struct carl9170_tx_superframe *super) +{ + /* check if this was the last possible retry with this rate */ + if (unlikely(super->s.cnt >= super->s.ri[super->s.rix].tries)) { + /* end of the road - indicate tx failure */ + if (unlikely(super->s.rix == CARL9170_TX_MAX_RETRY_RATES)) + return false; + + /* check if there are alternative rates available */ + if (!super->s.rr[super->s.rix].set) + return false; + + /* try next retry rate */ + super->f.hdr.phy.set = super->s.rr[super->s.rix].set; + + /* finally - mark the old rate as USED */ + super->s.rix++; + + /* update MAC flags */ + super->f.hdr.mac.erp_prot = super->s.ri[super->s.rix].erp_prot; + super->f.hdr.mac.ampdu = super->s.ri[super->s.rix].ampdu; + + /* reinitialize try counter */ + super->s.cnt = 1; + } else { + /* just increase retry counter */ + super->s.cnt++; + } + + return true; +} + +static inline u16 get_tid(struct ieee80211_hdr *hdr) +{ + return (ieee80211_get_qos_ctl(hdr))[0] & IEEE80211_QOS_CTL_TID_MASK; +} + +/* This function will only work on uint32_t-aligned pointers! */ +static bool same_hdr(const void *_d0, const void *_d1) +{ + const uint32_t *d0 = _d0; + const uint32_t *d1 = _d1; + + /* BUG_ON((unsigned long)d0 & 3 || (unsigned long)d1 & 3)) */ + return !((d0[0] ^ d1[0]) | /* FC + DU */ + (d0[1] ^ d1[1]) | /* addr1 */ + (d0[2] ^ d1[2]) | (d0[3] ^ d1[3]) | /* addr2 + addr3 */ + (d0[4] ^ d1[4])); /* addr3 */ +} + +static inline bool same_aggr(struct ieee80211_hdr *a, struct ieee80211_hdr *b) +{ + return (get_tid(a) == get_tid(b)) || same_hdr(a, b); +} + +static void wlan_tx_ampdu_reset(unsigned int qidx) +{ + fw.wlan.ampdu_prev[qidx] = NULL; +} + +static void wlan_tx_ampdu_end(unsigned int qidx) +{ + struct carl9170_tx_superframe *ht_prev = fw.wlan.ampdu_prev[qidx]; + + if (ht_prev) + ht_prev->f.hdr.mac.ba_end = 1; + + wlan_tx_ampdu_reset(qidx); +} + +static void wlan_tx_ampdu(struct carl9170_tx_superframe *super) +{ + unsigned int qidx = super->s.queue; + struct carl9170_tx_superframe *ht_prev = fw.wlan.ampdu_prev[qidx]; + + if (super->f.hdr.mac.ampdu) { + if (ht_prev && + !same_aggr(&super->f.data.i3e, &ht_prev->f.data.i3e)) + ht_prev->f.hdr.mac.ba_end = 1; + else + super->f.hdr.mac.ba_end = 0; + + fw.wlan.ampdu_prev[qidx] = super; + } else { + wlan_tx_ampdu_end(qidx); + } +} + +/* for all tries */ +static void __wlan_tx(struct dma_desc *desc) +{ + struct carl9170_tx_superframe *super = get_super(desc); + + if (unlikely(super->s.fill_in_tsf)) { + struct ieee80211_mgmt *mgmt = (void *) &super->f.data.i3e; + uint32_t *tsf = (uint32_t *) &mgmt->u.probe_resp.timestamp; + + /* + * Truth be told: this is a hack. + * + * The *real* TSF is definitely going to be higher/older. + * But this hardware emulation code is head and shoulders + * above anything a driver can possibly do. + * + * (even, if it's got an accurate atomic clock source). + */ + + read_tsf(tsf); + } + + wlan_tx_ampdu(super); + +#ifdef CONFIG_CARL9170FW_DEBUG + BUG_ON(fw.phy.psm.state != CARL9170_PSM_WAKE); +#endif /* CONFIG_CARL9170FW_DEBUG */ + + /* insert desc into the right queue */ + dma_put(&fw.wlan.tx_queue[super->s.queue], desc); +} + +static void wlan_assign_seq(struct ieee80211_hdr *hdr, unsigned int vif) +{ + hdr->seq_ctrl &= cpu_to_le16(~IEEE80211_SCTL_SEQ); + hdr->seq_ctrl |= cpu_to_le16(fw.wlan.sequence[vif]); + + if (ieee80211_is_first_frag(hdr->seq_ctrl)) + fw.wlan.sequence[vif] += 0x10; +} + +/* prepares frame for the first transmission */ +static void _wlan_tx(struct dma_desc *desc) +{ + struct carl9170_tx_superframe *super = get_super(desc); + + if (unlikely(super->s.assign_seq)) + wlan_assign_seq(&super->f.data.i3e, super->s.vif_id); + + if (unlikely(super->s.ampdu_commit_density)) { + set(AR9170_MAC_REG_AMPDU_DENSITY, + MOD_VAL(AR9170_MAC_AMPDU_DENSITY, + get(AR9170_MAC_REG_AMPDU_DENSITY), + super->s.ampdu_density)); + } + + if (unlikely(super->s.ampdu_commit_factor)) { + set(AR9170_MAC_REG_AMPDU_FACTOR, + MOD_VAL(AR9170_MAC_AMPDU_FACTOR, + get(AR9170_MAC_REG_AMPDU_FACTOR), + 8 << super->s.ampdu_factor)); + } +} + +/* propagate transmission status back to the driver */ +static bool wlan_tx_status(struct dma_queue *queue, + struct dma_desc *desc) +{ + struct carl9170_tx_superframe *super = get_super(desc); + unsigned int qidx = super->s.queue; + bool txfail = false, success; + + success = true; + + /* update hangcheck */ + fw.wlan.last_super_num[qidx] = 0; + + /* + * Note: + * There could be a corner case when the TXFAIL is set + * even though the frame was properly ACKed by the peer: + * a BlockAckReq with the immediate policy will cause + * the receiving peer to produce a BlockACK unfortunately + * the MAC in this chip seems to be expecting a legacy + * ACK and marks the BAR as failed! + */ + + if (!!(desc->ctrl & AR9170_CTRL_FAIL)) { + txfail = !!(desc->ctrl & AR9170_CTRL_TXFAIL); + + /* reset retry indicator flags */ + desc->ctrl &= ~(AR9170_CTRL_TXFAIL | AR9170_CTRL_BAFAIL); + + /* + * Note: wlan_tx_consume_retry will override the old + * phy [CCK,OFDM, HT, BW20/40, MCS...] and mac vectors + * [AMPDU,RTS/CTS,...] therefore be careful when they + * are used. + */ + if (wlan_tx_consume_retry(super)) { + /* + * retry for simple and aggregated 802.11 frames. + * + * Note: We must not mess up the original frame + * order. + */ + + if (!super->f.hdr.mac.ampdu) { + /* + * 802.11 - 7.1.3.1.5. + * set "Retry Field" for consecutive attempts + * + * Note: For AMPDU see: + * 802.11n 9.9.1.6 "Retransmit Procedures" + */ + super->f.data.i3e.frame_control |= + cpu_to_le16(IEEE80211_FCTL_RETRY); + } + + if (txfail) { + /* Normal TX Failure */ + + /* demise descriptor ownership back to the hardware */ + dma_rearm(desc); + + /* + * And this will get the queue going again. + * To understand why: you have to get the HW + * specs... But sadly I never saw them. + */ + wlan_txunstuck(qidx); + + /* abort cycle - this is necessary due to HW design */ + return false; + } else { + /* (HT-) BlockACK failure */ + + /* + * Unlink the failed attempt and put it into + * the retry queue. The caller routine must + * be aware of this so the frames don't get lost. + */ + +#ifndef CONFIG_CARL9170FW_DEBUG + dma_unlink_head(queue); +#else /* CONFIG_CARL9170FW_DEBUG */ + BUG_ON(dma_unlink_head(queue) != desc); +#endif /* CONFIG_CARL9170FW_DEBUG */ + dma_put(&fw.wlan.tx_retry, desc); + return true; + } + } else { + /* out of frame attempts - discard frame */ + success = false; + } + } + +#ifndef CONFIG_CARL9170FW_DEBUG + dma_unlink_head(queue); +#else /* CONFIG_CARL9170FW_DEBUG */ + BUG_ON(dma_unlink_head(queue) != desc); +#endif /* CONFIG_CARL9170FW_DEBUG */ + if (txfail) { + /* + * Issue the queue bump, + * We need to do this in case this was the frame's last + * possible retry attempt and it unfortunately: it failed. + */ + + wlan_txunstuck(qidx); + } + + unhide_super(desc); + + if (unlikely(super == fw.wlan.fw_desc_data)) { + fw.wlan.fw_desc = desc; + fw.wlan.fw_desc_available = 1; + + if (fw.wlan.fw_desc_callback) + fw.wlan.fw_desc_callback(super, success); + + return true; + } + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + if (unlikely(super->s.cab)) + fw.wlan.cab_queue_len[super->s.vif_id]--; +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + + wlan_tx_complete(super, success); + + if (ieee80211_is_back_req(super->f.data.i3e.frame_control)) { + fw.wlan.queued_bar--; + } + + /* recycle freed descriptors */ + dma_reclaim(&fw.pta.down_queue, desc); + down_trigger(); + return true; +} + +static void handle_tx_completion(void) +{ + struct dma_desc *desc; + int i; + + for (i = AR9170_TXQ_SPECIAL; i >= AR9170_TXQ0; i--) { + __while_desc_bits(desc, &fw.wlan.tx_queue[i], AR9170_OWN_BITS_SW) { + if (!wlan_tx_status(&fw.wlan.tx_queue[i], desc)) { + /* termination requested. */ + break; + } + } + + wlan_tx_ampdu_reset(i); + + for_each_desc(desc, &fw.wlan.tx_retry) + __wlan_tx(desc); + + wlan_tx_ampdu_end(i); + if (!queue_empty(&fw.wlan.tx_queue[i])) + wlan_trigger(BIT(i)); + } +} + +void __hot wlan_tx(struct dma_desc *desc) +{ + struct carl9170_tx_superframe *super = DESC_PAYLOAD(desc); + + if (ieee80211_is_back_req(super->f.data.i3e.frame_control)) { + fw.wlan.queued_bar++; + } + + /* initialize rate control struct */ + super->s.rix = 0; + super->s.cnt = 1; + hide_super(desc); + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + if (unlikely(super->s.cab)) { + fw.wlan.cab_queue_len[super->s.vif_id]++; + dma_put(&fw.wlan.cab_queue[super->s.vif_id], desc); + return; + } +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + + _wlan_tx(desc); + __wlan_tx(desc); + wlan_trigger(BIT(super->s.queue)); +} + +void wlan_tx_fw(struct carl9170_tx_superdesc *super, fw_desc_callback_t cb) +{ + if (!fw.wlan.fw_desc_available) + return; + + fw.wlan.fw_desc_available = 0; + + /* Format BlockAck */ + fw.wlan.fw_desc->ctrl = AR9170_CTRL_FS_BIT | AR9170_CTRL_LS_BIT; + fw.wlan.fw_desc->status = AR9170_OWN_BITS_SW; + + fw.wlan.fw_desc->totalLen = fw.wlan.fw_desc->dataSize = super->len; + fw.wlan.fw_desc_data = fw.wlan.fw_desc->dataAddr = super; + fw.wlan.fw_desc->nextAddr = fw.wlan.fw_desc->lastAddr = + fw.wlan.fw_desc; + fw.wlan.fw_desc_callback = cb; + wlan_tx(fw.wlan.fw_desc); +} + +static void wlan_send_buffered_ba(void) +{ + struct carl9170_tx_ba_superframe *baf = &dma_mem.reserved.ba.ba; + struct ieee80211_ba *ba = (struct ieee80211_ba *) &baf->f.ba; + struct carl9170_bar_ctx *ctx; + + if (likely(!fw.wlan.queued_ba)) + return; + + /* there's no point to continue when the ba_desc is not available. */ + if (!fw.wlan.fw_desc_available) + return; + + ctx = &fw.wlan.ba_cache[fw.wlan.ba_head_idx]; + fw.wlan.ba_head_idx++; + fw.wlan.ba_head_idx %= CONFIG_CARL9170FW_BACK_REQS_NUM; + fw.wlan.queued_ba--; + + baf->s.len = sizeof(struct carl9170_tx_superdesc) + + sizeof(struct ar9170_tx_hwdesc) + + sizeof(struct ieee80211_ba); + baf->s.ri[0].tries = 1; + baf->s.cookie = 0; + baf->s.queue = AR9170_TXQ_VO; + baf->f.hdr.length = sizeof(struct ieee80211_ba) + FCS_LEN; + + baf->f.hdr.mac.no_ack = 1; + + baf->f.hdr.phy.modulation = 1; /* OFDM */ + baf->f.hdr.phy.tx_power = 34; /* 17 dBm */ + baf->f.hdr.phy.chains = 1; + baf->f.hdr.phy.mcs = AR9170_TXRX_PHY_RATE_OFDM_6M; + + /* format outgoing BA */ + ba->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_BACK); + ba->duration = cpu_to_le16(0); + + /* the BAR contains all necessary MACs. All we need is to swap them */ + memcpy(ba->ra, ctx->ta, 6); + memcpy(ba->ta, ctx->ra, 6); + + /* + * Unfortunately, we cannot look into the hardware's scoreboard. + * Therefore we have to proceed as described in 802.11n 9.10.7.5 + * and send a null BlockAck. + */ + memset(ba->bitmap, 0x0, sizeof(ba->bitmap)); + + /* + * Both, the original firmare and ath9k set the NO ACK flag in + * the BA Ack Policy subfield. + */ + ba->control = ctx->control | cpu_to_le16(1); + ba->start_seq_num = ctx->start_seq_num; + wlan_tx_fw(&baf->s, NULL); +} + +static struct carl9170_bar_ctx *wlan_get_bar_cache_buffer(void) +{ + struct carl9170_bar_ctx *tmp; + + tmp = &fw.wlan.ba_cache[fw.wlan.ba_tail_idx]; + fw.wlan.ba_tail_idx++; + fw.wlan.ba_tail_idx %= CONFIG_CARL9170FW_BACK_REQS_NUM; + if (fw.wlan.queued_ba < CONFIG_CARL9170FW_BACK_REQS_NUM) + fw.wlan.queued_ba++; + + return tmp; +} + +static void handle_bar(struct dma_desc *desc __unused, struct ieee80211_hdr *hdr, + unsigned int len, unsigned int mac_err) +{ + struct ieee80211_bar *bar; + struct carl9170_bar_ctx *ctx; + + if (unlikely(mac_err)) { + /* + * This check does a number of things: + * 1. checks if the frame is in good nick + * 2. checks if the RA (MAC) matches + */ + return ; + } + + if (unlikely(len < (sizeof(struct ieee80211_bar) + FCS_LEN))) { + /* + * Sneaky, corrupted BARs... but not with us! + */ + + return ; + } + + bar = (void *) hdr; + + if ((bar->control & cpu_to_le16(IEEE80211_BAR_CTRL_MULTI_TID)) || + !(bar->control & cpu_to_le16(IEEE80211_BAR_CTRL_CBMTID_COMPRESSED_BA))) { + /* not implemented yet */ + + return ; + } + + ctx = wlan_get_bar_cache_buffer(); + + memcpy(ctx->ra, bar->ra, 6); + memcpy(ctx->ta, bar->ta, 6); + ctx->control = bar->control; + ctx->start_seq_num = bar->start_seq_num; +} + +static void wlan_check_rx_overrun(void) +{ + uint32_t overruns, total; + + fw.tally.rx_total += total = get(AR9170_MAC_REG_RX_TOTAL); + fw.tally.rx_overrun += overruns = get(AR9170_MAC_REG_RX_OVERRUN); + if (unlikely(overruns)) { + if (overruns == total) { + DBG("RX Overrun"); + fw.wlan.mac_reset++; + } + + wlan_trigger(AR9170_DMA_TRIGGER_RXQ); + } +} + +static unsigned int wlan_rx_filter(struct dma_desc *desc) +{ + struct ieee80211_hdr *hdr; + unsigned int data_len; + unsigned int rx_filter; + unsigned int mac_err; + + data_len = ar9170_get_rx_mpdu_len(desc); + mac_err = ar9170_get_rx_macstatus_error(desc); + +#define AR9170_RX_ERROR_BAD (AR9170_RX_ERROR_FCS | AR9170_RX_ERROR_PLCP) + + if (unlikely(data_len < (4 + 6 + FCS_LEN) || + desc->totalLen > CONFIG_CARL9170FW_RX_FRAME_LEN) || + mac_err & AR9170_RX_ERROR_BAD) { + /* + * This frame is too damaged to do anything + * useful with it. + */ + + return CARL9170_RX_FILTER_BAD; + } + + rx_filter = 0; + if (mac_err & AR9170_RX_ERROR_WRONG_RA) + rx_filter |= CARL9170_RX_FILTER_OTHER_RA; + + if (mac_err & AR9170_RX_ERROR_DECRYPT) + rx_filter |= CARL9170_RX_FILTER_DECRY_FAIL; + + hdr = ar9170_get_rx_i3e(desc); + if (likely(ieee80211_is_data(hdr->frame_control))) { + rx_filter |= CARL9170_RX_FILTER_DATA; + } else if (ieee80211_is_ctl(hdr->frame_control)) { + switch (le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_STYPE) { + case IEEE80211_STYPE_BACK_REQ: + handle_bar(desc, hdr, data_len, mac_err); + rx_filter |= CARL9170_RX_FILTER_CTL_BACKR; + break; + case IEEE80211_STYPE_PSPOLL: + rx_filter |= CARL9170_RX_FILTER_CTL_PSPOLL; + break; + case IEEE80211_STYPE_BACK: + if (fw.wlan.queued_bar) { + /* + * Don't filter block acks when the application + * has queued BARs. This is because the firmware + * can't do the accouting and the application + * has to sort out if the BA belongs to any BARs. + */ + break; + } + /* otherwise fall through */ + default: + rx_filter |= CARL9170_RX_FILTER_CTL_OTHER; + break; + } + } else { + /* ieee80211_is_mgmt */ + rx_filter |= CARL9170_RX_FILTER_MGMT; + } + + if (unlikely(fw.suspend_mode == CARL9170_HOST_SUSPENDED)) { + wol_rx(rx_filter, hdr, min(data_len, + (unsigned int)AR9170_BLOCK_SIZE)); + } + +#undef AR9170_RX_ERROR_BAD + + return rx_filter; +} + +static void handle_rx(void) +{ + struct dma_desc *desc; + + for_each_desc_not_bits(desc, &fw.wlan.rx_queue, AR9170_OWN_BITS_HW) { + if (!(wlan_rx_filter(desc) & fw.wlan.rx_filter)) { + dma_put(&fw.pta.up_queue, desc); + up_trigger(); + } else { + dma_reclaim(&fw.wlan.rx_queue, desc); + wlan_trigger(AR9170_DMA_TRIGGER_RXQ); + } + } +} + +#ifdef CONFIG_CARL9170FW_CAB_QUEUE +void wlan_cab_flush_queue(const unsigned int vif) +{ + struct dma_queue *cab_queue = &fw.wlan.cab_queue[vif]; + struct dma_desc *desc; + + /* move queued frames into the main tx queues */ + for_each_desc(desc, cab_queue) { + struct carl9170_tx_superframe *super = get_super(desc); + if (!queue_empty(cab_queue)) { + /* + * Set MOREDATA flag for all, + * but the last queued frame. + * see: 802.11-2007 11.2.1.5 f) + * + * This is actually the reason to why + * we need to prevent the reentry. + */ + + super->f.data.i3e.frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } else { + super->f.data.i3e.frame_control &= + cpu_to_le16(~IEEE80211_FCTL_MOREDATA); + } + + /* ready to roll! */ + _wlan_tx(desc); + __wlan_tx(desc); + wlan_trigger(BIT(super->s.queue)); + } +} + +static uint8_t *beacon_find_ie(uint8_t ie, void *addr, + const unsigned int len) +{ + struct ieee80211_mgmt *mgmt = addr; + uint8_t *pos, *end; + + pos = mgmt->u.beacon.variable; + end = (uint8_t *) ((unsigned long)mgmt + (len - FCS_LEN)); + while (pos < end) { + if (pos + 2 + pos[1] > end) + return NULL; + + if (pos[0] == ie) + return pos; + + pos += pos[1] + 2; + } + + return NULL; +} + +void wlan_modify_beacon(const unsigned int vif, + const unsigned int addr, const unsigned int len) +{ + uint8_t *_ie; + struct ieee80211_tim_ie *ie; + + _ie = beacon_find_ie(WLAN_EID_TIM, (void *)addr, len); + if (likely(_ie)) { + ie = (struct ieee80211_tim_ie *) &_ie[2]; + + if (!queue_empty(&fw.wlan.cab_queue[vif]) && (ie->dtim_count == 0)) { + /* schedule DTIM transfer */ + fw.wlan.cab_flush_trigger[vif] = CARL9170_CAB_TRIGGER_ARMED; + } else if ((fw.wlan.cab_queue_len[vif] == 0) && (fw.wlan.cab_flush_trigger[vif])) { + /* undo all chances to the beacon structure */ + ie->bitmap_ctrl &= ~0x1; + fw.wlan.cab_flush_trigger[vif] = CARL9170_CAB_TRIGGER_EMPTY; + } + + /* Triggered by CARL9170_CAB_TRIGGER_ARMED || CARL9170_CAB_TRIGGER_DEFER */ + if (fw.wlan.cab_flush_trigger[vif]) { + /* Set the almighty Multicast Traffic Indication Bit. */ + ie->bitmap_ctrl |= 0x1; + } + } + + /* + * Ideally, the sequence number should be assigned by the TX arbiter + * hardware. But AFAIK that's not possible, so we have to go for the + * next best thing and write it into the beacon fifo during the open + * beacon update window. + */ + + wlan_assign_seq((struct ieee80211_hdr *)addr, vif); +} + +static void wlan_send_buffered_cab(void) +{ + unsigned int i; + + for (i = 0; i < CARL9170_INTF_NUM; i++) { + if (unlikely(fw.wlan.cab_flush_trigger[i] == CARL9170_CAB_TRIGGER_ARMED)) { + /* + * This is hardcoded into carl9170usb driver. + * + * The driver must set the PRETBTT event to beacon_interval - + * CARL9170_PRETBTT_KUS (usually 6) Kus. + * + * But still, we can only do so much about 802.11-2007 9.3.2.1 & + * 11.2.1.6. Let's hope the current solution is adequate enough. + */ + + if (is_after_msecs(fw.wlan.cab_flush_time, (CARL9170_TBTT_DELTA))) { + wlan_cab_flush_queue(i); + + /* + * This prevents the code from sending new BC/MC frames + * which were queued after the previous buffered traffic + * has been sent out... They will have to wait until the + * next DTIM beacon comes along. + */ + fw.wlan.cab_flush_trigger[i] = CARL9170_CAB_TRIGGER_DEFER; + } + } + + } +} +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + +static void handle_beacon_config(void) +{ + uint32_t bcn_count; + + bcn_count = get(AR9170_MAC_REG_BCN_COUNT); + send_cmd_to_host(4, CARL9170_RSP_BEACON_CONFIG, 0x00, + (uint8_t *) &bcn_count); +} + +static void handle_pretbtt(void) +{ +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + fw.wlan.cab_flush_time = get_clock_counter(); +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + rf_psm(); + + send_cmd_to_host(4, CARL9170_RSP_PRETBTT, 0x00, + (uint8_t *) &fw.phy.psm.state); +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ +} + +static void handle_atim(void) +{ + send_cmd_to_host(0, CARL9170_RSP_ATIM, 0x00, NULL); +} + +#ifdef CONFIG_CARL9170FW_DEBUG +static void handle_qos(void) +{ + /* + * What is the QoS Bit used for? + * Is it only an indicator for TXOP & Burst, or + * should we do something here? + */ +} + +static void handle_radar(void) +{ + send_cmd_to_host(0, CARL9170_RSP_RADAR, 0x00, NULL); +} +#endif /* CONFIG_CARL9170FW_DEBUG */ + +static void wlan_janitor(void) +{ +#ifdef CONFIG_CARL9170FW_CAB_QUEUE + wlan_send_buffered_cab(); +#endif /* CONFIG_CARL9170FW_CAB_QUEUE */ + + wlan_send_buffered_tx_status(); + + wlan_send_buffered_ba(); + + wol_janitor(); +} + +void handle_wlan(void) +{ + uint32_t intr; + + intr = get(AR9170_MAC_REG_INT_CTRL); + /* ACK Interrupt */ + set(AR9170_MAC_REG_INT_CTRL, intr); + +#define HANDLER(intr, flag, func) \ + do { \ + if ((intr & flag) != 0) { \ + func(); \ + } \ + } while (0) + + intr |= fw.wlan.soft_int; + fw.wlan.soft_int = 0; + + HANDLER(intr, AR9170_MAC_INT_PRETBTT, handle_pretbtt); + + HANDLER(intr, AR9170_MAC_INT_ATIM, handle_atim); + + HANDLER(intr, AR9170_MAC_INT_RXC, handle_rx); + + HANDLER(intr, (AR9170_MAC_INT_TXC | AR9170_MAC_INT_RETRY_FAIL), + handle_tx_completion); + +#ifdef CONFIG_CARL9170FW_DEBUG + HANDLER(intr, AR9170_MAC_INT_QOS, handle_qos); + + HANDLER(intr, AR9170_MAC_INT_RADAR, handle_radar); +#endif /* CONFIG_CARL9170FW_DEBUG */ + + HANDLER(intr, AR9170_MAC_INT_CFG_BCN, handle_beacon_config); + + if (unlikely(intr)) + DBG("Unhandled Interrupt %x\n", (unsigned int) intr); + + wlan_janitor(); + +#undef HANDLER +} + +enum { + CARL9170FW_TX_MAC_BUMP = 4, + CARL9170FW_TX_MAC_DEBUG = 6, + CARL9170FW_TX_MAC_RESET = 7, +}; + +static void wlan_check_hang(void) +{ + struct dma_desc *desc; + int i; + + for (i = AR9170_TXQ_SPECIAL; i >= AR9170_TXQ0; i--) { + if (queue_empty(&fw.wlan.tx_queue[i])) { + /* Nothing to do here... move along */ + continue; + } + + /* fetch the current DMA queue position */ + desc = (struct dma_desc *)get_wlan_txq_addr(i); + + /* Stuck frame detection */ + if (unlikely(DESC_PAYLOAD(desc) == fw.wlan.last_super[i])) { + fw.wlan.last_super_num[i]++; + + if (unlikely(fw.wlan.last_super_num[i] >= CARL9170FW_TX_MAC_RESET)) { + /* + * schedule MAC reset (aka OFF/ON => dead) + * + * This will almost certainly kill + * the device for good, but it's the + * recommended thing to do... + */ + + fw.wlan.mac_reset++; + } + +#ifdef CONFIG_CARL9170FW_DEBUG + if (unlikely(fw.wlan.last_super_num[i] >= CARL9170FW_TX_MAC_DEBUG)) { + /* + * Sigh, the queue is almost certainly + * dead. Dump the queue content to the + * user, maybe we find out why it got + * so stuck. + */ + + wlan_dump_queue(i); + } +#endif /* CONFIG_CARL9170FW_DEBUG */ + +#ifdef CONFIG_CARL9170FW_DMA_QUEUE_BUMP + if (unlikely(fw.wlan.last_super_num[i] >= CARL9170FW_TX_MAC_BUMP)) { + /* + * Hrrm, bump the queue a bit. + * maybe this will get it going again. + */ + + wlan_dma_bump(i); + wlan_trigger(BIT(i)); + } +#endif /* CONFIG_CARL9170FW_DMA_QUEUE_BUMP */ + } else { + /* Nothing stuck */ + fw.wlan.last_super[i] = DESC_PAYLOAD(desc); + fw.wlan.last_super_num[i] = 0; + } + } +} + +#ifdef CONFIG_CARL9170FW_FW_MAC_RESET +/* + * NB: Resetting the MAC is a two-edged sword. + * On most occasions, it does what it is supposed to do. + * But there is a chance that this will make it + * even worse and the radio dies silently. + */ +static void wlan_mac_reset(void) +{ + uint32_t val; + uint32_t agg_wait_counter; + uint32_t agg_density; + uint32_t bcn_start_addr; + uint32_t rctl, rcth; + uint32_t cam_mode; + uint32_t ack_power; + uint32_t rts_cts_tpc; + uint32_t rts_cts_rate; + int i; + +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + uint32_t rx_BB; +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ + +#ifdef CONFIG_CARL9170FW_NOISY_MAC_RESET + INFO("MAC RESET"); +#endif /* CONFIG_CARL9170FW_NOISY_MAC_RESET */ + + /* Save aggregation parameters */ + agg_wait_counter = get(AR9170_MAC_REG_AMPDU_FACTOR); + agg_density = get(AR9170_MAC_REG_AMPDU_DENSITY); + + bcn_start_addr = get(AR9170_MAC_REG_BCN_ADDR); + + cam_mode = get(AR9170_MAC_REG_CAM_MODE); + rctl = get(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L); + rcth = get(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H); + + ack_power = get(AR9170_MAC_REG_ACK_TPC); + rts_cts_tpc = get(AR9170_MAC_REG_RTS_CTS_TPC); + rts_cts_rate = get(AR9170_MAC_REG_RTS_CTS_RATE); + +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + /* 0x1c8960 write only */ + rx_BB = get(AR9170_PHY_REG_SWITCH_CHAIN_0); +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ + + /* TX/RX must be stopped by now */ + val = get(AR9170_MAC_REG_POWER_STATE_CTRL); + + val |= AR9170_MAC_POWER_STATE_CTRL_RESET; + + /* + * Manipulate CCA threshold to stop transmission + * + * set(AR9170_PHY_REG_CCA_THRESHOLD, 0x300); + */ + + /* + * check Rx state in 0(idle) 9(disable) + * + * chState = (get(AR9170_MAC_REG_MISC_684) >> 16) & 0xf; + * while( (chState != 0) && (chState != 9)) { + * chState = (get(AR9170_MAC_REG_MISC_684) >> 16) & 0xf; + * } + */ + + set(AR9170_MAC_REG_POWER_STATE_CTRL, val); + + delay(2); + + /* Restore aggregation parameters */ + set(AR9170_MAC_REG_AMPDU_FACTOR, agg_wait_counter); + set(AR9170_MAC_REG_AMPDU_DENSITY, agg_density); + + set(AR9170_MAC_REG_BCN_ADDR, bcn_start_addr); + set(AR9170_MAC_REG_CAM_MODE, cam_mode); + set(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_L, rctl); + set(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H, rcth); + + set(AR9170_MAC_REG_RTS_CTS_TPC, rts_cts_tpc); + set(AR9170_MAC_REG_ACK_TPC, ack_power); + set(AR9170_MAC_REG_RTS_CTS_RATE, rts_cts_rate); + +#ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS + set(AR9170_PHY_REG_SWITCH_CHAIN_2, rx_BB); +#endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */ + + /* + * Manipulate CCA threshold to resume transmission + * + * set(AR9170_PHY_REG_CCA_THRESHOLD, 0x0); + */ + + val = AR9170_DMA_TRIGGER_RXQ; + /* Reinitialize all WLAN TX DMA queues. */ + for (i = AR9170_TXQ_SPECIAL; i >= AR9170_TXQ0; i--) { + struct dma_desc *iter; + + __for_each_desc_bits(iter, &fw.wlan.tx_queue[i], AR9170_OWN_BITS_SW); + + /* kill the stuck frame */ + if (!is_terminator(&fw.wlan.tx_queue[i], iter) && + fw.wlan.last_super_num[i] >= CARL9170FW_TX_MAC_RESET && + fw.wlan.last_super[i] == DESC_PAYLOAD(iter)) { + struct carl9170_tx_superframe *super = get_super(iter); + + iter->status = AR9170_OWN_BITS_SW; + /* + * Mark the frame as failed. + * The BAFAIL flag allows the frame to sail through + * wlan_tx_status without much "unstuck" trouble. + */ + iter->ctrl &= ~(AR9170_CTRL_FAIL); + iter->ctrl |= AR9170_CTRL_BAFAIL; + + super->s.cnt = CARL9170_TX_MAX_RATE_TRIES; + super->s.rix = CARL9170_TX_MAX_RETRY_RATES; + + fw.wlan.last_super_num[i] = 0; + fw.wlan.last_super[i] = NULL; + iter = iter->lastAddr->nextAddr; + } + + set_wlan_txq_dma_addr(i, (uint32_t) iter); + if (!is_terminator(&fw.wlan.tx_queue[i], iter)) + val |= BIT(i); + + DBG("Q:%d l:%d h:%p t:%p cu:%p it:%p ct:%x st:%x\n", i, queue_len(&fw.wlan.tx_queue[i]), + fw.wlan.tx_queue[i].head, fw.wlan.tx_queue[i].terminator, + get_wlan_txq_addr(i), iter, iter->ctrl, iter->status); + } + + fw.wlan.soft_int |= AR9170_MAC_INT_RXC | AR9170_MAC_INT_TXC | + AR9170_MAC_INT_RETRY_FAIL; + + set(AR9170_MAC_REG_DMA_RXQ_ADDR, (uint32_t) fw.wlan.rx_queue.head); + wlan_trigger(val); +} +#else +static void wlan_mac_reset(void) +{ + /* The driver takes care of reinitializing the device */ + BUG("MAC RESET"); +} +#endif /* CONFIG_CARL9170FW_FW_MAC_RESET */ + +void __cold wlan_timer(void) +{ + unsigned int cached_mac_reset; + + cached_mac_reset = fw.wlan.mac_reset; + + /* TX Queue Hang check */ + wlan_check_hang(); + + /* RX Overrun check */ + wlan_check_rx_overrun(); + + if (unlikely(fw.wlan.mac_reset >= CARL9170_MAC_RESET_RESET)) { + wlan_mac_reset(); + fw.wlan.mac_reset = CARL9170_MAC_RESET_OFF; + } else { + if (fw.wlan.mac_reset && cached_mac_reset == fw.wlan.mac_reset) + fw.wlan.mac_reset--; + } +} diff --git a/carl9170fw/carlfw/src/wol.c b/carl9170fw/carlfw/src/wol.c new file mode 100644 index 0000000..7afb8e5 --- /dev/null +++ b/carl9170fw/carlfw/src/wol.c @@ -0,0 +1,287 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * WakeUp on WLAN functions + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "shared/phy.h" +#include "timer.h" +#include "wl.h" +#include "printf.h" +#include "rf.h" +#include "wol.h" +#include "linux/ieee80211.h" + +#ifdef CONFIG_CARL9170FW_WOL + +void wol_cmd(const struct carl9170_wol_cmd *cmd) +{ + memcpy(&fw.wol.cmd, cmd, sizeof(cmd)); +} + +void wol_prepare(void) +{ + /* set MAC filter */ + memcpy((void *)AR9170_MAC_REG_MAC_ADDR_L, fw.wol.cmd.mac, 6); + memcpy((void *)AR9170_MAC_REG_BSSID_L, fw.wol.cmd.bssid, 6); + set(AR9170_MAC_REG_RX_CONTROL, AR9170_MAC_RX_CTRL_DEAGG); + + /* set filter policy to: discard everything */ + fw.wlan.rx_filter = CARL9170_RX_FILTER_EVERYTHING; + + /* reenable rx dma */ + wlan_trigger(AR9170_DMA_TRIGGER_RXQ); + + /* initialize the last_beacon timer */ + fw.wol.last_null = fw.wol.last_beacon = get_clock_counter(); +} + +#ifdef CONFIG_CARL9170FW_WOL_NL80211_TRIGGERS +static bool wlan_rx_wol_magic_packet(const struct ieee80211_hdr *hdr, const unsigned int len) +{ + const unsigned char *data, *end, *mac; + unsigned int found = 0; + + /* + * LIMITATION: + * We can only scan the first AR9170_BLOCK_SIZE [=~320] bytes + * for MAGIC patterns! + */ + + mac = (const unsigned char *) AR9170_MAC_REG_MAC_ADDR_L; + + data = (u8 *)((unsigned long)hdr + ieee80211_hdrlen(hdr->frame_control)); + end = (u8 *)((unsigned long)hdr + len); + + /* + * scan for standard WOL Magic frame + * + * "A physical WakeOnLAN (Magic Packet) will look like this: + * --------------------------------------------------------------- + * | Synchronization Stream | Target MAC | Password (optional) | + * | 6 octets | 96 octets | 0, 4 or 6 | + * --------------------------------------------------------------- + * + * The Synchronization Stream is defined as 6 bytes of FFh. + * The Target MAC block contains 16 duplications of the IEEEaddress + * of the target, with no breaks or interruptions. + * + * The Password field is optional, but if present, contains either + * 4 bytes or 6 bytes. The WakeOnLAN dissector was implemented to + * dissect the password, if present, according to the command-line + * format that ether-wake uses, therefore, if a 4-byte password is + * present, it will be dissected as an IPv4 address and if a 6-byte + * password is present, it will be dissected as an Ethernet address. + * + * <http://wiki.wireshark.org/WakeOnLAN> + */ + + while (data < end) { + if (found >= 6) { + if (*data == mac[found % 6]) + found++; + else + found = 0; + } + + /* previous check might reset found counter */ + if (found < 6) { + if (*data == 0xff) + found++; + else + found = 0; + } + + if (found == (6 + 16 * 6)) + return true; + + data++; + } + + return false; +} + +static void wlan_wol_connect_callback(void __unused *dummy, bool success) +{ + if (success) + fw.wol.lost_null = 0; + else + fw.wol.lost_null++; +} + +static void wlan_wol_connection_monitor(void) +{ + struct carl9170_tx_null_superframe *nullf = &dma_mem.reserved.cmd.null; + struct ieee80211_hdr *null = (struct ieee80211_hdr *) &nullf->f.null; + + if (!fw.wlan.fw_desc_available) + return; + + memset(nullf, 0, sizeof(*nullf)); + + nullf->s.len = sizeof(struct carl9170_tx_superdesc) + + sizeof(struct ar9170_tx_hwdesc) + + sizeof(struct ieee80211_hdr); + nullf->s.ri[0].tries = 3; + nullf->s.assign_seq = true; + nullf->s.queue = AR9170_TXQ_VO; + nullf->f.hdr.length = sizeof(struct ieee80211_hdr) + FCS_LEN; + + nullf->f.hdr.mac.backoff = 1; + nullf->f.hdr.mac.hw_duration = 1; + nullf->f.hdr.mac.erp_prot = AR9170_TX_MAC_PROT_RTS; + + nullf->f.hdr.phy.modulation = AR9170_TX_PHY_MOD_OFDM; + nullf->f.hdr.phy.bandwidth = AR9170_TX_PHY_BW_20MHZ; + nullf->f.hdr.phy.chains = AR9170_TX_PHY_TXCHAIN_2; + nullf->f.hdr.phy.tx_power = 29; /* 14.5 dBm */ + nullf->f.hdr.phy.mcs = AR9170_TXRX_PHY_RATE_OFDM_6M; + + /* format outgoing nullfunc */ + null->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC | IEEE80211_FCTL_TODS); + + memcpy(null->addr1, fw.wol.cmd.bssid, 6); + memcpy(null->addr2, fw.wol.cmd.mac, 6); + memcpy(null->addr3, fw.wol.cmd.bssid, 6); + + wlan_tx_fw(&nullf->s, wlan_wol_connect_callback); +} + +static bool wlan_rx_wol_disconnect(const unsigned int rx_filter, + const struct ieee80211_hdr *hdr, + const unsigned int __unused len) +{ + const unsigned char *bssid; + bssid = (const unsigned char *) AR9170_MAC_REG_BSSID_L; + + /* should catch both broadcast and unicast MLMEs */ + if (!(rx_filter & CARL9170_RX_FILTER_OTHER_RA)) { + if (ieee80211_is_deauth(hdr->frame_control) || + ieee80211_is_disassoc(hdr->frame_control)) + return true; + } + + if (ieee80211_is_beacon(hdr->frame_control) && + compare_ether_address(hdr->addr3, bssid)) { + fw.wol.last_beacon = get_clock_counter(); + } + + return false; +} + +#endif /* CARL9170FW_WOL_NL80211_TRIGGERS */ + +#ifdef CONFIG_CARL9170FW_WOL_PROBE_REQUEST + +/* + * Note: CONFIG_CARL9170FW_WOL_PROBE_REQUEST_SSID is not a real + * string. We have to be careful not to add a \0 at the end. + */ +static const struct { + u8 ssid_ie; + u8 ssid_len; + u8 ssid[sizeof(CONFIG_CARL9170FW_WOL_PROBE_REQUEST_SSID) - 1]; +} __packed probe_req = { + .ssid_ie = WLAN_EID_SSID, + .ssid_len = sizeof(CONFIG_CARL9170FW_WOL_PROBE_REQUEST_SSID) - 1, + .ssid = CONFIG_CARL9170FW_WOL_PROBE_REQUEST_SSID, +}; + +static bool wlan_rx_wol_probe_ssid(const struct ieee80211_hdr *hdr, const unsigned int len) +{ + const unsigned char *data, *end, *scan = (void *) &probe_req; + + /* + * IEEE 802.11-2007 7.3.2.1 specifies that the SSID is no + * longer than 32 octets. + */ + BUILD_BUG_ON((sizeof(CONFIG_CARL9170FW_WOL_PROBE_REQUEST_SSID) - 1) > 32); + + if (ieee80211_is_probe_req(hdr->frame_control)) { + unsigned int i; + end = (u8 *)((unsigned long)hdr + len); + + /* + * The position of the SSID information element inside + * a probe request frame is more or less "fixed". + */ + data = (u8 *)((struct ieee80211_mgmt *)hdr)->u.probe_req.variable; + for (i = 0; i < (unsigned int)(probe_req.ssid_len + 1); i++) { + if (data > end || scan[i] != data[i]) + return false; + } + + return true; + } + + return false; +} +#endif /* CONFIG_CARL9170FW_WOL_PROBE_REQUEST */ + +void wol_rx(const unsigned int rx_filter __unused, const struct ieee80211_hdr *hdr __unused, const unsigned int len __unused) +{ +#ifdef CONFIG_CARL9170FW_WOL_NL80211_TRIGGERS + /* Disconnect is always enabled */ + if (fw.wol.cmd.flags & CARL9170_WOL_DISCONNECT && + rx_filter & CARL9170_RX_FILTER_MGMT) + fw.wol.wake_up |= wlan_rx_wol_disconnect(rx_filter, hdr, len); + + if (fw.wol.cmd.flags & CARL9170_WOL_MAGIC_PKT && + rx_filter & CARL9170_RX_FILTER_DATA) + fw.wol.wake_up |= wlan_rx_wol_magic_packet(hdr, len); +#endif /* CONFIG_CARL9170FW_WOL_NL80211_TRIGGERS */ + +#ifdef CONFIG_CARL9170FW_WOL_PROBE_REQUEST + if (rx_filter & CARL9170_RX_FILTER_MGMT) + fw.wol.wake_up |= wlan_rx_wol_probe_ssid(hdr, len); +#endif /* CONFIG_CARL9170FW_WOL_PROBE_REQUEST */ +} + +void wol_janitor(void) +{ + if (unlikely(fw.suspend_mode == CARL9170_HOST_SUSPENDED)) { +#ifdef CONFIG_CARL9170FW_WOL_NL80211_TRIGGERS + if (fw.wol.cmd.flags & CARL9170_WOL_DISCONNECT) { + /* + * connection lost after 10sec without receiving + * a beacon + */ + if (is_after_msecs(fw.wol.last_beacon, 10000)) + fw.wol.wake_up |= true; + + if (fw.wol.cmd.null_interval && + is_after_msecs(fw.wol.last_null, fw.wol.cmd.null_interval)) + wlan_wol_connection_monitor(); + + if (fw.wol.lost_null >= 5) + fw.wol.wake_up |= true; + } +#endif /* CONFIG_CARL9170FW_WOL_NL80211_TRIGGERS */ + + if (fw.wol.wake_up) { + fw.suspend_mode = CARL9170_AWAKE_HOST; + set(AR9170_USB_REG_WAKE_UP, AR9170_USB_WAKE_UP_WAKE); + } + } +} +#else + +#endif /* CONFIG_CARL9170FW_WOL */ diff --git a/carl9170fw/carlfw/usb/Kconfig b/carl9170fw/carlfw/usb/Kconfig new file mode 100644 index 0000000..dc70776 --- /dev/null +++ b/carl9170fw/carlfw/usb/Kconfig @@ -0,0 +1,37 @@ +menu "USB Firmware Configuration Settings" + +config CARL9170FW_USB_STANDARD_CMDS + def_bool y + prompt "Basic USB Interface" + ---help--- + Allows the device to be queried about Standard USB 2.0 Device + Description Descriptors. + + Say Y, unless you don't care if lsusb -v fails. + +config CARL9170FW_USB_UP_STREAM + def_bool y + prompt "USB Upload Stream" + ---help--- + This features allows the USB silicon to combine small, single + frames into bigger transfers. This can help to reduce + some per-transfer overhead in the application. + + Say Y, unless you have experienced strange rx corruptions. + +config CARL9170FW_USB_DN_STREAM + def_bool n + prompt "USB Download Stream" + +config CARL9170FW_DEBUG_USB + def_bool y + prompt "Pass debug messages through USB transport" + ---help--- + Report all firmware messages through the USB transport. + But there is a catch: In case of a BUG, the USB transport + needs to be functional, otherwise the application won't + receive anything. + + Say Y. + +endmenu diff --git a/carl9170fw/carlfw/usb/fifo.c b/carl9170fw/carlfw/usb/fifo.c new file mode 100644 index 0000000..d5c2e12 --- /dev/null +++ b/carl9170fw/carlfw/usb/fifo.c @@ -0,0 +1,206 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" +#include "printf.h" +#include "rom.h" +#include "usb_fifo.h" + +/* TODO / TOTEST */ +#ifdef CONFIG_CARL9170FW_USB_MODESWITCH +static inline void usb_ep_map(const uint8_t ep, const uint8_t map) +{ + setb(AR9170_USB_REG_EP_MAP + (ep - 1), map); +} + +static inline void usb_fifo_map(const uint8_t fifo, const uint8_t map) +{ + setb(AR9170_USB_REG_FIFO_MAP + (fifo - 1), map); +} + +static inline void usb_fifo_config(const uint8_t fifo, const uint8_t cfg) +{ + setb(AR9170_USB_REG_FIFO_CONFIG + (fifo - 1), cfg); +} + +static inline void usb_ep_packet_size_hi(const uint8_t ep, const uint8_t dir, + const uint16_t size) +{ + setb(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (((dir * 0x20) + ep) << 1), + (size >> 8) & 0xf); +} + +static inline void usb_ep_packet_size_lo(const uint8_t ep, const uint8_t dir, + const uint16_t size) +{ + setb(AR9170_USB_REG_EP_IN_MAX_SIZE_LOW + (((dir * 0x20) + ep) << 1), + size & 0xff); +} + +static void usb_ep_in_highbandset(const uint8_t ep, const uint8_t dir, + const uint16_t size) +{ + andb(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (ep << 1), ~(BIT(6) | BIT(5))); + + switch (dir) { + case DIRECTION_IN: + setb(AR9170_USB_REG_EP_IN_MAX_SIZE_HIGH + (ep << 1), + ((size >> 11) + 1) << 5); + break; + case DIRECTION_OUT: + default: + break; + } +} + +/* + * vUsbFIFO_EPxCfg_HS(void) + * Description: + * 1. Configure the FIFO and EPx map + * input: none + * output: none + */ + +void usb_init_highspeed_fifo_cfg(void) +{ + int i; + + /* EP 1 */ + usb_ep_map(1, HS_C1_I0_A0_EP1_MAP); + usb_fifo_map(HS_C1_I0_A0_EP1_FIFO_START, HS_C1_I0_A0_EP1_FIFO_MAP); + usb_fifo_config(HS_C1_I0_A0_EP1_FIFO_START, HS_C1_I0_A0_EP1_FIFO_CONFIG); + + for (i = HS_C1_I0_A0_EP1_FIFO_START + 1; + i < HS_C1_I0_A0_EP1_FIFO_START + HS_C1_I0_A0_EP1_FIFO_NO; i++) { + usb_fifo_config(i, (HS_C1_I0_A0_EP1_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(1, HS_C1_I0_A0_EP1_DIRECTION, (HS_C1_I0_A0_EP1_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(1, HS_C1_I0_A0_EP1_DIRECTION, (HS_C1_I0_A0_EP1_MAX_PACKET & 0x7ff)); + usb_ep_in_highbandset(1, HS_C1_I0_A0_EP1_DIRECTION, HS_C1_I0_A0_EP1_MAX_PACKET); + + /* EP 2 */ + usb_ep_map(2, HS_C1_I0_A0_EP2_MAP); + usb_fifo_map(HS_C1_I0_A0_EP2_FIFO_START, HS_C1_I0_A0_EP2_FIFO_MAP); + usb_fifo_config(HS_C1_I0_A0_EP2_FIFO_START, HS_C1_I0_A0_EP2_FIFO_CONFIG); + + for (i = HS_C1_I0_A0_EP2_FIFO_START + 1; + i < HS_C1_I0_A0_EP2_FIFO_START + HS_C1_I0_A0_EP2_FIFO_NO; i++) { + usb_fifo_config(i, (HS_C1_I0_A0_EP2_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(2, HS_C1_I0_A0_EP2_DIRECTION, (HS_C1_I0_A0_EP2_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(2, HS_C1_I0_A0_EP2_DIRECTION, (HS_C1_I0_A0_EP2_MAX_PACKET & 0x7ff)); + usb_ep_in_highbandset(2, HS_C1_I0_A0_EP2_DIRECTION, HS_C1_I0_A0_EP2_MAX_PACKET); + + /* EP 3 */ + usb_ep_map(3, HS_C1_I0_A0_EP3_MAP); + usb_fifo_map(HS_C1_I0_A0_EP3_FIFO_START, HS_C1_I0_A0_EP3_FIFO_MAP); + usb_fifo_config(HS_C1_I0_A0_EP3_FIFO_START, HS_C1_I0_A0_EP3_FIFO_CONFIG); + + for (i = HS_C1_I0_A0_EP3_FIFO_START + 1; + i < HS_C1_I0_A0_EP3_FIFO_START + HS_C1_I0_A0_EP3_FIFO_NO; i++) { + usb_fifo_config(i, (HS_C1_I0_A0_EP3_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(3, HS_C1_I0_A0_EP3_DIRECTION, (HS_C1_I0_A0_EP3_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(3, HS_C1_I0_A0_EP3_DIRECTION, (HS_C1_I0_A0_EP3_MAX_PACKET & 0x7ff)); + usb_ep_in_highbandset(3, HS_C1_I0_A0_EP3_DIRECTION, HS_C1_I0_A0_EP3_MAX_PACKET); + + /* EP 4 */ + usb_ep_map(4, HS_C1_I0_A0_EP4_MAP); + usb_fifo_map(HS_C1_I0_A0_EP4_FIFO_START, HS_C1_I0_A0_EP4_FIFO_MAP); + usb_fifo_config(HS_C1_I0_A0_EP4_FIFO_START, HS_C1_I0_A0_EP4_FIFO_CONFIG); + + for (i = HS_C1_I0_A0_EP4_FIFO_START + 1; + i < HS_C1_I0_A0_EP4_FIFO_START + HS_C1_I0_A0_EP4_FIFO_NO; i++) { + usb_fifo_config(i, (HS_C1_I0_A0_EP4_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(4, HS_C1_I0_A0_EP4_DIRECTION, (HS_C1_I0_A0_EP4_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(4, HS_C1_I0_A0_EP4_DIRECTION, (HS_C1_I0_A0_EP4_MAX_PACKET & 0x7ff)); + usb_ep_in_highbandset(4, HS_C1_I0_A0_EP4_DIRECTION, HS_C1_I0_A0_EP4_MAX_PACKET); +} + +void usb_init_fullspeed_fifo_cfg(void) +{ + int i; + + /* EP 1 */ + usb_ep_map(1, FS_C1_I0_A0_EP1_MAP); + usb_fifo_map(FS_C1_I0_A0_EP1_FIFO_START, FS_C1_I0_A0_EP1_FIFO_MAP); + usb_fifo_config(FS_C1_I0_A0_EP1_FIFO_START, FS_C1_I0_A0_EP1_FIFO_CONFIG); + + for (i = FS_C1_I0_A0_EP1_FIFO_START + 1; + i < FS_C1_I0_A0_EP1_FIFO_START + FS_C1_I0_A0_EP1_FIFO_NO; i++) { + usb_fifo_config(i, (FS_C1_I0_A0_EP1_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(1, FS_C1_I0_A0_EP1_DIRECTION, (FS_C1_I0_A0_EP1_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(1, FS_C1_I0_A0_EP1_DIRECTION, (FS_C1_I0_A0_EP1_MAX_PACKET & 0x7ff)); + /* ``.JWEI 2003/04/29 */ + usb_ep_in_highbandset(1, FS_C1_I0_A0_EP1_DIRECTION, FS_C1_I0_A0_EP1_MAX_PACKET); + + /* EP 2 */ + usb_ep_map(2, FS_C1_I0_A0_EP2_MAP); + usb_fifo_map(FS_C1_I0_A0_EP2_FIFO_START, FS_C1_I0_A0_EP2_FIFO_MAP); + usb_fifo_config(FS_C1_I0_A0_EP2_FIFO_START, FS_C1_I0_A0_EP2_FIFO_CONFIG); + + for (i = FS_C1_I0_A0_EP2_FIFO_START + 1; + i < FS_C1_I0_A0_EP2_FIFO_START + FS_C1_I0_A0_EP2_FIFO_NO; i++) { + usb_fifo_config(i, (FS_C1_I0_A0_EP2_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(2, FS_C1_I0_A0_EP2_DIRECTION, (FS_C1_I0_A0_EP2_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(2, FS_C1_I0_A0_EP2_DIRECTION, (FS_C1_I0_A0_EP2_MAX_PACKET & 0x7ff)); + usb_ep_in_highbandset(2, FS_C1_I0_A0_EP2_DIRECTION, FS_C1_I0_A0_EP2_MAX_PACKET); + + /* EP 3 */ + usb_ep_map(3, FS_C1_I0_A0_EP3_MAP); + usb_fifo_map(FS_C1_I0_A0_EP3_FIFO_START, FS_C1_I0_A0_EP3_FIFO_MAP); + usb_fifo_config(FS_C1_I0_A0_EP3_FIFO_START, FS_C1_I0_A0_EP3_FIFO_CONFIG); + + for (i = FS_C1_I0_A0_EP3_FIFO_START + 1; + i < FS_C1_I0_A0_EP3_FIFO_START + FS_C1_I0_A0_EP3_FIFO_NO; i++) { + usb_fifo_config(i, (FS_C1_I0_A0_EP3_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(3, FS_C1_I0_A0_EP3_DIRECTION, (FS_C1_I0_A0_EP3_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(3, FS_C1_I0_A0_EP3_DIRECTION, (FS_C1_I0_A0_EP3_MAX_PACKET & 0x7ff)); + usb_ep_in_highbandset(3, FS_C1_I0_A0_EP3_DIRECTION, FS_C1_I0_A0_EP3_MAX_PACKET); + + /* EP 4 */ + usb_ep_map(4, FS_C1_I0_A0_EP4_MAP); + usb_fifo_map(FS_C1_I0_A0_EP4_FIFO_START, FS_C1_I0_A0_EP4_FIFO_MAP); + usb_fifo_config(FS_C1_I0_A0_EP4_FIFO_START, FS_C1_I0_A0_EP4_FIFO_CONFIG); + + for (i = FS_C1_I0_A0_EP4_FIFO_START + 1; + i < FS_C1_I0_A0_EP4_FIFO_START + FS_C1_I0_A0_EP4_FIFO_NO; i++) { + usb_fifo_config(i, (FS_C1_I0_A0_EP4_FIFO_CONFIG & (~BIT(7)))); + } + + usb_ep_packet_size_hi(4, FS_C1_I0_A0_EP4_DIRECTION, (FS_C1_I0_A0_EP4_MAX_PACKET & 0x7ff)); + usb_ep_packet_size_lo(4, FS_C1_I0_A0_EP4_DIRECTION, (FS_C1_I0_A0_EP4_MAX_PACKET & 0x7ff)); + usb_ep_in_highbandset(4, FS_C1_I0_A0_EP4_DIRECTION, FS_C1_I0_A0_EP4_MAX_PACKET); +} +#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */ diff --git a/carl9170fw/carlfw/usb/main.c b/carl9170fw/carlfw/usb/main.c new file mode 100644 index 0000000..890970c --- /dev/null +++ b/carl9170fw/carlfw/usb/main.c @@ -0,0 +1,432 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "carl9170.h" + +#include "shared/phy.h" +#include "hostif.h" +#include "printf.h" +#include "timer.h" +#include "rom.h" +#include "wl.h" +#include "wol.h" + +#ifdef CONFIG_CARL9170FW_DEBUG_USB +void usb_putc(const char c) +{ + fw.usb.put_buffer[fw.usb.put_index++] = (uint8_t) c; + + if (fw.usb.put_index == CARL9170_MAX_CMD_PAYLOAD_LEN || c == '\0') { + fw.usb.put_buffer[fw.usb.put_index] = 0; + + send_cmd_to_host(__roundup(fw.usb.put_index, 4), + CARL9170_RSP_TEXT, fw.usb.put_index, + fw.usb.put_buffer); + fw.usb.put_index = 0; + } +} + +void usb_print_hex_dump(const void *buf, int len) +{ + unsigned int offset = 0, block = 0; + while (len > 0) { + block = min(__roundup(len, 4), CARL9170_MAX_CMD_PAYLOAD_LEN); + + send_cmd_to_host(block, CARL9170_RSP_HEXDUMP, len, + (const uint8_t *) buf + offset); + + offset += block; + len -= block; + } +} +#endif /* CONFIG_CARL9170FW_DEBUG_USB */ + +/* grab a buffer from the interrupt in queue ring-buffer */ +static struct carl9170_rsp *get_int_buf(void) +{ + struct carl9170_rsp *tmp; + + /* fetch the _oldest_ buffer from the ring */ + tmp = &fw.usb.int_buf[fw.usb.int_tail_index]; + + /* assign a unique sequence for every response/trap */ + tmp->hdr.seq = fw.usb.int_tail_index; + + fw.usb.int_tail_index++; + + fw.usb.int_tail_index %= CARL9170_INT_RQ_CACHES; + if (fw.usb.int_pending != CARL9170_INT_RQ_CACHES) + fw.usb.int_pending++; + + return tmp; +} + +/* Pop up data from Interrupt IN Queue to USB Response buffer */ +static struct carl9170_rsp *dequeue_int_buf(unsigned int space) +{ + struct carl9170_rsp *tmp = NULL; + + if (fw.usb.int_pending > 0) { + tmp = &fw.usb.int_buf[fw.usb.int_head_index]; + + if ((unsigned int)(tmp->hdr.len + 8) > space) + return NULL; + + fw.usb.int_head_index++; + fw.usb.int_head_index %= CARL9170_INT_RQ_CACHES; + fw.usb.int_pending--; + } + + return tmp; +} + +static void usb_data_in(void) +{ +} + +static void usb_reg_out(void) +{ + uint32_t *regaddr = (uint32_t *) &dma_mem.reserved.cmd; + uint16_t usbfifolen, i; + + usb_reset_out(); + + usbfifolen = getb(AR9170_USB_REG_EP4_BYTE_COUNT_LOW) | + getb(AR9170_USB_REG_EP4_BYTE_COUNT_HIGH) << 8; + + if (usbfifolen & 0x3) + usbfifolen = (usbfifolen >> 2) + 1; + else + usbfifolen = usbfifolen >> 2; + + for (i = 0; i < usbfifolen; i++) + *regaddr++ = get(AR9170_USB_REG_EP4_DATA); + + handle_cmd(get_int_buf()); + + usb_trigger_in(); +} + +static void usb_status_in(void) +{ + struct carl9170_rsp *rsp; + unsigned int rem, tlen, elen; + + if (!fw.usb.int_desc_available) + return ; + + fw.usb.int_desc_available = 0; + + rem = AR9170_BLOCK_SIZE - AR9170_INT_MAGIC_HEADER_SIZE; + tlen = AR9170_INT_MAGIC_HEADER_SIZE; + + usb_reset_in(); + + while (fw.usb.int_pending) { + rsp = dequeue_int_buf(rem); + if (!rsp) + break; + + elen = rsp->hdr.len + 4; + + memcpy(DESC_PAYLOAD_OFF(fw.usb.int_desc, tlen), rsp, elen); + + rem -= elen; + tlen += elen; + } + + if (tlen == AR9170_INT_MAGIC_HEADER_SIZE) { + DBG("attempted to send an empty int response!\n"); + goto reclaim; + } + + fw.usb.int_desc->ctrl = AR9170_CTRL_FS_BIT | AR9170_CTRL_LS_BIT; + fw.usb.int_desc->totalLen = tlen; + fw.usb.int_desc->dataSize = tlen; + + /* Put to UpQ */ + dma_put(&fw.pta.up_queue, fw.usb.int_desc); + + /* Trigger PTA UP DMA */ + set(AR9170_PTA_REG_UP_DMA_TRIGGER, 1); + usb_trigger_out(); + + return ; + +reclaim: + /* TODO: not sure what to do here */ + fw.usb.int_desc_available = 1; +} + +void send_cmd_to_host(const uint8_t len, const uint8_t type, + const uint8_t ext, const uint8_t *body) +{ + struct carl9170_cmd *resp; + +#ifdef CONFIG_CARL9170FW_DEBUG + if (unlikely(len > sizeof(resp->data))) { + DBG("CMD too long:%x %d\n", type, len); + return ; + } + + /* Element length must be a multiple of 4. */ + if (unlikely(len & 0x3)) { + DBG("CMD length not mult. of 4:%x %d\n", type, len); + return ; + } +#endif /* CONFIG_CARL9170FW_DEBUG */ + + resp = (struct carl9170_cmd *) get_int_buf(); + if (unlikely(resp == NULL)) { + /* not very helpful for NON UART users */ + DBG("out of msg buffers\n"); + return ; + } + + resp->hdr.len = len; + resp->hdr.cmd = type; + resp->hdr.ext = ext; + + memcpy(resp->data, body, len); + usb_trigger_in(); +} + +/* Turn off ADDA/RF power, PLL */ +static void turn_power_off(void) +{ + set(AR9170_PHY_REG_ACTIVE, AR9170_PHY_ACTIVE_DIS); + set(AR9170_PHY_REG_ADC_CTL, 0xa0000000 | + AR9170_PHY_ADC_CTL_OFF_PWDADC | AR9170_PHY_ADC_CTL_OFF_PWDDAC); + + /* This will also turn-off the LEDs */ + set(AR9170_GPIO_REG_PORT_DATA, 0); + set(AR9170_GPIO_REG_PORT_TYPE, 0xf); + + set(AR9170_PWR_REG_BASE, 0x40021); + + set(AR9170_MAC_REG_DMA_TRIGGER, 0); + + andl(AR9170_USB_REG_DMA_CTL, ~(AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE | + AR9170_USB_DMA_CTL_ENABLE_FROM_DEVICE | + AR9170_USB_DMA_CTL_UP_PACKET_MODE | + AR9170_USB_DMA_CTL_DOWN_STREAM)); + + /* Do a software reset to PTA component */ + orl(AR9170_PTA_REG_DMA_MODE_CTRL, AR9170_PTA_DMA_MODE_CTRL_RESET); + andl(AR9170_PTA_REG_DMA_MODE_CTRL, ~AR9170_PTA_DMA_MODE_CTRL_RESET); + + orl(AR9170_PTA_REG_DMA_MODE_CTRL, AR9170_PTA_DMA_MODE_CTRL_DISABLE_USB); + + set(AR9170_MAC_REG_POWER_STATE_CTRL, + AR9170_MAC_POWER_STATE_CTRL_RESET); + + /* Reset USB FIFO */ + set(AR9170_PWR_REG_RESET, AR9170_PWR_RESET_COMMIT_RESET_MASK | + AR9170_PWR_RESET_DMA_MASK | + AR9170_PWR_RESET_WLAN_MASK); + set(AR9170_PWR_REG_RESET, 0x0); + + clock_set(AHB_20_22MHZ, false); + + set(AR9170_PWR_REG_PLL_ADDAC, 0x5163); /* 0x502b; */ + set(AR9170_PHY_REG_ADC_SERIAL_CTL, AR9170_PHY_ADC_SCTL_SEL_EXTERNAL_RADIO); + set(0x1c589c, 0); /* 7-0 */ + set(0x1c589c, 0); /* 15-8 */ + set(0x1c589c, 0); /* 23-16 */ + set(0x1c589c, 0); /* 31- */ + set(0x1c589c, 0); /* 39- */ + set(0x1c589c, 0); /* 47- */ + set(0x1c589c, 0); /* 55- */ + set(0x1c589c, 0xf8); /* 63- */ + set(0x1c589c, 0x27); /* 0x24; 71- modified */ + set(0x1c589c, 0xf9); /* 79- */ + set(0x1c589c, 0x90); /* 87- */ + set(0x1c589c, 0x04); /* 95- */ + set(0x1c589c, 0x48); /* 103- */ + set(0x1c589c, 0x19); /* 0; 111- modified */ + set(0x1c589c, 0); /* 119- */ + set(0x1c589c, 0); /* 127- */ + set(0x1c589c, 0); /* 135- */ + set(0x1c589c, 0); /* 143- */ + set(0x1c589c, 0); /* 151- */ + set(0x1c589c, 0x70); /* 159- */ + set(0x1c589c, 0x0c); /* 167- */ + set(0x1c589c, 0); /* 175- */ + set(0x1c589c, 0); /* 183-176 */ + set(0x1c589c, 0); /* 191-184 */ + set(0x1c589c, 0); /* 199- */ + set(0x1c589c, 0); /* 207- */ + set(0x1c589c, 0); /* 215- */ + set(0x1c589c, 0); /* 223- */ + set(0x1c589c, 0); /* 231- */ + set(0x1c58c4, 0); /* 233- 232 */ + set(AR9170_PHY_REG_ADC_SERIAL_CTL, AR9170_PHY_ADC_SCTL_SEL_INTERNAL_ADDAC); +} + +static void disable_watchdog(void) +{ + if (!fw.watchdog_enable) + return; + + /* write watchdog magic pattern for suspend */ + andl(AR9170_PWR_REG_WATCH_DOG_MAGIC, 0xffff); + orl(AR9170_PWR_REG_WATCH_DOG_MAGIC, 0x98760000); + + /* Disable watchdog */ + set(AR9170_TIMER_REG_WATCH_DOG, 0xffff); +} + +void __noreturn reboot(void) +{ + disable_watchdog(); + + /* Turn off power */ + turn_power_off(); + + /* clean bootloader workspace */ + memset(&dma_mem, 0, sizeof(dma_mem)); + + /* add by ygwei for work around USB PHY chirp sequence problem */ + set(0x10f100, 0x12345678); + + /* Jump to boot code */ + jump_to_bootcode(); +} + +/* service USB events and re-enable USB interrupt */ +static void usb_handler(uint8_t usb_interrupt_level1) +{ + uint8_t usb_interrupt_level2; + + if (usb_interrupt_level1 & BIT(5)) + usb_data_in(); + + if (usb_interrupt_level1 & BIT(4)) + usb_reg_out(); + + if (usb_interrupt_level1 & BIT(6)) + usb_status_in(); + + if (usb_interrupt_level1 & BIT(0)) { + usb_interrupt_level2 = getb(AR9170_USB_REG_INTR_SOURCE_0); + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC0_SETUP) + usb_ep0setup(); + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC0_IN) + usb_ep0tx(); + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC0_OUT) + usb_ep0rx(); + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC0_ABORT) { + /* Clear the command abort interrupt */ + andb(AR9170_USB_REG_INTR_SOURCE_0, (uint8_t) + ~AR9170_USB_INTR_SRC0_ABORT); + } + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC0_FAIL || + fw.usb.ep0_action & CARL9170_EP0_STALL) { + /* + * transmission failure. + * stall ep 0 + */ + setb(AR9170_USB_REG_CX_CONFIG_STATUS, BIT(2)); + fw.usb.ep0_action &= ~CARL9170_EP0_STALL; + } + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC0_END || + fw.usb.ep0_action & CARL9170_EP0_TRIGGER) { + /* + * transmission done. + * set DONE bit. + */ + setb(AR9170_USB_REG_CX_CONFIG_STATUS, BIT(0)); + fw.usb.ep0_action &= ~CARL9170_EP0_TRIGGER; + } + } + + if (usb_interrupt_level1 & BIT(7)) { + usb_interrupt_level2 = getb(AR9170_USB_REG_INTR_SOURCE_7); + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC7_RX0BYTE) + usb_data_out0Byte(); + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC7_TX0BYTE) + usb_data_in0Byte(); + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC7_USB_RESET) { + usb_reset_ack(); + reboot(); + } + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC7_USB_SUSPEND) { + usb_suspend_ack(); + + fw.suspend_mode = CARL9170_HOST_SUSPENDED; + +#ifdef CONFIG_CARL9170FW_WOL + if (!(fw.usb.device_feature & USB_DEVICE_REMOTE_WAKEUP) || + !fw.wol.cmd.flags) { + disable_watchdog(); + + /* GO_TO_SUSPEND stops the CPU clock too. */ + orb(AR9170_USB_REG_MAIN_CTRL, AR9170_USB_MAIN_CTRL_GO_TO_SUSPEND); + } else { + wol_prepare(); + } +#else /* CONFIG_CARL9170FW_WOL */ + disable_watchdog(); + + /* GO_TO_SUSPEND stops the CPU clock too. */ + orb(AR9170_USB_REG_MAIN_CTRL, AR9170_USB_MAIN_CTRL_GO_TO_SUSPEND); +#endif /* CONFIG_CARL9170FW_WOL */ + } + + if (usb_interrupt_level2 & AR9170_USB_INTR_SRC7_USB_RESUME) { + usb_resume_ack(); + + fw.suspend_mode = CARL9170_HOST_AWAKE; + set(AR9170_USB_REG_WAKE_UP, 0); + + reboot(); + } + } +} + +void handle_usb(void) +{ + uint8_t usb_interrupt_level1; + + usb_interrupt_level1 = getb(AR9170_USB_REG_INTR_GROUP); + + if (usb_interrupt_level1) + usb_handler(usb_interrupt_level1); + + if (fw.usb.int_pending > 0) + usb_trigger_in(); +} + +void usb_timer(void) +{ +} diff --git a/carl9170fw/carlfw/usb/usb.c b/carl9170fw/carlfw/usb/usb.c new file mode 100644 index 0000000..74b8f8c --- /dev/null +++ b/carl9170fw/carlfw/usb/usb.c @@ -0,0 +1,758 @@ +/* + * carl9170 firmware - used by the ar9170 wireless device + * + * USB Controller + * + * Copyright (c) 2000-2005 ZyDAS Technology Corporation + * Copyright (c) 2007-2009 Atheros Communications, Inc. + * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2009-2011 Christian Lamparter <chunkeey@googlemail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "carl9170.h" +#include "usb.h" +#include "printf.h" +#include "rom.h" + +/* + * NB: The firmware has to write into these structures + * so don't try to make them "const". + */ + +static struct ar9170_usb_config usb_config_highspeed = { + .cfg = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = cpu_to_le16(sizeof(usb_config_highspeed)), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = USB_CONFIG_ATT_ONE | +#ifdef CONFIG_CARL9170FW_WOL + USB_CONFIG_ATT_WAKEUP | +#endif /* CONFIG_CARL9170FW_WOL */ + 0, + .bMaxPower = 0xfa, /* 500 mA */ + }, + + .intf = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = AR9170_USB_NUM_EXTRA_EP, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0, + .iInterface = 0, + }, + + .ep = { + { /* EP 1 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT | AR9170_USB_EP_TX, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + .bInterval = 0, + }, + + { /* EP 2 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN | AR9170_USB_EP_RX, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + .bInterval = 0, + }, + + { /* EP 3 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN | AR9170_USB_EP_IRQ, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 1, + }, + + { /* EP 4 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT | AR9170_USB_EP_CMD, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 1, + }, + }, +}; + +static struct ar9170_usb_config usb_config_fullspeed = { + .cfg = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = cpu_to_le16(sizeof(usb_config_fullspeed)), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = USB_CONFIG_ATT_ONE | +#ifdef CONFIG_CARL9170FW_WOL + USB_CONFIG_ATT_WAKEUP | +#endif /* CONFIG_CARL9170FW_WOL */ + 0, + .bMaxPower = 0xfa, /* 500 mA */ + }, + + .intf = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = AR9170_USB_NUM_EXTRA_EP, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, + .bInterfaceProtocol = 0, + .iInterface = 0, + }, + + .ep = { + { /* EP 1 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT | AR9170_USB_EP_TX, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 0, + }, + + { /* EP 2 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN | AR9170_USB_EP_RX, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 0, + }, + + { /* EP 3 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN | AR9170_USB_EP_IRQ, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 1, + }, + + { /* EP 4 */ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT | AR9170_USB_EP_CMD, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 1, + }, + }, +}; + +#ifdef CONFIG_CARL9170FW_USB_MODESWITCH +static void usb_reset_eps(void) +{ + unsigned int i; + + /* clear all EPs' toggle bit */ + for (i = 1; i < __AR9170_USB_NUM_MAX_EP; i++) { + usb_set_input_ep_toggle(i); + usb_clear_input_ep_toggle(i); + } + + /* + * NB: I've no idea why this cannot be integrated into the + * previous loop? + */ + for (i = 1; i < __AR9170_USB_NUM_MAX_EP; i++) { + usb_set_output_ep_toggle(i); + usb_clear_output_ep_toggle(i); + } +} +#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */ + + +static void usb_pta_init(void) +{ + unsigned int usb_dma_ctrl = 0; + /* Set PTA mode to USB */ + andl(AR9170_PTA_REG_DMA_MODE_CTRL, + ~AR9170_PTA_DMA_MODE_CTRL_DISABLE_USB); + + /* Do a software reset to PTA component */ + orl(AR9170_PTA_REG_DMA_MODE_CTRL, AR9170_PTA_DMA_MODE_CTRL_RESET); + andl(AR9170_PTA_REG_DMA_MODE_CTRL, ~AR9170_PTA_DMA_MODE_CTRL_RESET); + + if (usb_detect_highspeed()) { + fw.usb.os_cfg_desc = &usb_config_fullspeed; + fw.usb.cfg_desc = &usb_config_highspeed; + + /* 512 Byte DMA transfers */ + usb_dma_ctrl |= AR9170_USB_DMA_CTL_HIGH_SPEED; + } else { + fw.usb.cfg_desc = &usb_config_fullspeed; + fw.usb.os_cfg_desc = &usb_config_highspeed; + } + +#ifdef CONFIG_CARL9170FW_USB_UP_STREAM +# if (CONFIG_CARL9170FW_RX_FRAME_LEN == 4096) + usb_dma_ctrl |= AR9170_USB_DMA_CTL_UP_STREAM_4K; +# elif (CONFIG_CARL9170FW_RX_FRAME_LEN == 8192) + usb_dma_ctrl |= AR9170_USB_DMA_CTL_UP_STREAM_8K; +# elif (CONFIG_CARL9170FW_RX_FRAME_LEN == 16384) + usb_dma_ctrl |= AR9170_USB_DMA_CTL_UP_STREAM_16K; +# elif (CONFIG_CARL9170FW_RX_FRAME_LEN == 32768) + usb_dma_ctrl |= AR9170_USB_DMA_CTL_UP_STREAM_32K; +# else +# error "Invalid AR9170_RX_FRAME_LEN setting" +# endif + +#else /* CONFIG_CARL9170FW_USB_UP_STREAM */ + usb_dma_ctrl |= AR9170_USB_DMA_CTL_UP_PACKET_MODE; +#endif /* CONFIG_CARL9170FW_USB_UP_STREAM */ + +#ifdef CONFIG_CARL9170FW_USB_DOWN_STREAM + /* Enable down stream mode */ + usb_dma_ctrl |= AR9170_USB_DMA_CTL_DOWN_STREAM; +#endif /* CONFIG_CARL9170FW_USB_DOWN_STREAM */ + +#ifdef CONFIG_CARL9170FW_USB_UP_STREAM + /* Set the up stream mode maximum aggregate number */ + set(AR9170_USB_REG_MAX_AGG_UPLOAD, 4); + + /* + * Set the up stream mode timeout value. + * NB: The vendor driver (otus) set 0x80? + */ + set(AR9170_USB_REG_UPLOAD_TIME_CTL, 0x80); +#endif /* CONFIG_CARL9170FW_USB_UP_STREAM */ + + /* Enable up stream and down stream */ + usb_dma_ctrl |= AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE | + AR9170_USB_DMA_CTL_ENABLE_FROM_DEVICE; + + set(AR9170_USB_REG_DMA_CTL, usb_dma_ctrl); +} + +void usb_init(void) +{ + usb_pta_init(); + + fw.usb.config = 1; + /* + * The fw structure is always initialized with "0" + * during boot(); No need to waste precious bytes here. + * + * fw.usb.interface_setting = 0; + * fw.usb.alternate_interface_setting = 0; + * fw.usb.device_feature = 0; + */ + +#ifdef CONFIG_CARL9170FW_WOL + fw.usb.device_feature |= USB_DEVICE_REMOTE_WAKEUP; + usb_enable_remote_wakeup(); +#endif /* CONFIG_CARL9170FW_WOL */ +} + +#define GET_ARRAY(a, o) ((uint32_t *) (((unsigned long) data) + offset)) + +static void usb_ep0rx_data(const void *data, const unsigned int len) +{ + unsigned int offset; + uint32_t value; + + BUG_ON(len > AR9170_USB_EP_CTRL_MAX); + BUILD_BUG_ON(len > AR9170_USB_EP_CTRL_MAX); + + for (offset = 0; offset < ((len + 3) & ~3); offset += 4) { + value = get(AR9170_USB_REG_EP0_DATA); + memcpy(GET_ARRAY(data, offset), &value, + min(len - offset, (unsigned int)4)); + } +} + +static int usb_ep0tx_data(const void *data, const unsigned int len) +{ + unsigned int offset = 0, block, last_block = 0; + uint32_t value; + + BUG_ON(len > AR9170_USB_EP_CTRL_MAX); + BUILD_BUG_ON(len > AR9170_USB_EP_CTRL_MAX); + + block = min(len, (unsigned int) 4); + offset = 0; + while (offset < len) { + + if (last_block != block || block < 4) + setb(AR9170_USB_REG_FIFO_SIZE, (1 << block) - 1); + + memcpy(&value, GET_ARRAY(data, offset), block); + + set(AR9170_USB_REG_EP0_DATA, value); + + offset += block; + last_block = block = min(len - offset, (unsigned int) 4); + } + + setb(AR9170_USB_REG_FIFO_SIZE, 0xf); + + /* this will push the data to the host */ + return 1; +} +#undef GET_ARRAY + +#ifdef CONFIG_CARL9170FW_USB_STANDARD_CMDS +static int usb_get_status(const struct usb_ctrlrequest *ctrl) +{ + __le16 status = cpu_to_le16(fw.usb.device_feature); + + if ((ctrl->bRequestType & USB_DIR_MASK) != USB_DIR_IN) + return -1; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + status &= cpu_to_le16(~USB_DEVICE_SELF_POWERED); + status &= cpu_to_le16(~USB_DEVICE_REMOTE_WAKEUP); + break; + + case USB_RECIP_INTERFACE: + /* USB spec: This is reserved for future use. */ + status = cpu_to_le16(0); + break; + + case USB_RECIP_ENDPOINT: + case USB_RECIP_OTHER: + default: + break; + } + + return usb_ep0tx_data((const void *) &status, sizeof(status)); +} + +static int usb_get_string_desc(const struct usb_ctrlrequest *ctrl) +{ + const struct usb_string_descriptor *string_desc = NULL; + + switch (le16_to_cpu(ctrl->wValue) & 0xff) { + case 0x00: + string_desc = (const struct usb_string_descriptor *) + rom.hw.usb.string0_desc; + break; + + case 0x10: + string_desc = (const struct usb_string_descriptor *) + rom.hw.usb.string1_desc; + break; + + case 0x20: + string_desc = (const struct usb_string_descriptor *) + rom.hw.usb.string2_desc; + break; + + case 0x30: + string_desc = (const struct usb_string_descriptor *) + rom.hw.usb.string3_desc; + break; + + default: + break; + } + + if (string_desc) + return usb_ep0tx_data(string_desc, string_desc->bLength); + + return -1; +} + +static int usb_get_device_desc(const struct usb_ctrlrequest *ctrl __unused) +{ + return usb_ep0tx_data(&rom.hw.usb.device_desc, + rom.hw.usb.device_desc.bLength); +} + +static int usb_get_config_desc(const struct usb_ctrlrequest *ctrl __unused) +{ + fw.usb.cfg_desc->cfg.bDescriptorType = USB_DT_CONFIG; + + return usb_ep0tx_data(fw.usb.cfg_desc, + le16_to_cpu(fw.usb.cfg_desc->cfg.wTotalLength)); +} + +#ifdef CONFIG_CARL9170FW_USB_MODESWITCH +static int usb_get_otherspeed_desc(const struct usb_ctrlrequest *ctrl __unused) +{ + + fw.usb.os_cfg_desc->cfg.bDescriptorType = USB_DT_OTHER_SPEED_CONFIG; + + return usb_ep0tx_data(fw.usb.os_cfg_desc, + le16_to_cpu(fw.usb.os_cfg_desc->cfg.wTotalLength)); +} +#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */ + +static int usb_get_qualifier_desc(const struct usb_ctrlrequest *ctrl __unused) +{ + struct usb_qualifier_descriptor qual; + + /* + * The qualifier descriptor shares some structural details + * with the main device descriptor. + */ + + memcpy(&qual, &rom.hw.usb.device_desc, sizeof(qual)); + + /* (Re)-Initialize fields */ + qual.bDescriptorType = USB_DT_DEVICE_QUALIFIER; + qual.bLength = sizeof(qual); + qual.bNumConfigurations = rom.hw.usb.device_desc.bNumConfigurations; + qual.bRESERVED = 0; + + return usb_ep0tx_data(&qual, qual.bLength); +} + +#define USB_CHECK_REQTYPE(ctrl, recip, dir) \ + (((ctrl->bRequestType & USB_RECIP_MASK) != recip) || \ + ((ctrl->bRequestType & USB_DIR_MASK) != dir)) + +static int usb_get_descriptor(const struct usb_ctrlrequest *ctrl) +{ + int status = -1; + + if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_DEVICE, USB_DIR_IN)) + return status; + + switch (le16_to_cpu(ctrl->wValue) >> 8) { + case USB_DT_DEVICE: + status = usb_get_device_desc(ctrl); + break; + + case USB_DT_CONFIG: + status = usb_get_config_desc(ctrl); + break; + + case USB_DT_STRING: + status = usb_get_string_desc(ctrl); + break; + + case USB_DT_INTERFACE: + break; + + case USB_DT_ENDPOINT: + break; + + case USB_DT_DEVICE_QUALIFIER: + status = usb_get_qualifier_desc(ctrl); + break; + +#ifdef CONFIG_CARL9170FW_USB_MODESWITCH + case USB_DT_OTHER_SPEED_CONFIG: + status = usb_get_otherspeed_desc(ctrl); + break; +#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */ + default: + break; + + } + + return status; +} + +static int usb_get_configuration(const struct usb_ctrlrequest *ctrl) +{ + if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_DEVICE, USB_DIR_IN)) + return -1; + + return usb_ep0tx_data(&fw.usb.config, 1); +} + +static int usb_set_configuration(const struct usb_ctrlrequest *ctrl) +{ + unsigned int config; + + if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_DEVICE, USB_DIR_OUT)) + return -1; + + config = le16_to_cpu(ctrl->wValue); + switch (config) { + case 0: + /* Disable Device */ + andb(AR9170_USB_REG_DEVICE_ADDRESS, + (uint8_t) ~(AR9170_USB_DEVICE_ADDRESS_CONFIGURE)); +#ifdef CONFIG_CARL9170FW_USB_MODESWITCH + case 1: + fw.usb.config = config; + + if (usb_detect_highspeed()) { + /* High Speed Configuration */ + usb_init_highspeed_fifo_cfg(); + } else { + /* Full Speed Configuration */ + usb_init_fullspeed_fifo_cfg(); + } + break; + + default: + return -1; + } + /* usb_pta_init() ? */ + + usb_reset_eps(); + orb(AR9170_USB_REG_DEVICE_ADDRESS, + (AR9170_USB_DEVICE_ADDRESS_CONFIGURE)); + + usb_enable_global_int(); + usb_trigger_out(); + return 1; +#else + default: + return -1; + } +#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */ +} + +static int usb_set_address(const struct usb_ctrlrequest *ctrl) +{ + unsigned int address; + + if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_DEVICE, USB_DIR_OUT)) + return -1; + + address = le16_to_cpu(ctrl->wValue); + + /* + * The original firmware used 0x100 (which is, of course, + * too big to fit into uint8_t). + * However based on the available information (hw.h), BIT(7) + * is used as some sort of flag and should not be + * part of the device address. + */ + if (address >= BIT(7)) + return -1; + + setb(AR9170_USB_REG_DEVICE_ADDRESS, (uint8_t) address); + return 1; +} + +static int usb_get_interface(const struct usb_ctrlrequest *ctrl) +{ + if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_INTERFACE, USB_DIR_IN)) + return -1; + + if (usb_configured() == false) + return -1; + + switch (fw.usb.config) { + case 1: + break; + + default: + return -1; + } + + return usb_ep0tx_data(&fw.usb.alternate_interface_setting, 1); +} + +static int usb_manipulate_feature(const struct usb_ctrlrequest *ctrl, bool __unused clear) +{ + unsigned int feature; + if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_DEVICE, USB_DIR_OUT)) + return -1; + + if (usb_configured() == false) + return -1; + + feature = le16_to_cpu(ctrl->wValue); + +#ifdef CONFIG_CARL9170FW_WOL + if (feature & USB_DEVICE_REMOTE_WAKEUP) { + if (clear) + usb_disable_remote_wakeup(); + else + usb_enable_remote_wakeup(); + } +#endif /* CONFIG_CARL9170FW_WOL */ + + if (clear) + fw.usb.device_feature &= ~feature; + else + fw.usb.device_feature |= feature; + + return 1; +} + +#ifdef CONFIG_CARL9170FW_USB_MODESWITCH +static int usb_set_interface(const struct usb_ctrlrequest *ctrl) +{ + unsigned int intf, alt_intf; + if (USB_CHECK_REQTYPE(ctrl, USB_RECIP_INTERFACE, USB_DIR_OUT)) + return -1; + + if (usb_configured() == false) + return -1; + + intf = le16_to_cpu(ctrl->wIndex); + alt_intf = le16_to_cpu(ctrl->wValue); + + switch (intf) { + case 0: + if (alt_intf != fw.usb.cfg_desc->intf.bAlternateSetting) + return -1; + + fw.usb.interface_setting = (uint8_t) intf; + fw.usb.alternate_interface_setting = (uint8_t) alt_intf; + if (usb_detect_highspeed()) + usb_init_highspeed_fifo_cfg(); + else + usb_init_fullspeed_fifo_cfg(); + + usb_reset_eps(); + usb_enable_global_int(); + usb_trigger_out(); + return 1; + + default: + return -1; + } +} +#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */ +#endif /* CONFIG_CARL9170FW_USB_STANDARD_CMDS */ + +static int usb_standard_command(const struct usb_ctrlrequest *ctrl __unused) +{ + int status = -1; + +#ifdef CONFIG_CARL9170FW_USB_STANDARD_CMDS + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + status = usb_get_status(ctrl); + break; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + usb_manipulate_feature(ctrl, ctrl->bRequest == USB_REQ_CLEAR_FEATURE); + break; + + case USB_REQ_SET_ADDRESS: + status = usb_set_address(ctrl); + break; + + case USB_REQ_GET_DESCRIPTOR: + status = usb_get_descriptor(ctrl); + break; + + case USB_REQ_SET_DESCRIPTOR: + break; + + case USB_REQ_GET_CONFIGURATION: + status = usb_get_configuration(ctrl); + break; + + case USB_REQ_SET_CONFIGURATION: + status = usb_set_configuration(ctrl); + break; + + case USB_REQ_GET_INTERFACE: + status = usb_get_interface(ctrl); + break; + + case USB_REQ_SET_INTERFACE: +#ifdef CONFIG_CARL9170FW_USB_MODESWITCH + status = usb_set_interface(ctrl); +#endif /* CONFIG_CARL9170FW_USB_MODESWITCH */ + break; + + case USB_REQ_SYNCH_FRAME: + break; + + default: + break; + + } +#endif /* CONFIG_CARL9170FW_USB_STANDARD_CMDS */ + + return status; +} + +static int usb_class_command(const struct usb_ctrlrequest *ctrl __unused) +{ + return -1; +} + +static int usb_vendor_command(const struct usb_ctrlrequest *ctrl __unused) +{ + /* + * Note: Firmware upload/boot is not implemented. + * It's impossible to replace the current image + * in place. + */ + + return -1; +} + +#undef USB_CHECK_TYPE + +void usb_ep0setup(void) +{ + struct usb_ctrlrequest ctrl; + int status = -1; + usb_ep0rx_data(&ctrl, sizeof(ctrl)); + + switch (ctrl.bRequestType & USB_TYPE_MASK) { + case USB_TYPE_STANDARD: + status = usb_standard_command(&ctrl); + break; + + case USB_TYPE_CLASS: + status = usb_class_command(&ctrl); + break; + + case USB_TYPE_VENDOR: + status = usb_vendor_command(&ctrl); + break; + + default: + break; + + } + + if (status < 0) + fw.usb.ep0_action |= CARL9170_EP0_STALL; +#ifdef CONFIG_CARL9170FW_USB_STANDARD_CMDS + if (status > 0) + fw.usb.ep0_action |= CARL9170_EP0_TRIGGER; +#endif /* CONFIG_CARL9170FW_USB_STANDARD_CMDS */ +} + +void usb_ep0rx(void) +{ + if (BUG_ON(!fw.usb.ep0_txrx_buffer || !fw.usb.ep0_txrx_len)) + return ; + + usb_ep0rx_data(fw.usb.ep0_txrx_buffer, fw.usb.ep0_txrx_len); + fw.usb.ep0_txrx_pos = fw.usb.ep0_txrx_len; +} + +void usb_ep0tx(void) +{ + if (BUG_ON(!fw.usb.ep0_txrx_buffer || !fw.usb.ep0_txrx_len)) + return ; + + usb_ep0tx_data(fw.usb.ep0_txrx_buffer, fw.usb.ep0_txrx_len); + fw.usb.ep0_txrx_pos = fw.usb.ep0_txrx_len; +} |