diff --git a/core/app_main.c b/core/app_main.c index 19bf73e..45f7e2f 100644 --- a/core/app_main.c +++ b/core/app_main.c @@ -24,6 +24,7 @@ #include "os_version.h" #include "espressif/esp_common.h" +#include "espressif/phy_info.h" #include "sdk_internal.h" /* This is not declared in any header file (but arguably should be) */ @@ -31,8 +32,6 @@ void user_init(void); #define BOOT_INFO_SIZE 28 -//TODO: phy_info should probably be a struct (no idea about its organization, though) -#define PHY_INFO_SIZE 128 // These are the offsets of these values within the RTCMEM regions. It appears // that the ROM saves them to RTCMEM before calling us, and we pull them out of @@ -45,26 +44,6 @@ void user_init(void); extern uint32_t _bss_start; extern uint32_t _bss_end; -// .Ldata003 -- .irom.text+0x0 -static const uint8_t IROM default_phy_info[PHY_INFO_SIZE] = { - 0x05, 0x00, 0x04, 0x02, 0x05, 0x05, 0x05, 0x02, - 0x05, 0x00, 0x04, 0x05, 0x05, 0x04, 0x05, 0x05, - 0x04, 0xfe, 0xfd, 0xff, 0xf0, 0xf0, 0xf0, 0xe0, - 0xe0, 0xe0, 0xe1, 0x0a, 0xff, 0xff, 0xf8, 0x00, - 0xf8, 0xf8, 0x52, 0x4e, 0x4a, 0x44, 0x40, 0x38, - 0x00, 0x00, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xe1, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x93, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - // user_init_flag -- .bss+0x0 uint8_t sdk_user_init_flag; @@ -82,7 +61,7 @@ xTaskHandle sdk_xWatchDogTaskHandle; static void IRAM get_otp_mac_address(uint8_t *buf); static void IRAM set_spi0_divisor(uint32_t divisor); static void zero_bss(void); -static void init_networking(uint8_t *phy_info, uint8_t *mac_addr); +static void init_networking(sdk_phy_info_t *phy_info, uint8_t *mac_addr); static void init_g_ic(void); static void dump_excinfo(void); static void user_start_phase2(void); @@ -273,7 +252,7 @@ static void zero_bss(void) { } // .Lfunc006 -- .irom0.text+0x70 -static void init_networking(uint8_t *phy_info, uint8_t *mac_addr) { +static void init_networking(sdk_phy_info_t *phy_info, uint8_t *mac_addr) { if (sdk_register_chipv6_phy(phy_info)) { printf("FATAL: sdk_register_chipv6_phy failed"); halt(); @@ -413,7 +392,7 @@ extern void (*__init_array_end)(void); // .Lfunc009 -- .irom0.text+0x5b4 static __attribute__((noinline)) void user_start_phase2(void) { uint8_t *buf; - uint8_t *phy_info; + sdk_phy_info_t phy_info, default_phy_info; sdk_system_rtc_mem_read(0, &sdk_rst_if, sizeof(sdk_rst_if)); if (sdk_rst_if.version > 3) { @@ -431,8 +410,16 @@ static __attribute__((noinline)) void user_start_phase2(void) { sdk_info._unknown1 = 0x00ffffff; sdk_info._unknown2 = 0x0104a8c0; init_g_ic(); - phy_info = malloc(PHY_INFO_SIZE); - sdk_spi_flash_read(sdk_flashchip.chip_size - sdk_flashchip.sector_size * 4, (uint32_t *)phy_info, PHY_INFO_SIZE); + + read_saved_phy_info(&phy_info); + get_default_phy_info(&default_phy_info); + + if (phy_info.version != default_phy_info.version) { + /* Versions don't match, use default for PHY info + (may be a blank config sector, or a new default version.) + */ + memcpy(&phy_info, &default_phy_info, sizeof(sdk_phy_info_t)); + } // Disable default buffering on stdout setbuf(stdout, NULL); @@ -440,13 +427,7 @@ static __attribute__((noinline)) void user_start_phase2(void) { uart_flush_txfifo(0); uart_flush_txfifo(1); - if (phy_info[0] != 5) { - // Bad version byte. Discard what we read and use default values - // instead. - memcpy(phy_info, default_phy_info, PHY_INFO_SIZE); - } - init_networking(phy_info, sdk_info.sta_mac_addr); - free(phy_info); + init_networking(&phy_info, sdk_info.sta_mac_addr); // Call gcc constructor functions void (**ctor)(void); @@ -487,7 +468,7 @@ static __attribute__((noinline)) void dump_flash_config_sectors(uint32_t start_s printf("system param error\n"); // Note: original SDK code didn't dump PHY info printf("phy_info:\n"); - dump_flash_sector(start_sector, PHY_INFO_SIZE); + dump_flash_sector(start_sector, sizeof(sdk_phy_info_t)); printf("\ng_ic saved 0:\n"); dump_flash_sector(start_sector + 1, sizeof(struct sdk_g_ic_saved_st)); printf("\ng_ic saved 1:\n"); diff --git a/core/include/sdk_internal.h b/core/include/sdk_internal.h index 5a5aee5..b8233fd 100644 --- a/core/include/sdk_internal.h +++ b/core/include/sdk_internal.h @@ -3,6 +3,7 @@ #include "espressif/esp_wifi.h" #include "espressif/spi_flash.h" +#include "espressif/phy_info.h" #include "lwip/netif.h" /////////////////////////////////////////////////////////////////////////////// @@ -217,7 +218,7 @@ void sdk_phy_enable_agc(void); void sdk_pm_attach(void); void sdk_pp_attach(void); void sdk_pp_soft_wdt_init(void); -int sdk_register_chipv6_phy(uint8_t *); +int sdk_register_chipv6_phy(sdk_phy_info_t *); void sdk_sleep_reset_analog_rtcreg_8266(void); uint32_t sdk_system_get_checksum(uint8_t *, uint32_t); void sdk_system_restart_in_nmi(void); diff --git a/core/phy_info.c b/core/phy_info.c new file mode 100644 index 0000000..8d83d1b --- /dev/null +++ b/core/phy_info.c @@ -0,0 +1,161 @@ +/* Routines to allow custom access to the Internal Espressif + SDK PHY datastructures. + + Matches espressif/phy_internal.h + + Part of esp-open-rtos. Copyright (C) 2016 Angus Gratton, + BSD Licensed as described in the file LICENSE. + */ +#include +#include +#include +#include + +static const sdk_phy_info_t IROM default_phy_info = { + ._reserved00 = { 0x05, 0x00, 0x04, 0x02, 0x05 }, + .version = 5, + ._reserved06 = { 0x05, 0x02, 0x05, 0x00, 0x04, 0x05, 0x05, 0x04, + 0x05, 0x05, 0x04,-0x02,-0x03,-0x01,-0x10,-0x10, + -0x10,-0x20,-0x20, -0x20}, + .spur_freq_primary = 225, + .spur_freq_divisor = 10, + .spur_freq_en_h = 0xFF, + .spur_freq_en_l = 0xFF, + + ._reserved1e = { 0xf8, 0, 0xf8, 0xf8 }, + + .target_power = { 82, 78, 74, 68, 64, 56 }, + .target_power_index_mcs = { 0, 0, 1, 1, 2, 3, 4, 5 }, + + .crystal_freq = CRYSTAL_FREQ_26M, + + .sdio_config = SDIO_CONFIG_AUTO, + + .bt_coexist_config = BT_COEXIST_CONFIG_NONE, + .bt_coexist_protocol = BT_COEXIST_PROTOCOL_WIFI_ONLY, + + .dual_ant_config = DUAL_ANT_CONFIG_NONE, + + ._reserved34 = 0x02, + + .crystal_sleep = CRYSTAL_SLEEP_OFF, + + .spur_freq_2_primary = 225, + .spur_freq_2_divisor = 10, + .spur_freq_2_en_h = 0x00, + .spur_freq_2_en_l = 0x00, + .spur_freq_cfg_msb = 0x00, + .spur_freq_2_cfg_msb = 0x00, + .spur_freq_3_cfg = 0x0000, + .spur_freq_4_cfg = 0x0000, + + ._reserved4a = { 0x01, 0x93, 0x43, 0x00 }, + + .low_power_en = false, + .lp_atten_stage01 = LP_ATTEN_STAGE01_23DB, + .lp_atten_bb = 0, + + .pwr_ind_11b_en = false, + .pwr_ind_11b_0 = 0, + .pwr_ind_11b_1 = 0, + + /* Nominal 3.3V VCC. NOTE: This value is 0 in the + esp-open-rtos SDK default config sector, and may be unused + by that version of the SDK? + */ + .pa_vdd = 33, + + /* Note: untested with the esp-open-rtos SDK default config sector, may be unused? */ + .freq_correct_mode = FREQ_CORRECT_DISABLE, + .force_freq_offset = 0, + + /* Note: is zero with the esp-open-rtos SDK default config sector, may be unused? */ + .rf_cal_mode = RF_CAL_MODE_SAVED, +}; + +void get_default_phy_info(sdk_phy_info_t *info) __attribute__((weak, alias("get_sdk_default_phy_info"))); + +void get_sdk_default_phy_info(sdk_phy_info_t *info) +{ + memcpy(info, &default_phy_info, sizeof(sdk_phy_info_t)); +} + +void read_saved_phy_info(sdk_phy_info_t *info) +{ + sdk_spi_flash_read(sdk_flashchip.chip_size - sdk_flashchip.sector_size * 4, (uint32_t *)info, sizeof(sdk_phy_info_t)); +} + +void write_saved_phy_info(const sdk_phy_info_t *info) +{ + sdk_spi_flash_write(sdk_flashchip.chip_size - sdk_flashchip.sector_size * 4, (uint32_t *)info, sizeof(sdk_phy_info_t)); +} + +void dump_phy_info(const sdk_phy_info_t *info, bool raw) +{ + printf("version=%d\n", info->version); + printf("spur_freq = %.3f (%d/%d)\n", + (float)info->spur_freq_primary / info->spur_freq_divisor, + info->spur_freq_primary, + info->spur_freq_divisor); + printf("spur_freq_en = 0x%02x 0x%02x\n", info->spur_freq_en_h, + info->spur_freq_en_l); + printf("target_power\n"); + for(int i = 0; i < 6; i++) { + printf(" %d: %.2fdB (raw 0x%02x)\n", i, + info->target_power[i]/4.0, + info->target_power[i]); + } + printf("target_power_index_mcs:"); + for(int i = 0; i < 8; i++) { + printf(" %d%c", info->target_power_index_mcs[i], + i == 7 ? '\n' : ','); + } + + printf("crystal_freq: %s (raw %d)\n", + (info->crystal_freq == CRYSTAL_FREQ_40M ? "40MHz" : + (info->crystal_freq == CRYSTAL_FREQ_26M ? "26MHz" : + (info->crystal_freq == CRYSTAL_FREQ_24M ? "24MHz" : "???"))), + info->crystal_freq); + + printf("sdio_config: %d\n", info->sdio_config); + printf("bt_coexist config: %d protocol: 0x%02x\n", + info->bt_coexist_config, info->bt_coexist_protocol); + printf("dual_ant_config: %d\n", info->dual_ant_config); + + printf("crystal_sleep: %d\n", info->crystal_sleep); + + printf("spur_freq_2 = %.3f (%d/%d)\n", + (float)info->spur_freq_2_primary / info->spur_freq_2_divisor, + info->spur_freq_2_primary, + info->spur_freq_2_divisor); + printf("spur_freq_2_en = 0x%02x 0x%02x\n", info->spur_freq_2_en_h, + info->spur_freq_2_en_l); + + printf("spur_freq_cfg_msb = 0x%02x\n", info->spur_freq_cfg_msb); + printf("spur_freq_2_)cfg_msb = 0x%02x\n", info->spur_freq_2_cfg_msb); + printf("spur_freq_3_cfg = 0x%04x\n", info->spur_freq_3_cfg); + printf("spur_freq_4_cfg = 0x%04x\n", info->spur_freq_4_cfg); + + printf("low_power_en = %d\n", info->low_power_en); + printf("lp_atten_stage01 = 0x%02x\n", info->lp_atten_stage01); + printf("lp_atten_bb = %.2f (raw 0x%02x)\n", info->lp_atten_bb / 4.0, + info->lp_atten_bb); + + printf("pa_vdd = %d\n", info->pa_vdd); + + printf("freq_correct_mode = 0x%02x\n", info->freq_correct_mode); + printf("force_freq_offset = %d\n", info->force_freq_offset); + printf("rf_cal_mode = 0x%02x\n", info->rf_cal_mode); + + if(raw) { + printf("Raw values:"); + uint8_t *p = (uint8_t *)info; + for(int i = 0; i < sizeof(sdk_phy_info_t); i ++) { + if(i % 8 == 0) { + printf("\n0x%02x:", i); + } + printf(" %02x", p[i]); + } + printf("\n\n"); + } +} diff --git a/include/espressif/phy_info.h b/include/espressif/phy_info.h new file mode 100644 index 0000000..bda23ea --- /dev/null +++ b/include/espressif/phy_info.h @@ -0,0 +1,482 @@ +/** Internal Espressif SDK "PHY info" data structure + + The data structure (sdk_phy_info_t) is used to configure the + ESP8266 PHY layer via the SDK. The fields here are not written + directly to hardware, the SDK code (mostly in libphy) parses this + structure and configures the hardware. + + The structure loaded at reset time from a flash configuration + sector (see read_saved_phy_info()) (Espressif's SDK sources this + from a file "esp_init_data_default.bin"). If no valid structure is + found in the flash config sector then the SDK loads default values + (see get_default_phy_info()). It is possible to implement a custom + get_default_phy_info() to change the PHY default settings (see the + 'version' field below). + + @note It is possible that the SDK will quietly write a new + configuration sector to flash itself following internal + calibration, etc. However this does not seem to happen, you need to + flash it explicitly if you want it stored there. + + @note Most of what is below is unconfirmed, except where a @note + says that it has been confirmed to work as expected. Please + consider submitting notes if you find behaviour here that works or + doesn't work as expected. + + Information on the meaning/offset of fields comes from Espressif's + flash download tool, which uses an Excel spreadsheet (in the + init_data directory of the ZIP file) to configure and a Python + script to convert an esp_init_data_custom.bin file to flash: + http://bbs.espressif.com/viewtopic.php?f=5&t=433 + + Many fields remain undocumented (& disassembly of libphy suggests + that some documented fields supported undocumented values.) + + A few additional notes about the phy_info fields can be found + in the ESP Arduino ESP8266 phy_init_data structure (however most of + that content is verbatim from Espressif's spreadsheet): + https://github.com/esp8266/Arduino/blob/master/cores/esp8266/core_esp8266_phy.c#L29 + + Part of esp-open-rtos. Copyright (C) 2016 Angus Gratton, + BSD Licensed as described in the file LICENSE. + */ +#ifndef _ESPRESSIF_PHY_INFO_H +#define _ESPRESSIF_PHY_INFO_H + +#include +#include +#include + +/* CRYSTAL_FREQ_xx values as used by sdk_phy_info_t.crystal_freq */ +#define CRYSTAL_FREQ_40M 0 +#define CRYSTAL_FREQ_26M 1 +#define CRYSTAL_FREQ_24M 2 + +/* SDIO_CONFIG_xx values as used by sdk_phy_info_t.sdio_config */ +#define SDIO_CONFIG_AUTO 0 /* Uses pin strapping to determine */ +#define SDIO_CONFIG_SDIOV1_1 /* Data output on negative edge */ +#define SDIO_CONFIG_SDIOV2_0 /* data output on positive edge */ + +/* BT_COEXIST_CONFIG_xx values as used by sdk_phy_info_t.bt_coexist */ +/* No bluetooth */ +#define BT_COEXIST_CONFIG_NONE 0 +/* Coexistence configuration A: + GPIO 0 - WLAN_ACTIVE + GPIO 14 - BT_ACTIVE + GPIO 13 - BT_PRIORITY + GPIO 3 - ANT_SEL_BT +*/ +#define BT_COEXIST_CONFIG_A 1 +/* No coexistence, but Bluetooth enabled? + Unsure how this works? + */ +#define BT_COEXIST_CONFIG_PRESENT 2 +/* Coexistence configuration B: + GPIO 0 - WLAN_ACTIVE + GPIO 14 - BT_PRIORITY + GPIO 13 - BT_ACTIVE + GPIO 3 - ANT_SEL_BT +*/ +#define BT_COEXIST_CONFIG_B 3 + +/* BT_COEXIST_PROTOCOL_xx values for coexistence protocol, + field sdk_phy_info_t.bt_coexist_protocol + */ +#define BT_COEXIST_PROTOCOL_WIFI_ONLY 0 +#define BT_COEXIST_PROTOCOL_BT_ONLY 1 + +/* Coexistence is enabled, Bluetooth has its own antenna */ +#define BT_COEXIST_PROTOCOL_FLAG_SEPARATE_ANT 2 +/* Coexistence is enabled, Bluetooth shares WiFi antenna */ +#define BT_COEXIST_PROTOCOL_FLAG_SHARE_ANT 4 + +/* Coexistence is enabled, use only BT_ACTIVE signal */ +#define BT_COEXIST_PROTOCOL_FLAG_BT_ACTIVE_ONLY 0 +/* Coexistence is enabled, use both BT_ACTIVE and BT_PRIORITY signals */ +#define BT_COEXIST_PROTOCOL_FLAG_BT_ACTIVE_PRIORITY 1 + +/* DUAL_ANT_CONFIG_xx values for dual antenna config, + field sdk_phy_info_t.dual_ant_config + + (Not really clear how this feature works, if at all.) +*/ +#define DUAL_ANT_CONFIG_NONE 0 +/* antenna diversity for WiFi, use GPIO0 + U0RXD (?) */ +#define DUAL_ANT_CONFIG_DUAL 1 +/* TX/RX switch for external PA & LNA: GPIO 0 high, GPIO 3 low during TX */ +#define DUAL_ANT_CONFIG_TX_GPIO0_HIGH_GPIO3_LOW +/* TX/RX switch for external PA & LNA: GPIO 0 low, GPIO 3 high during TX */ +#define DUAL_ANT_CONFIG_TX_GPIO0_LOW_GPIO3_HIGH + + +/* CRYSTAL_SLEEP_xx values used for sdk_phy_info_t.crystal_sleep + */ +#define CRYSTAL_SLEEP_OFF 0 +#define CRYSTAL_SLEEP_ON 1 +#define CRYSTAL_SLEEP_GPIO16 2 +#define CRYSTAL_SLEEP_GPIO2 3 + +/* RF Stage 0 & 1 attenuation constants. Use for sdk_phy_info_t.lp_atten_stage01 + + @note These values have been tested and are confirmed to work as + expected by measuring RSSI w/ rt73 USB adapter in monitor mode + (some values also checked on spectrum analyzer) - provided + low_power_en is set then the signal is attenuated as per this + setting. + + (It may look like LP_ATTEN_STAGE01_11_5DB is out of order, but + according to monitor mode captures this is the correct ordering of + these constants.) + + Setting the numeric values in between these constants appears to + also attenuate the signal, but not necessarily by the amount you'd + expect. +*/ +#define LP_ATTEN_STAGE01_0DB 0x0f /* 0dB */ +#define LP_ATTEN_STAGE01_2_5DB 0x0e /* -2.5dB */ +#define LP_ATTEN_STAGE01_6DB 0x0d /* -6dB */ +#define LP_ATTEN_STAGE01_8_5DB 0x09 /* -8.5dB */ +#define LP_ATTEN_STAGE01_11_5DB 0x0c /* -11.5dB */ +#define LP_ATTEN_STAGE01_14DB 0x08 /* -14dB */ +#define LP_ATTEN_STAGE01_17_5DB 0x04 /* -17.5dB */ +#define LP_ATTEN_STAGE01_23DB 0x00 /* -23dB */ + +/* Constant for sdk_phy_info_t.pa_vdd */ +#define PA_VDD_MEASURE_VCC 0xFF + +/* Bitmask flags for sdk_phy_info_t.freq_correct_mode */ + +/* Set this flag to disable frequency offset correction */ +#define FREQ_CORRECT_DISABLE 0 + +/* Set this flag to enable frequency offset correction */ +#define FREQ_CORRECT_ENABLE BIT(0) + +/* Set = Baseband PLL frequency is 160MHz (can only apply +ve offset) + * Unset = Baseband PLL frequency is 168MHz (can apply +ve/-ve offset */ +#define FREQ_CORRECT_BB_160M BIT(1) + +/* Set = use force_freq_offset field to correct, Unset = automatically + measure & correct offset +*/ +#define FREQ_CORRECT_FORCE BIT(2) + + +/* RF_CAL_MODE_xx fields used for sdk_phy_info_t.rf_cal_mode + */ +/* Use saved RF CAL data from flash, only. RF init takes 2ms. */ +#define RF_CAL_MODE_SAVED 0 +/* Calibrate TX power control only, use saved RF CAL data for others. + RF init takes 20ms. */ +#define RF_CAL_MODE_TXPOWER_ONLY 1 +/* Unclear if/how this mode is different to 2? */ +#define RF_CAL_MODE_SAVED_2 2 +/* Run full RF CAL routine. RF init takes approx 200ms. */ +#define RF_CAL_MODE_FULL 3 + +/* Data structure that maps to the phy_info configuration block */ +typedef struct __attribute__((packed)) { + uint8_t _reserved00[0x05]; /* 0x00 - 0x04 */ + + /* This "version" field was set to 5 in the SDK phy_info, + and the original SDK startup code checks it is 5 and then loads + default PHY configuration otherwise. + + esp-open-rtos will load phy_info from get_default_phy_info() if + the value stored in flash has a different value to the value + returned in get_default_phy_info(). This means you can + increment the version return by get_default_phy_info() (to any + value but 0xFF), and know that the new defaults will replace + any older stored values. + + @notes It's not clear whether this is actually a version field + (the other 24 bytes here have equally arbitrary numbers in + them.) Changing the "version" to other values does not seem to + effect WiFi performance at all, neither does zeroing out the + first 5 reserved bytes in _reserved00. However zeroing bytes in + the _reserved06 region will break WiFi entirely. + */ + uint8_t version; /* 0x05 */ + int8_t _reserved06[0x14]; /* 0x06 - 0x19 */ + + /* spur_freq = spur_freq_primary / spur_freq_divisor */ + uint8_t spur_freq_primary; /* 0x1a */ + uint8_t spur_freq_divisor; /* 0x1b */ + + /* Bitmask to enable spur_freq for each channel + Appears to be a big endian short word? + */ + uint8_t spur_freq_en_h; /* 0x1c */ + uint8_t spur_freq_en_l; /* 0x1d */ + + uint8_t _reserved1e[4]; /* 0x1e - 0x21 */ + + /* Each value is a target power level. + Units are 1/4 dBm ie value 64 = 16dBm. + + SDK defaults to using these transmit powers: + 20.5dBm, 19.5dBm, 18.5dBm, 17dBm, 16dBm, 14dBm + + @note Adjusting these values is confirmed to reduce + transmit power accordingly. + */ + uint8_t target_power[6]; /* 0x22 - 0x27 */ + + /* Maps 8 MCS (modulation & coding schemes) types for 802.11b, g & + * n to a target_power level index (0-5), set above. + + This mapping of MCS slot to MCS type is derived from the + spreadsheet and also a table sent by Espressif, but is untested + and may be SDK version dependendent (especially any 802.11n + rates). However the general relationship is confirmed to hold + (higher MCS index = higher bit rate). + + MCS 0: 1Mbps/2Mbps/5.5Mbps/11Mbps (802.11b) / 6Mbps/9Mbps (802.11g) + default target_power 0 (default 20.5dBm) + (see also pwr_ind_11b_en) + + MCS 1: 12Mbps (802.11g) + default target_power 0 (default 20.5dBm) + + MCS 2: 18Mbps (802.11g) + default target_power 1 (19.5dBm) + + MCS 3: 24Mbps (802.11g) + default target_power 1 (19.5dBm) + + MCS 4: 36Mbps (802.11g) + default target_power 2 (18.5dBm) + + MCS 5: 48Mbps (802.11g) + default target_power 3 (17dBm) + + MCS 6: 54Mbps (802.11g) + default target_power 4 (16dBm) + + MCS 7: 65Mbps (802.11n) - unclear if ever used? + default target_power 5 (14dBm) + */ + uint8_t target_power_index_mcs[8]; /* 0x28 - 0x2f */ + + /* One of CRYSTAL_FREQ_40M / CRYSTAL_FREQ_26M / CRYSTAL_FREQ_24M + + The crystal configured here is the input to the PLL setting + calculations which are used to derive the CPU & APB peripheral + clock frequency, and probably the WiFi PLLs (unconfirmed.) + */ + uint8_t crystal_freq; /* 0x30 */ + + uint8_t _unused31; /* 0x31: Possibly high byte of crystal freq? */ + + /* One of SDIO_CONFIG_AUTO, SDIO_CONFIG_SDIOV1_1, SDIO_CONFIG_SDIOV2_0 */ + uint8_t sdio_config; /* 0x32 */ + + /* BT coexistence pin configuration. + + One of BT_COEXIST_CONFIG_NONE, BT_COEXIST_CONFIG_A, + BT_COEXIST_CONFIG_PRESENT, BT_COEXIST_CONFIG_B + */ + uint8_t bt_coexist_config; /* 0x33 */ + + /* BT coexistence pin protocol. + + If no coexistence: + Either BT_COEXIST_PROTOCOL_WIFI_ONLY, or + BT_COEXIST_PROTOCOL_BT_ONLY. + + If coexistence: + Combine one of + BT_COEXIST_PROTOCOL_FLAG_SEPARATE_ANT or + BT_COEXIST_PROTOCOL_FLAG_SHARE_ANT + with one of + BT_COEXIST_PROTOCOL_FLAG_BT_ACTIVE_ONLY or + BT_COEXIST_PROTOCOL_FLAG_BT_ACTIVE_BT_PRIORITY + */ + uint8_t bt_coexist_protocol; /* 0x34 */ + + /* Dual antenna configuration + + One of DUAL_ANT_CONFIG_NONE, DUAL_ANT_CONFIG_DUAL, + DUAL_ANT_CONFIG_TX_GPIO0_HIGH_GPIO3_LOW, + DUAL_ANT_CONFIG_TX_GPIO0_LOW_GPIO3_HIGH + */ + uint8_t dual_ant_config; /* 0x35 */ + + uint8_t _reserved34; /* 0x36 */ + + /* For sharing crystal clock with other devices: + one of CRYSTAL_SLEEP_OFF, CRYSTAL_SLEEP_ON, + CRYSTAL_SLEEP_GPIO16, CRYSTAL_SLEEP_GPIO2 + */ + uint8_t crystal_sleep; /* 0x37 */ + + uint8_t _unused38[8]; + + /* spur_freq_2 = spur_freq_2_primary / spur_freq_2_divisor */ + uint8_t spur_freq_2_primary; /* 0x40 */ + uint8_t spur_freq_2_divisor; /* 0x41 */ + + /* Bitmask to enable spur_freq_2 for each channel? + Appears to be a big endian short word? + */ + uint8_t spur_freq_2_en_h; /* 0x42 */ + uint8_t spur_freq_2_en_l; /* 0x43 */ + + /* Not really clear what these do */ + uint8_t spur_freq_cfg_msb; /* 0x44 */ + uint8_t spur_freq_2_cfg_msb; /* 0x45 */ + uint16_t spur_freq_3_cfg; /* 0x46 - 0x47 */ + uint16_t spur_freq_4_cfg; /* 0x48 - 0x49 */ + + uint8_t _reserved4a[4]; /* 0x4a - 0x4d */ + + uint8_t _unused78[15]; /* 0x4e - 0x5c */ + + /* Flag to enable low power mode */ + uint8_t low_power_en; /* 0x5d */ + + /* Low Power attenuation of RF gain stages 0 & 1 + + Attenuates transmit power if/when low_power_en is set. + + Use one of the constants LP_ATTEN_STAGE01_0DB, + LP_ATTEN_STAGE01_2_5DB, LP_ATTEN_STAGE01_6DB, + LP_ATTEN_STAGE01_8_5DB, LP_ATTEN_STAGE01_11_5DB, + LP_ATTEN_STAGE01_14DB, LP_ATTEN_STAGE01_17_5DB, + LP_ATTEN_STAGE01_23DB. + */ + uint8_t lp_atten_stage01; /* 0x5e */ + + /* Low Power(?) attenuation of baseband gain + + Units are minus 1/4 dB, ie value 4 == -1dB. + + Maximum value is 24 (0x18) == -6dB + */ + uint8_t lp_atten_bb; /* 0x5f */ + + /* I believe this means, when pwr_ind_11b_en == 0 then the 802.11g + MCS 0 level from target_power_index_mcs are used to + determine 802.11b transmit power level. + + However, when pwr_ind_11b_en == 1 then the index values in + pwr_ind_11b_0 & pwr_ind_11b_1 are used for 802.11b instead. + + This is all unconfirmed, if you can confirm then please update + this comment. + */ + uint8_t pwr_ind_11b_en; /* 0x60 */ + + /* 802.11b low data rate power index (0~5). + Sets the power level index for operation at 1 & 2Mbps + */ + uint8_t pwr_ind_11b_0; /* 0x61 */ + + /* 802.11b high data rate power index (0~5) + Sets the power level index for operation at 5.5 & 11Mbps + */ + uint8_t pwr_ind_11b_1; /* 0x62 */ + + uint8_t _unused63[8]; /* 0x63 - 0x6a */ + + /* Set the voltage of PA_VDD, which appears to be an internal analog + reference voltage(?) + + This field is called vdd33_const in the Arduino phy fields, + and relates to usage of the TOUT pin (ADC pin). + + Set to PA_VDD_MEASURE_VCC (0xFF) and leave TOUT (ADC) pin + floating in order to use the ADC to measure the 3.3V input + voltage. + + Set to value in the range 18-36 (1.8V to 3.6V) to set a + reference voltage(?) when using TOUT pin as an ADC input. I + think this is the reference voltage used to scale the 0-1V + which is allowed on the pin, in order to get an accurate + reading. So it should be set to a value that matches system + VCC... I think! + */ + uint8_t pa_vdd; /* 0x6b */ + + /* Disable RF calibration cycle for this many times */ + uint8_t disable_rfcal_count; /* 0x6c */ + + uint8_t _unused6d[3]; + + /* Flags for frequency correction + + A bitmask combination of any of: FREQ_CORRECT_DISABLE, + FREQ_CORRECT_ENABLE, FREQ_CORRECT_BB_160M, FREQ_CORRECT_FORCE + */ + uint8_t freq_correct_mode; /* 0x70 */ + + /* Force frequency offset adjustment (instead of auto measuring) + units are 1 = 8kHz, full range +/- 1016kHz. + + Only used if FREQ_CORRECT_ENABLE and FREQ_CORRECT_FORCE are + set in freq_correct_mode. + + Unclear whether setting FREQ_CORRECT_BB_160M (which allows only positive offsets) changes the usable range. + */ + int8_t force_freq_offset; /* 0x71 */ + + /* Use stored data in flash for RF calibration. + + This field was previously called rf_cal_use_flash. + + Acceptable values one of RF_CAL_MODE_SAVED, RF_CAL_MODE_TXPOWER_ONLY, RF_CAL_MODE_SAVED_2, RF_CAL_MODE_FULL. + */ + uint8_t rf_cal_mode; /* 0x72 */ + + uint8_t _unused73[13]; +} sdk_phy_info_t; + +/* Some sanity check static assertions. These can probably be + removed after this structure has been better tested. +*/ +_Static_assert(sizeof(sdk_phy_info_t) == 128, "sdk_phy_info_t is wrong size!"); +_Static_assert(offsetof(sdk_phy_info_t, version) == 5, "version at wrong offset"); +_Static_assert(offsetof(sdk_phy_info_t, target_power) == 34, "target_power_qdb at wrong offset"); +_Static_assert(offsetof(sdk_phy_info_t, bt_coexist_protocol) == 52, "bt_coexist_protocol at wrong offset"); +_Static_assert(offsetof(sdk_phy_info_t, spur_freq_2_primary) == 64, "spur_freq_2_primary at wrong offset"); +_Static_assert(offsetof(sdk_phy_info_t, lp_atten_stage01) == 94, "lp_atten_stage01 at wrong offset"); +_Static_assert(offsetof(sdk_phy_info_t, pa_vdd) == 107, "pa_vdd aka vdd33_const at wrong offset"); +_Static_assert(offsetof(sdk_phy_info_t, rf_cal_mode) == 114, "rf_cal_use_flash at wrong offset!"); + +/* Read the default PHY info into the supplied structure. + + This function is weak-aliased to get_sdk_default_phy_info() so you + can replace it with your own if you want to vary the default values + - suggested way to do this is to call get_sdk_default_phy_info() + and then only update the fields you care about. + + The default PHY info is used at startup whenever the version field + in the default sdk_phy_info_t does not match the version field + stored in flash. So you can increment the version field to force a + reset to defaults, regardless of what values are in flash. +*/ +void get_default_phy_info(sdk_phy_info_t *info); + +/* Read the "SDK default" PHY info as used by the Espressif SDK */ +void get_sdk_default_phy_info(sdk_phy_info_t *info); + +/* Read the PHY info currently stored in the SPI flash SDK configuration sector. + + This PHY info is updated by the SDK following RF calibration, etc. + + Note that the saved data may be corrupt - read the 'version' field to verify. +*/ +void read_saved_phy_info(sdk_phy_info_t *info); + +/* Update the saved PHY info in the SPI flash. A reset is necessary to use these values. + + Note that the SDK may clobber these values, so it's recommended you reset ASAP after updating them. +*/ +void write_saved_phy_info(const sdk_phy_info_t *info); + +/* Dump known fields in the phy info structure to stdout, + if 'raw' flag is set then the raw hex values are also dumped. +*/ +void dump_phy_info(const sdk_phy_info_t *info, bool raw); + +#endif