470 lines
15 KiB
C
470 lines
15 KiB
C
/* Implementation of libmain/app_main.o from the Espressif SDK.
|
|
*
|
|
* This contains most of the startup code for the SDK/OS, some event handlers,
|
|
* etc.
|
|
*
|
|
* Part of esp-open-rtos
|
|
* Copyright (C) 2015 Superhouse Automation Pty Ltd
|
|
* BSD Licensed as described in the file LICENSE
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <FreeRTOS.h>
|
|
#include <task.h>
|
|
#include <lwip/tcpip.h>
|
|
|
|
#include "common_macros.h"
|
|
#include "xtensa_ops.h"
|
|
#include "esp/rom.h"
|
|
#include "esp/uart.h"
|
|
#include "esp/iomux_regs.h"
|
|
#include "esp/spi_regs.h"
|
|
#include "esp/dport_regs.h"
|
|
#include "esp/wdev_regs.h"
|
|
#include "esp/wdt_regs.h"
|
|
#include "esp/rtcmem_regs.h"
|
|
#include "esp/hwrand.h"
|
|
#include "os_version.h"
|
|
|
|
#include "espressif/esp_common.h"
|
|
#include "espressif/phy_info.h"
|
|
#include "esplibs/libmain.h"
|
|
#include "esplibs/libnet80211.h"
|
|
#include "esplibs/libphy.h"
|
|
#include "esplibs/libpp.h"
|
|
#include "sysparam.h"
|
|
|
|
/* This is not declared in any header file (but arguably should be) */
|
|
|
|
void user_init(void);
|
|
|
|
#define BOOT_INFO_SIZE 28
|
|
|
|
// 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
|
|
// there to display them in startup messages (not sure why it works that way).
|
|
#define RTCMEM_BACKUP_PHY_VER 31
|
|
#define RTCMEM_SYSTEM_PP_VER 62
|
|
|
|
extern uint32_t _bss_start;
|
|
extern uint32_t _bss_end;
|
|
|
|
// user_init_flag -- .bss+0x0
|
|
uint8_t sdk_user_init_flag;
|
|
|
|
// info -- .bss+0x4
|
|
struct sdk_info_st sdk_info;
|
|
|
|
// xUserTaskHandle -- .bss+0x28
|
|
TaskHandle_t sdk_xUserTaskHandle;
|
|
|
|
// xWatchDogTaskHandle -- .bss+0x2c
|
|
TaskHandle_t sdk_xWatchDogTaskHandle;
|
|
|
|
/* Static function prototypes */
|
|
|
|
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(sdk_phy_info_t *phy_info, uint8_t *mac_addr);
|
|
static void init_g_ic(void);
|
|
static void user_start_phase2(void);
|
|
static void dump_flash_sector(uint32_t start_sector, uint32_t length);
|
|
static void dump_flash_config_sectors(uint32_t start_sector);
|
|
|
|
// .Lfunc001 -- .text+0x14
|
|
static void IRAM get_otp_mac_address(uint8_t *buf) {
|
|
uint32_t otp_flags;
|
|
uint32_t otp_id0, otp_id1;
|
|
uint32_t otp_vendor_id;
|
|
|
|
otp_flags = DPORT.OTP_CHIPID;
|
|
otp_id1 = DPORT.OTP_MAC1;
|
|
otp_id0 = DPORT.OTP_MAC0;
|
|
if (!(otp_flags & 0x8000)) {
|
|
//FIXME: do we really need this check?
|
|
printf("Firmware ONLY supports ESP8266!!!\n");
|
|
abort();
|
|
}
|
|
if (otp_id0 == 0 && otp_id1 == 0) {
|
|
printf("empty otp\n");
|
|
abort();
|
|
}
|
|
if (otp_flags & 0x1000) {
|
|
// If bit 12 is set, it indicates that the vendor portion of the MAC
|
|
// address is stored in DPORT.OTP_MAC2.
|
|
otp_vendor_id = DPORT.OTP_MAC2;
|
|
buf[0] = otp_vendor_id >> 16;
|
|
buf[1] = otp_vendor_id >> 8;
|
|
buf[2] = otp_vendor_id;
|
|
} else {
|
|
// If bit 12 is clear, there's no MAC vendor in DPORT.OTP_MAC2, so
|
|
// default to the Espressif MAC vendor prefix instead.
|
|
buf[1] = 0xfe;
|
|
buf[0] = 0x18;
|
|
buf[2] = 0x34;
|
|
}
|
|
buf[3] = otp_id1 >> 8;
|
|
buf[4] = otp_id1;
|
|
buf[5] = otp_id0 >> 24;
|
|
}
|
|
|
|
// .Lfunc002 -- .text+0xa0
|
|
static void IRAM set_spi0_divisor(uint32_t divisor) {
|
|
int cycle_len, half_cycle_len, clkdiv;
|
|
|
|
if (divisor < 2) {
|
|
clkdiv = 0;
|
|
SPI(0).CTRL0 |= SPI_CTRL0_CLOCK_EQU_SYS_CLOCK;
|
|
IOMUX.CONF |= IOMUX_CONF_SPI0_CLOCK_EQU_SYS_CLOCK;
|
|
} else {
|
|
cycle_len = divisor - 1;
|
|
half_cycle_len = (divisor / 2) - 1;
|
|
clkdiv = VAL2FIELD(SPI_CTRL0_CLOCK_NUM, cycle_len)
|
|
| VAL2FIELD(SPI_CTRL0_CLOCK_HIGH, half_cycle_len)
|
|
| VAL2FIELD(SPI_CTRL0_CLOCK_LOW, cycle_len);
|
|
SPI(0).CTRL0 &= ~SPI_CTRL0_CLOCK_EQU_SYS_CLOCK;
|
|
IOMUX.CONF &= ~IOMUX_CONF_SPI0_CLOCK_EQU_SYS_CLOCK;
|
|
}
|
|
SPI(0).CTRL0 = SET_FIELD(SPI(0).CTRL0, SPI_CTRL0_CLOCK, clkdiv);
|
|
}
|
|
|
|
|
|
static void IRAM default_putc(char c) {
|
|
uart_putc(0, c);
|
|
}
|
|
|
|
// .text+0x258
|
|
void IRAM sdk_user_start(void) {
|
|
uint32_t buf32[sizeof(struct sdk_g_ic_saved_st) / 4];
|
|
uint8_t *buf8 = (uint8_t *)buf32;
|
|
uint32_t flash_speed_divisor;
|
|
uint32_t flash_sectors;
|
|
uint32_t flash_size;
|
|
int boot_slot;
|
|
uint32_t cksum_magic;
|
|
uint32_t cksum_len;
|
|
uint32_t cksum_value;
|
|
uint32_t ic_flash_addr;
|
|
uint32_t sysparam_addr;
|
|
sysparam_status_t status;
|
|
|
|
SPI(0).USER0 |= SPI_USER0_CS_SETUP;
|
|
sdk_SPIRead(0, buf32, 4);
|
|
|
|
switch (buf8[3] & 0x0f) {
|
|
case 0xf: // 80 MHz
|
|
flash_speed_divisor = 1;
|
|
break;
|
|
case 0x0: // 40 MHz
|
|
flash_speed_divisor = 2;
|
|
break;
|
|
case 0x1: // 26 MHz
|
|
flash_speed_divisor = 3;
|
|
break;
|
|
case 0x2: // 20 MHz
|
|
flash_speed_divisor = 4;
|
|
break;
|
|
default: // Invalid -- Assume 40 MHz
|
|
flash_speed_divisor = 2;
|
|
}
|
|
switch (buf8[3] >> 4) {
|
|
case 0x0: // 4 Mbit (512 KByte)
|
|
flash_sectors = 128;
|
|
break;
|
|
case 0x1: // 2 Mbit (256 Kbyte)
|
|
flash_sectors = 64;
|
|
break;
|
|
case 0x2: // 8 Mbit (1 Mbyte)
|
|
flash_sectors = 256;
|
|
break;
|
|
case 0x3: // 16 Mbit (2 Mbyte)
|
|
flash_sectors = 512;
|
|
break;
|
|
case 0x4: // 32 Mbit (4 Mbyte)
|
|
flash_sectors = 1024;
|
|
break;
|
|
default: // Invalid -- Assume 4 Mbit (512 KByte)
|
|
flash_sectors = 128;
|
|
}
|
|
//FIXME: we should probably calculate flash_sectors by starting with flash_size and dividing by sdk_flashchip.sector_size instead of vice-versa.
|
|
flash_size = flash_sectors * 4096;
|
|
sdk_flashchip.chip_size = flash_size;
|
|
set_spi0_divisor(flash_speed_divisor);
|
|
sdk_SPIRead(flash_size - 4096, buf32, BOOT_INFO_SIZE);
|
|
boot_slot = buf8[0] ? 1 : 0;
|
|
cksum_magic = buf32[1];
|
|
cksum_len = buf32[3 + boot_slot];
|
|
cksum_value = buf32[5 + boot_slot];
|
|
ic_flash_addr = (flash_sectors - 3 + boot_slot) * sdk_flashchip.sector_size;
|
|
sdk_SPIRead(ic_flash_addr, buf32, sizeof(struct sdk_g_ic_saved_st));
|
|
Cache_Read_Enable(0, 0, 1);
|
|
zero_bss();
|
|
sdk_os_install_putc1(default_putc);
|
|
if (cksum_magic == 0xffffffff) {
|
|
// No checksum required
|
|
} else if ((cksum_magic == 0x55aa55aa) &&
|
|
(sdk_system_get_checksum(buf8, cksum_len) == cksum_value)) {
|
|
// Checksum OK
|
|
} else {
|
|
// Bad checksum or bad cksum_magic value
|
|
dump_flash_config_sectors(flash_sectors - 4);
|
|
//FIXME: should we halt here? (original SDK code doesn't)
|
|
}
|
|
memcpy(&sdk_g_ic.s, buf32, sizeof(struct sdk_g_ic_saved_st));
|
|
|
|
// By default, put the sysparam region just below the config sectors at the
|
|
// top of the flash space, and allowing one extra sector spare.
|
|
sysparam_addr = flash_size - (5 + DEFAULT_SYSPARAM_SECTORS) * sdk_flashchip.sector_size;
|
|
status = sysparam_init(sysparam_addr, flash_size);
|
|
if (status == SYSPARAM_NOTFOUND) {
|
|
status = sysparam_create_area(sysparam_addr, DEFAULT_SYSPARAM_SECTORS, false);
|
|
if (status == SYSPARAM_OK) {
|
|
status = sysparam_init(sysparam_addr, 0);
|
|
}
|
|
}
|
|
if (status != SYSPARAM_OK) {
|
|
printf("WARNING: Could not initialize sysparams (%d)!\n", status);
|
|
}
|
|
|
|
user_start_phase2();
|
|
}
|
|
|
|
// .text+0x3a8
|
|
void IRAM vApplicationStackOverflowHook(TaskHandle_t task, char *task_name) {
|
|
printf("Task stack overflow (high water mark=%lu name=\"%s\")\n", uxTaskGetStackHighWaterMark(task), task_name);
|
|
}
|
|
|
|
// .text+0x3d8
|
|
void __attribute__((weak)) IRAM vApplicationIdleHook(void) {
|
|
printf("idle %u\n", WDEV.SYS_TIME);
|
|
}
|
|
|
|
// .text+0x404
|
|
void __attribute__((weak)) IRAM vApplicationTickHook(void) {
|
|
printf("tick %u\n", WDEV.SYS_TIME);
|
|
}
|
|
|
|
// .Lfunc005 -- .irom0.text+0x8
|
|
static void zero_bss(void) {
|
|
uint32_t *addr;
|
|
|
|
for (addr = &_bss_start; addr < &_bss_end; addr++) {
|
|
*addr = 0;
|
|
}
|
|
}
|
|
|
|
// .Lfunc006 -- .irom0.text+0x70
|
|
static void init_networking(sdk_phy_info_t *phy_info, uint8_t *mac_addr) {
|
|
// The call to sdk_register_chipv6_phy appears to change the bus clock,
|
|
// perhaps from 40MHz to 26MHz, at least it has such an effect on the uart
|
|
// baud rate. The caller flushes the TX fifos.
|
|
if (sdk_register_chipv6_phy(phy_info)) {
|
|
printf("FATAL: sdk_register_chipv6_phy failed");
|
|
abort();
|
|
}
|
|
|
|
// The boot rom initializes uart0 for a 115200 baud rate but the bus clock
|
|
// does not appear to be as expected so the initial baud rate is actually
|
|
// 74906. On a cold boot, to keep the 74906 baud rate the uart0 divisor
|
|
// would need to changed here to 74906. On a warm boot the bus clock is
|
|
// expected to have already been set so the boot baud rate is 115200.
|
|
// Reset the rate here and settle on a 115200 baud rate.
|
|
if (sdk_rst_if.reason > 0) {
|
|
uart_set_baud(0, 115200);
|
|
uart_set_baud(1, 115200);
|
|
}
|
|
|
|
sdk_phy_disable_agc();
|
|
sdk_ieee80211_phy_init(sdk_g_ic.s.phy_mode);
|
|
sdk_lmacInit();
|
|
sdk_wDev_Initialize();
|
|
sdk_pp_attach();
|
|
sdk_ieee80211_ifattach(&sdk_g_ic, mac_addr);
|
|
_xt_isr_mask(1);
|
|
DPORT.DPORT0 = SET_FIELD(DPORT.DPORT0, DPORT_DPORT0_FIELD0, 1);
|
|
sdk_pm_attach();
|
|
sdk_phy_enable_agc();
|
|
sdk_cnx_attach(&sdk_g_ic);
|
|
sdk_wDevEnableRx();
|
|
}
|
|
|
|
// .Lfunc007 -- .irom0.text+0x148
|
|
static void init_g_ic(void) {
|
|
if (sdk_g_ic.s.wifi_mode == 0xff) {
|
|
sdk_g_ic.s.wifi_mode = 2;
|
|
}
|
|
sdk_wifi_softap_set_default_ssid();
|
|
if (sdk_g_ic.s._unknown30d < 1 || sdk_g_ic.s._unknown30d > 14) {
|
|
sdk_g_ic.s._unknown30d = 1;
|
|
}
|
|
if (sdk_g_ic.s._unknown544 < 100 || sdk_g_ic.s._unknown544 > 60000) {
|
|
sdk_g_ic.s._unknown544 = 100;
|
|
}
|
|
if (sdk_g_ic.s._unknown30e == 1 || sdk_g_ic.s._unknown30e > 4) {
|
|
sdk_g_ic.s._unknown30e = 0;
|
|
}
|
|
bzero(sdk_g_ic.s._unknown2ac, sizeof(sdk_g_ic.s._unknown2ac));
|
|
if (sdk_g_ic.s._unknown30f > 1) {
|
|
sdk_g_ic.s._unknown30f = 0;
|
|
}
|
|
if (sdk_g_ic.s._unknown310 > 4) {
|
|
sdk_g_ic.s._unknown310 = 4;
|
|
}
|
|
if (sdk_g_ic.s.sta_ssid.ssid_length == 0xffffffff) {
|
|
bzero(&sdk_g_ic.s.sta_ssid, sizeof(sdk_g_ic.s.sta_ssid));
|
|
bzero(&sdk_g_ic.s.sta_password, sizeof(sdk_g_ic.s.sta_password));
|
|
}
|
|
sdk_g_ic.s.wifi_led_enable = 0;
|
|
if (sdk_g_ic.s.sta_bssid_set > 1) {
|
|
sdk_g_ic.s.sta_bssid_set = 0;
|
|
}
|
|
if (sdk_g_ic.s.ap_number > 5) {
|
|
sdk_g_ic.s.ap_number = 1;
|
|
}
|
|
if (sdk_g_ic.s.phy_mode < 1 || sdk_g_ic.s.phy_mode > 3) {
|
|
sdk_g_ic.s.phy_mode = PHY_MODE_11N;
|
|
}
|
|
}
|
|
|
|
// .irom0.text+0x398
|
|
void sdk_wdt_init(void) {
|
|
WDT.CTRL &= ~WDT_CTRL_ENABLE;
|
|
DPORT.INT_ENABLE |= DPORT_INT_ENABLE_WDT;
|
|
WDT.REG1 = 0x0000000b;
|
|
WDT.REG2 = 0x0000000c;
|
|
WDT.CTRL |= WDT_CTRL_FLAG3 | WDT_CTRL_FLAG4 | WDT_CTRL_FLAG5;
|
|
WDT.CTRL = SET_FIELD(WDT.CTRL, WDT_CTRL_FIELD0, 0);
|
|
WDT.CTRL |= WDT_CTRL_ENABLE;
|
|
sdk_pp_soft_wdt_init();
|
|
}
|
|
|
|
// .irom0.text+0x474
|
|
void sdk_user_init_task(void *params) {
|
|
int phy_ver, pp_ver;
|
|
|
|
sdk_ets_timer_init();
|
|
printf("\nESP-Open-SDK ver: %s compiled @ %s %s\n", OS_VERSION_STR, __DATE__, __TIME__);
|
|
phy_ver = RTCMEM_BACKUP[RTCMEM_BACKUP_PHY_VER] >> 16;
|
|
printf("phy ver: %d, ", phy_ver);
|
|
pp_ver = RTCMEM_SYSTEM[RTCMEM_SYSTEM_PP_VER];
|
|
printf("pp ver: %d.%d\n\n", (pp_ver >> 8) & 0xff, pp_ver & 0xff);
|
|
user_init();
|
|
sdk_user_init_flag = 1;
|
|
sdk_wifi_mode_set(sdk_g_ic.s.wifi_mode);
|
|
if (sdk_g_ic.s.wifi_mode == 1) {
|
|
sdk_wifi_station_start();
|
|
netif_set_default(sdk_g_ic.v.station_netif_info->netif);
|
|
}
|
|
if (sdk_g_ic.s.wifi_mode == 2) {
|
|
sdk_wifi_softap_start();
|
|
netif_set_default(sdk_g_ic.v.softap_netif_info->netif);
|
|
}
|
|
if (sdk_g_ic.s.wifi_mode == 3) {
|
|
sdk_wifi_station_start();
|
|
sdk_wifi_softap_start();
|
|
netif_set_default(sdk_g_ic.v.softap_netif_info->netif);
|
|
}
|
|
if (sdk_wifi_station_get_auto_connect()) {
|
|
sdk_wifi_station_connect();
|
|
}
|
|
vTaskDelete(NULL);
|
|
}
|
|
|
|
extern void (*__init_array_start)(void);
|
|
extern void (*__init_array_end)(void);
|
|
|
|
// .Lfunc009 -- .irom0.text+0x5b4
|
|
static __attribute__((noinline)) void user_start_phase2(void) {
|
|
uint8_t *buf;
|
|
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.reason > 3) {
|
|
// Bad reason. Probably garbage.
|
|
bzero(&sdk_rst_if, sizeof(sdk_rst_if));
|
|
}
|
|
buf = malloc(sizeof(sdk_rst_if));
|
|
bzero(buf, sizeof(sdk_rst_if));
|
|
sdk_system_rtc_mem_write(0, buf, sizeof(sdk_rst_if));
|
|
free(buf);
|
|
sdk_sleep_reset_analog_rtcreg_8266();
|
|
get_otp_mac_address(sdk_info.sta_mac_addr);
|
|
sdk_wifi_softap_cacl_mac(sdk_info.softap_mac_addr, sdk_info.sta_mac_addr);
|
|
sdk_info.softap_ipaddr.addr = 0x0104a8c0; // 192.168.4.1
|
|
sdk_info.softap_netmask.addr = 0x00ffffff; // 255.255.255.0
|
|
sdk_info.softap_gw.addr = 0x0104a8c0; // 192.168.4.1
|
|
init_g_ic();
|
|
|
|
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);
|
|
// Wait for UARTs to finish sending anything in their queues.
|
|
uart_flush_txfifo(0);
|
|
uart_flush_txfifo(1);
|
|
|
|
init_networking(&phy_info, sdk_info.sta_mac_addr);
|
|
|
|
srand(hwrand()); /* seed libc rng */
|
|
|
|
// Set intial CPU clock speed to 160MHz if necessary
|
|
_Static_assert(configCPU_CLOCK_HZ == 80000000 || configCPU_CLOCK_HZ == 160000000, "FreeRTOSConfig must define initial clock speed as either 80MHz or 160MHz");
|
|
sdk_system_update_cpu_freq(configCPU_CLOCK_HZ / 1000000);
|
|
|
|
// Call gcc constructor functions
|
|
void (**ctor)(void);
|
|
for ( ctor = &__init_array_start; ctor != &__init_array_end; ++ctor) {
|
|
(*ctor)();
|
|
}
|
|
|
|
tcpip_init(NULL, NULL);
|
|
sdk_wdt_init();
|
|
xTaskCreate(sdk_user_init_task, "uiT", 1024, 0, 14, &sdk_xUserTaskHandle);
|
|
vTaskStartScheduler();
|
|
}
|
|
|
|
// .Lfunc010 -- .irom0.text+0x710
|
|
static void dump_flash_sector(uint32_t start_sector, uint32_t length) {
|
|
uint8_t *buf;
|
|
int bufsize, i;
|
|
|
|
bufsize = (length + 3) & 0xfffc;
|
|
buf = malloc(bufsize);
|
|
sdk_spi_flash_read(start_sector * sdk_flashchip.sector_size, (uint32_t *)buf
|
|
, bufsize);
|
|
for (i = 0; i < length; i++) {
|
|
if ((i & 0xf) == 0) {
|
|
if (i) {
|
|
printf("\n");
|
|
}
|
|
printf("%04x:", i);
|
|
}
|
|
printf(" %02x", buf[i]);
|
|
}
|
|
printf("\n");
|
|
free(buf);
|
|
}
|
|
|
|
// .Lfunc011 -- .irom0.text+0x790
|
|
static __attribute__((noinline)) void dump_flash_config_sectors(uint32_t start_sector) {
|
|
printf("system param error\n");
|
|
// Note: original SDK code didn't dump PHY info
|
|
printf("phy_info:\n");
|
|
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");
|
|
dump_flash_sector(start_sector + 2, sizeof(struct sdk_g_ic_saved_st));
|
|
printf("\nboot info:\n");
|
|
dump_flash_sector(start_sector + 3, BOOT_INFO_SIZE);
|
|
}
|
|
|