Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Johan Kanflo 2015-10-05 21:18:34 +02:00
commit 20a3b4c0cd
101 changed files with 8203 additions and 1133 deletions

510
core/app_main.c Normal file
View file

@ -0,0 +1,510 @@
/* 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/iomux_regs.h"
#include "esp/uart_regs.h"
#include "esp/spi_regs.h"
#include "esp/dport_regs.h"
#include "esp/wdev_regs.h"
#include "os_version.h"
#include "espressif/esp_common.h"
#include "sdk_internal.h"
/* This is not declared in any header file (but arguably should be) */
void user_init(void);
#define UART_DIVISOR 1068 // 74906 bps (yeah, I don't understand it either)
#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
// 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
#define halt() while (1) {}
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;
// info -- .bss+0x4
struct sdk_info_st sdk_info;
// xUserTaskHandle -- .bss+0x28
xTaskHandle sdk_xUserTaskHandle;
// xWatchDogTaskHandle -- .bss+0x2c
xTaskHandle 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 int IRAM default_putc(char c);
static void IRAM default_putc1(char c);
static void zero_bss(void);
static void init_networking(uint8_t *phy_info, uint8_t *mac_addr);
static void init_g_ic(void);
static void dump_excinfo(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");
halt();
}
if (otp_id0 == 0 && otp_id1 == 0) {
printf("empty otp\n");
halt();
}
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);
}
// .text+0x148
void IRAM sdk_user_fatal_exception_handler(void) {
if (!sdk_NMIIrqIsOn) {
vPortEnterCritical();
do {
DPORT.DPORT0 &= 0xffffffe0;
} while (DPORT.DPORT0 & 0x00000001);
}
Cache_Read_Disable();
Cache_Read_Enable(0, 0, 1);
dump_excinfo();
while (FIELD2VAL(UART_STATUS_TXFIFO_COUNT, UART(0).STATUS)) {}
while (FIELD2VAL(UART_STATUS_TXFIFO_COUNT, UART(1).STATUS)) {}
sdk_system_restart_in_nmi();
halt();
}
// .Lfunc003 -- .text+0x1d0
static int IRAM default_putc(char c) {
while (FIELD2VAL(UART_STATUS_TXFIFO_COUNT, UART(0).STATUS) > 125) {}
UART(0).FIFO = c;
return 0;
}
// .Lfunc004 -- .text+0x1f4
static void IRAM default_putc1(char c) {
if (c == '\n') {
default_putc('\r');
} else if (c == '\r') {
return;
}
default_putc(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;
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_putc1);
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));
user_start_phase2();
}
// .text+0x3a8
void IRAM vApplicationStackOverflowHook(xTaskHandle task, char *task_name) {
printf("\"%s\"(stack_size = %lu) overflow the heap_size.\n", task_name, uxTaskGetStackHighWaterMark(task));
}
// .text+0x3d8
void IRAM vApplicationIdleHook(void) {
printf("idle %u\n", WDEV.SYS_TIME);
}
// .text+0x404
void 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(uint8_t *phy_info, uint8_t *mac_addr) {
if (sdk_register_chipv6_phy(phy_info)) {
printf("FATAL: sdk_register_chipv6_phy failed");
halt();
}
sdk_uart_div_modify(0, UART_DIVISOR);
sdk_uart_div_modify(1, UART_DIVISOR);
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._unknown1e4._unknown1e4 == 0xffffffff) {
bzero(&sdk_g_ic.s._unknown1e4, sizeof(sdk_g_ic.s._unknown1e4));
bzero(&sdk_g_ic.s._unknown20f, sizeof(sdk_g_ic.s._unknown20f));
}
sdk_g_ic.s.wifi_led_enable = 0;
if (sdk_g_ic.s._unknown281 > 1) {
sdk_g_ic.s._unknown281 = 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;
}
}
// .Lfunc008 -- .irom0.text+0x2a0
static void dump_excinfo(void) {
uint32_t exccause, epc1, epc2, epc3, excvaddr, depc, excsave1;
uint32_t excinfo[8];
RSR(exccause, exccause);
printf("Fatal exception (%d): \n", (int)exccause);
RSR(epc1, epc1);
RSR(epc2, epc2);
RSR(epc3, epc3);
RSR(excvaddr, excvaddr);
RSR(depc, depc);
RSR(excsave1, excsave1);
printf("%s=0x%08x\n", "epc1", epc1);
printf("%s=0x%08x\n", "epc2", epc2);
printf("%s=0x%08x\n", "epc3", epc3);
printf("%s=0x%08x\n", "excvaddr", excvaddr);
printf("%s=0x%08x\n", "depc", depc);
printf("%s=0x%08x\n", "excsave1", excsave1);
sdk_system_rtc_mem_read(0, excinfo, 32); // Why?
excinfo[0] = 2;
excinfo[1] = exccause;
excinfo[2] = epc1;
excinfo[3] = epc2;
excinfo[4] = epc3;
excinfo[5] = excvaddr;
excinfo[6] = depc;
excinfo[7] = excsave1;
sdk_system_rtc_mem_write(0, excinfo, 32);
}
// .irom0.text+0x368
int sdk_uart_rx_one_char(char *buf) {
if (FIELD2VAL(UART_STATUS_RXFIFO_COUNT, UART(0).STATUS)) {
*buf = UART(0).FIFO;
return 0;
}
return 1;
}
// .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);
}
// .Lfunc009 -- .irom0.text+0x5b4
static void user_start_phase2(void) {
uint8_t *buf;
uint8_t *phy_info;
sdk_system_rtc_mem_read(0, &sdk_rst_if, sizeof(sdk_rst_if));
if (sdk_rst_if.version > 3) {
// Bad version number. 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._unknown0 = 0x0104a8c0;
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);
// Wait for UARTs to finish sending anything in their queues.
while (FIELD2VAL(UART_STATUS_TXFIFO_COUNT, UART(0).STATUS) > 0) {}
while (FIELD2VAL(UART_STATUS_TXFIFO_COUNT, UART(1).STATUS) > 0) {}
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);
tcpip_init(NULL, NULL);
sdk_wdt_init();
xTaskCreate(sdk_user_init_task, (signed char *)"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 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, PHY_INFO_SIZE);
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);
}

View file

@ -1,6 +1,6 @@
INC_DIRS += $(core_ROOT)include
# args for passing into compile rule generation
core_ROOT = $(ROOT)core/
core_SRC_DIR = $(core_ROOT)
$(eval $(call component_compile_rules,core))

View file

@ -0,0 +1,25 @@
/* Part of esp-open-rtos
* BSD Licensed as described in the file LICENSE
*/
#include <stdio.h>
#include <stdlib.h>
void *operator new(size_t size)
{
return malloc(size);
}
void *operator new[](size_t size)
{
return malloc(size);
}
void operator delete(void * ptr)
{
free(ptr);
}
void operator delete[](void * ptr)
{
free(ptr);
}

27
core/esp_hwrand.c Normal file
View file

@ -0,0 +1,27 @@
/* Hardware Random Number Generator Functions
*
* For documentation, see http://esp8266-re.foogod.com/wiki/Random_Number_Generator
*
* Part of esp-open-rtos
* Copyright (C) 2015 Angus Gratton
* BSD Licensed as described in the file LICENSE
*/
#include <esp/hwrand.h>
#include <esp/wdev_regs.h>
#include <string.h>
/* Return a random 32-bit number */
uint32_t hwrand(void)
{
return WDEV.HWRNG;
}
/* Fill a variable size buffer with data from the Hardware RNG */
void hwrand_fill(uint8_t *buf, size_t len)
{
for(size_t i = 0; i < len; i+=4) {
uint32_t random = WDEV.HWRNG;
/* using memcpy here in case 'buf' is unaligned */
memcpy(buf + i, &random, (i+4 <= len) ? 4 : (len % 4));
}
}

View file

@ -1,256 +1,541 @@
/* Xtensa Exception (ie interrupt) Vectors & low-level handler code
*
* Core exception handler code is placed in the .vecbase section, which gets
* picked up specially in the linker script and placed at beginning of IRAM.
*
* The actual VecBase symbol should be the first thing in .vecbase (this is not
* strictly important as it gets set by symbol lookup not by hardcoded address,
* but having it at 0x40100000 means that the exception vectors have memorable
* offsets, which match the default Boot ROM vector offsets. So convenient for
* human understanding.
*
* Part of esp-open-rtos
* Original vector contents Copyright (C) 2014-2015 Espressif Systems
* Additions Copyright (C) Superhouse Automation Pty Ltd and Angus Gratton
* BSD Licensed as described in the file LICENSE
*/
Core exception handler code is placed in the .vecbase section,
which gets picked up specially in the linker script and placed
at beginning of IRAM.
#include "led_debug.s"
The actual VecBase symbol should be the first thing in .vecbase
(this is not strictly important as it gets set by symbol lookup not
by hardcoded address, but having it at 0x40100000 means that the
exception vectors have memorable offsets, which match the default
Boot ROM vector offsets. So convenient for human understanding.
/* Some UserException causes, see table Table 464 in ISA reference */
Part of esp-open-rtos
Original vector contents Copyright (C) 2014-2015 Espressif Systems
Additions Copyright (C) Superhouse Automation Pty Ltd
BSD Licensed as described in the file LICENSE
*/
.text
.section .vecbase.text, "x"
.align 256
.global VecBase
.type VecBase, @function /* it's not really a function, but treat it like one */
#define CAUSE_SYSCALL 1
#define CAUSE_LOADSTORE 3
#define CAUSE_LVL1INT 4
.section .bss
NMIHandlerStack: # stack space for NMI handler
.skip 4*0x100
.LNMIHandlerStackTop:
NMIRegisterSaved: # register space for saving NMI registers
.skip 4*(16 + 6)
LoadStoreErrorHandlerStack:
.word 0 # a0
.word 0 # (unused)
.word 0 # a2
.word 0 # a3
.word 0 # a4
/***************************** Exception Vectors *****************************/
.section .vecbase.text, "x"
/* Note: Exception vectors must be aligned on a 256-byte (0x100) boundary or
* they will not function properly. (This is taken care of in the linker
* script by ensuring .vecbase.text is aligned properly, and putting VecBase
* right at the beginning of .vecbase.text) */
.org 0
VecBase:
/* IMPORTANT: exception vector literals will go here, but we
can't have more than 4 otherwise we push DebugExceptionVector past
offset 0x10 relative to VecBase. There should be ways to avoid this,
and also keep the VecBase offsets easy to read, but this works for now.
*/
.literal_position
.align 16
.global VecBase
/* IMPORTANT: exception vector literals will go here, but we
* can't have more than 4 otherwise we push DebugExceptionVector past
* offset 0x10 relative to VecBase. There should be ways to avoid this,
* and also keep the VecBase offsets easy to read, but this works for
* now. */
.literal_position
.org VecBase + 0x10
DebugExceptionVector:
wsr.excsave2 a0
call0 sdk_user_fatal_exception_handler
rfi 2
.align 16
.type DebugExceptionVector, @function
wsr a0, excsave2
call0 sdk_user_fatal_exception_handler
rfi 2
.org VecBase + 0x20
NMIExceptionVector:
wsr.excsave3 a0
call0 CallNMIExceptionHandler
rfi 3 /* CallNMIExceptionHandler should call rfi itself */
.align 16
.type NMIExceptionVector, @function
wsr a0, excsave3
call0 CallNMIExceptionHandler
rfi 3 # Should never be reached
.org VecBase + 0x30
KernelExceptionVector:
break 1, 0
call0 sdk_user_fatal_exception_handler
rfe
.align 16
.L_EmptyVectorEntry:
nop
.align 16
.type KernelExceptionVector, @function
break 1, 0
call0 sdk_user_fatal_exception_handler
rfe
.org VecBase + 0x50
UserExceptionVector:
wsr.excsave1 a0
call0 CallUserExceptionHandler
rfe /* CallUserExceptionHandler should call rfe itself */
.align 16
.L_EmptyVectorEntry2:
nop
.align 16
.type UserExceptionVector, @function
wsr a1, excsave1
rsr a1, exccause
beqi a1, CAUSE_LOADSTORE, LoadStoreErrorHandler
j UserExceptionHandler
.org VecBase + 0x70
DoubleExceptionVector:
break 1, 4
call0 sdk_user_fatal_exception_handler
.align 16
.L_UnusedResetVector:
/* reset vector slot doesn't get used, as vecbase goes back to mask ROM on reset */
nop
.type DoubleExceptionVector, @function
.section .bss
NMIHandlerStack: /* stack space for NMI handler */
.skip 4*0x100
NMIRegisterSaved: /* register space for saving NMI registers */
.skip 4*(0x16 + 6)
break 1, 4
call0 sdk_user_fatal_exception_handler
/* this symbol is _Pri_3_HandlerAddress in the RTOS SDK, appears totally
unused (stays zero at all times) */
.global NMIHandlerAddress
NMIHandlerAddress:
.long 0
/* Reset vector at offset 0x80 is unused, as vecbase gets reset to mask ROM
* vectors on chip reset. */
/*************************** LoadStoreError Handler **************************/
.section .vecbase.text, "x"
/* Xtensa "Load/Store Exception" handler:
* Completes L8/L16 load instructions from Instruction address space, for which
* the architecture only supports 32-bit reads.
*
* Called from UserExceptionVector if EXCCAUSE is LoadStoreErrorCause
*
* (Fast path (no branches) is for L8UI)
*/
.literal_position
.balign 4
LoadStoreErrorHandler:
.type LoadStoreErrorHandler, @function
/* Registers are saved in the address corresponding to their register
* number times 4. This allows a quick and easy mapping later on when
* needing to store the value to a particular register number. */
movi sp, LoadStoreErrorHandlerStack
s32i a0, sp, 0
s32i a2, sp, 0x08
s32i a3, sp, 0x0c
s32i a4, sp, 0x10
rsr a0, sar # Save SAR in a0 to restore later
/* Examine the opcode which generated the exception */
/* Note: Instructions are in this order to avoid pipeline stalls. */
rsr a2, epc1
movi a3, ~3
ssa8l a2 # sar is now correct shift for aligned read
and a2, a2, a3 # a2 now 4-byte aligned address of instruction
l32i a4, a2, 0
l32i a2, a2, 4
movi a3, 0x00700F # opcode mask for l8ui/l16si/l16ui
src a2, a2, a4 # a2 now instruction that failed
and a3, a2, a3 # a3 is masked instruction
bnei a3, 0x000002, .LSE_check_l16
/* Note: At this point, opcode could technically be one of two things:
* xx0xx2 (L8UI)
* xx8xx2 (Reserved (invalid) opcode)
* It is assumed that we'll never get to this point from an illegal
* opcode, so we don't bother to check for that case and presume this
* is always an L8UI. */
movi a4, ~3
rsr a3, excvaddr # read faulting address
and a4, a3, a4 # a4 now word aligned read address
l32i a4, a4, 0 # perform the actual read
ssa8l a3 # sar is now shift to extract a3's byte
srl a3, a4 # shift right correct distance
extui a4, a3, 0, 8 # mask off bits we need for an l8
.LSE_post_fetch:
/* We jump back here after either the L8UI or the L16*I routines do the
* necessary work to read the value from memory.
* At this point, a2 holds the faulting instruction and a4 holds the
* correctly read value.
* Restore original SAR value (saved in a0) and update EPC so we'll
* return back to the instruction following the one we just emulated */
/* Note: Instructions are in this order to avoid pipeline stalls */
rsr a3, epc1
wsr a0, sar
addi a3, a3, 0x3
wsr a3, epc1
/* Stupid opcode tricks: The jumptable we use later on needs 16 bytes
* per entry (so we can avoid a second jump by just doing a RFE inside
* each entry). Unfortunately, however, Xtensa doesn't have an addx16
* operation to make that easy for us. Luckily, all of the faulting
* opcodes we're processing are guaranteed to have bit 3 be zero, which
* means if we just shift the register bits of the opcode down by 3
* instead of 4, we will get the register number multiplied by 2. This
* combined with an addx8 will give us an effective addx16 without
* needing any extra shift operations. */
extui a2, a2, 3, 5 # a2 is now destination register 0-15 times 2
bgei a2, 10, .LSE_assign_reg # a5..a15 use jumptable
beqi a2, 2, .LSE_assign_a1 # a1 uses a special routine
/* We're storing into a0 or a2..a4, which are all saved in our "stack"
* area. Calculate the correct address and stick the value in there,
* then just do our normal restore and RFE (no jumps required, which
* actually makes a0..a4 substantially faster). */
addx2 a2, a2, sp
s32i a4, a2, 0
/* Restore all regs and return */
l32i a0, sp, 0
l32i a2, sp, 0x08
l32i a3, sp, 0x0c
l32i a4, sp, 0x10
rsr a1, excsave1 # restore a1 saved by UserExceptionVector
rfe
.LSE_assign_reg:
/* At this point, a2 contains the register number times 2, a4 is the
* read value. */
/* Calculate the jumptable address, and restore all regs except a2 and
* a4 so we have less to do after jumping. */
/* Note: Instructions are in this order to avoid pipeline stalls. */
movi a3, .LSE_jumptable_base
l32i a0, sp, 0
addx8 a2, a2, a3 # a2 is now the address to jump to
l32i a3, sp, 0x0c
jx a2
.balign 4
.LSE_check_l16:
/* At this point, a2 contains the opcode, a3 is masked opcode */
movi a4, 0x001002 # l16si or l16ui opcode after masking
bne a3, a4, .LSE_wrong_opcode
/* Note: At this point, the opcode could be one of two things:
* xx1xx2 (L16UI)
* xx9xx2 (L16SI)
* Both of these we can handle. */
movi a4, ~3
rsr a3, excvaddr # read faulting address
and a4, a3, a4 # a4 now word aligned read address
l32i a4, a4, 0 # perform the actual read
ssa8l a3 # sar is now shift to extract a3's bytes
srl a3, a4 # shift right correct distance
extui a4, a3, 0, 16 # mask off bits we need for an l16
bbci a2, 15, .LSE_post_fetch # Not a signed op
bbci a4, 15, .LSE_post_fetch # Value does not need sign-extension
movi a3, 0xFFFF0000
or a4, a3, a4 # set 32-bit sign bits
j .LSE_post_fetch
.LSE_wrong_opcode:
/* If we got here it's not an opcode we can try to fix, so bomb out.
* Restore registers so any dump the fatal exception routine produces
* will have correct values */
wsr a0, sar
l32i a0, sp, 0
l32i a2, sp, 0x08
l32i a3, sp, 0x0c
l32i a4, sp, 0x10
rsr a1, excsave1
call0 sdk_user_fatal_exception_handler
.balign 4
.LSE_assign_a1:
/* a1 is saved in excsave1, so just update that with the value, */
wsr a4, excsave1
/* Then restore all regs and return */
l32i a0, sp, 0
l32i a2, sp, 0x08
l32i a3, sp, 0x0c
l32i a4, sp, 0x10
rsr a1, excsave1
rfe
.balign 4
.LSE_jumptable:
/* The first 5 entries (80 bytes) of this table are unused (registers
* a0..a4 are handled separately above). Rather than have a whole bunch
* of wasted space, we just pretend that the table starts 80 bytes
* earlier in memory. */
.set .LSE_jumptable_base, .LSE_jumptable - (16 * 5)
.org .LSE_jumptable_base + (16 * 5)
mov a5, a4
l32i a2, sp, 0x08
l32i a4, sp, 0x10
rsr a1, excsave1
rfe
.org .LSE_jumptable_base + (16 * 6)
mov a6, a4
l32i a2, sp, 0x08
l32i a4, sp, 0x10
rsr a1, excsave1
rfe
.org .LSE_jumptable_base + (16 * 7)
mov a7, a4
l32i a2, sp, 0x08
l32i a4, sp, 0x10
rsr a1, excsave1
rfe
.org .LSE_jumptable_base + (16 * 8)
mov a8, a4
l32i a2, sp, 0x08
l32i a4, sp, 0x10
rsr a1, excsave1
rfe
.org .LSE_jumptable_base + (16 * 9)
mov a9, a4
l32i a2, sp, 0x08
l32i a4, sp, 0x10
rsr a1, excsave1
rfe
.org .LSE_jumptable_base + (16 * 10)
mov a10, a4
l32i a2, sp, 0x08
l32i a4, sp, 0x10
rsr a1, excsave1
rfe
.org .LSE_jumptable_base + (16 * 11)
mov a11, a4
l32i a2, sp, 0x08
l32i a4, sp, 0x10
rsr a1, excsave1
rfe
.org .LSE_jumptable_base + (16 * 12)
mov a12, a4
l32i a2, sp, 0x08
l32i a4, sp, 0x10
rsr a1, excsave1
rfe
.org .LSE_jumptable_base + (16 * 13)
mov a13, a4
l32i a2, sp, 0x08
l32i a4, sp, 0x10
rsr a1, excsave1
rfe
.org .LSE_jumptable_base + (16 * 14)
mov a14, a4
l32i a2, sp, 0x08
l32i a4, sp, 0x10
rsr a1, excsave1
rfe
.org .LSE_jumptable_base + (16 * 15)
mov a15, a4
l32i a2, sp, 0x08
l32i a4, sp, 0x10
rsr a1, excsave1
rfe
/****************************** call_user_start ******************************/
.section .vecbase.text, "x"
/* This is the first entrypoint called from the ROM after loading the image
* into IRAM. It just sets up the VECBASE register to point at our own
* exception vectors and then calls sdk_user_start() */
.literal_position
.balign 4
call_user_start:
.global call_user_start
.type call_user_start, @function
movi a2, VecBase
wsr a2, vecbase
call0 sdk_user_start
/*************************** NMI Exception Handler ***************************/
.section .vecbase.text, "x"
/* Save register relative to a0 */
.macro SAVE_REG register, regnum
s32i \register, a0, (0x20 + 4 * \regnum)
s32i \register, a0, (4 * (\regnum + 6))
.endm
/* Load register relative to sp */
.macro LOAD_REG register, regnum
l32i \register, sp, (0x20 + 4 * \regnum)
l32i \register, sp, (4 * (\regnum + 6))
.endm
.text
.section .vecbase.text
.literal_position
.align 4
.global call_user_start
.type call_user_start, @function
call_user_start:
movi a2, VecBase
wsr.vecbase a2
call0 sdk_user_start
.literal_position
.literal_position
.align 16
.type CallNMIExceptionHandler, @function
.balign 16
CallNMIExceptionHandler:
movi a0, NMIRegisterSaved
SAVE_REG a2, 2
movi a2, NMIHandlerAddress
l32i a2, a2, 0
SAVE_REG sp, 1
SAVE_REG a3, 3
xsr.excsave3 a2 /* excsave3 is now NMIHandlerAddress, a2 is former a0 */
SAVE_REG a4, 4
SAVE_REG a2, 0
rsr.epc1 a3
rsr.exccause a4
SAVE_REG a3, -5
SAVE_REG a4, -4
rsr.excvaddr a3
SAVE_REG a3, -3
rsr.excsave1 a3
SAVE_REG a3, -2
SAVE_REG a5, 5
SAVE_REG a6, 6
SAVE_REG a7, 7
SAVE_REG a8, 8
SAVE_REG a9, 9
SAVE_REG a10, 10
SAVE_REG a11, 11
SAVE_REG a12, 12
SAVE_REG a13, 13
SAVE_REG a14, 14
SAVE_REG a15, 15
movi sp, NMIRegisterSaved /* also top of NMIHandlerStack */
movi a0, 0
movi a2, 0x23 /* argument for handler */
wsr.ps a2
rsync
rsr.sar a14
s32i a14, sp, 0 /* this is also NMIRegisterSaved+0 */
call0 sdk_wDev_ProcessFiq
l32i a15, sp, 0
wsr.sar a15
movi a2, 0x33
wsr.ps a2
rsync
LOAD_REG a4, 4
LOAD_REG a5, 5
LOAD_REG a6, 6
LOAD_REG a7, 7
LOAD_REG a8, 8
LOAD_REG a9, 9
LOAD_REG a10, 10
LOAD_REG a11, 11
LOAD_REG a12, 12
LOAD_REG a13, 13
LOAD_REG a14, 14
LOAD_REG a15, 15
LOAD_REG a2, -5
LOAD_REG a3, -4
wsr.epc1 a2
wsr.exccause a3
LOAD_REG a2, -3
LOAD_REG a3, -2
wsr.excvaddr a2
wsr.excsave1 a3
LOAD_REG a0, 0
/* set dport nmi status bit 0 (wDev_ProcessFiq clears & verifies this bit stays cleared,
see http://esp8266-re.foogod.com/wiki/WDev_ProcessFiq_%28IoT_RTOS_SDK_0.9.9%29) */
movi a2, 0x3ff00000
movi a3, 0x1
s32i a3, a2, 0
LOAD_REG a2, 2
LOAD_REG a3, 3
LOAD_REG a1, 1
rfi 0x3
.type CallNMIExceptionHandler, @function
/* Some UserException causes, see table Table 464 in ISA reference */
#define CAUSE_SYSCALL 1
#define CAUSE_LVL1INT 4
movi a0, NMIRegisterSaved
SAVE_REG a2, 2
SAVE_REG sp, 1
SAVE_REG a3, 3
rsr a2, excsave3 # a2 is now former a0
SAVE_REG a4, 4
SAVE_REG a2, 0
rsr a3, epc1
rsr a4, exccause
SAVE_REG a3, -5
SAVE_REG a4, -4
rsr a3, excvaddr
SAVE_REG a3, -3
rsr a3, excsave1
SAVE_REG a3, -2
SAVE_REG a5, 5
SAVE_REG a6, 6
SAVE_REG a7, 7
SAVE_REG a8, 8
SAVE_REG a9, 9
SAVE_REG a10, 10
SAVE_REG a11, 11
SAVE_REG a12, 12
SAVE_REG a13, 13
SAVE_REG a14, 14
SAVE_REG a15, 15
movi sp, .LNMIHandlerStackTop
movi a0, 0
movi a2, 0x23 # argument for handler
wsr a2, ps
rsync
rsr a14, sar
s32i a14, sp, 0 # this is also NMIRegisterSaved+0
call0 sdk_wDev_ProcessFiq
l32i a15, sp, 0
wsr a15, sar
movi a2, 0x33
wsr a2, ps
rsync
LOAD_REG a4, 4
LOAD_REG a5, 5
LOAD_REG a6, 6
LOAD_REG a7, 7
LOAD_REG a8, 8
LOAD_REG a9, 9
LOAD_REG a10, 10
LOAD_REG a11, 11
LOAD_REG a12, 12
LOAD_REG a13, 13
LOAD_REG a14, 14
LOAD_REG a15, 15
LOAD_REG a2, -5
LOAD_REG a3, -4
wsr a2, epc1
wsr a3, exccause
LOAD_REG a2, -3
LOAD_REG a3, -2
wsr a2, excvaddr
wsr a3, excsave1
LOAD_REG a0, 0
/* set dport nmi status bit 0 (wDev_ProcessFiq clears & verifies this
* bit stays cleared, see
* http://esp8266-re.foogod.com/wiki/WDev_ProcessFiq_%28IoT_RTOS_SDK_0.9.9%29)
*/
movi a2, 0x3ff00000
movi a3, 0x1
s32i a3, a2, 0
LOAD_REG a2, 2
LOAD_REG a3, 3
LOAD_REG a1, 1
rfi 3
.type CallUserExceptionHandler, @function
CallUserExceptionHandler:
rsr.exccause a0
beqi a0, CAUSE_SYSCALL, UserSyscallHandler
mov a0, sp
addi sp, sp, -0x50
s32i a0, sp, 0x10
rsr.ps a0
s32i a0, sp, 0x08
rsr.epc1 a0
s32i a0, sp, 0x04
rsr.excsave1 a0 /* a0 was saved in UserExceptionVector */
s32i a0, sp, 0x0c
movi a0, _xt_user_exit
s32i a0, sp, 0x0
call0 sdk__xt_int_enter
movi a0, 0x23
wsr.ps a0
rsync
rsr.exccause a2
beqi a2, CAUSE_LVL1INT, UserHandleInterrupt
/* Any UserException cause other than level 1 interrupt triggers a panic */
/*********************** General UserException Handler ***********************/
.section .vecbase.text, "x"
/* Called by UserExceptionVector if EXCCAUSE is anything other than
* LoadStoreCause. */
.literal_position
.balign 4
UserExceptionHandler:
.type UserExceptionHandler, @function
xsr a0, excsave1 # a0 now contains sp
mov sp, a0
addi sp, sp, -0x50
s32i a0, sp, 0x10
rsr a0, ps
s32i a0, sp, 0x08
rsr a0, epc1
s32i a0, sp, 0x04
rsr a0, excsave1
s32i a0, sp, 0x0c
movi a0, _xt_user_exit
s32i a0, sp, 0x0
call0 sdk__xt_int_enter
movi a0, 0x23
wsr a0, ps
rsync
rsr a2, exccause
beqi a2, CAUSE_LVL1INT, UserHandleInterrupt
/* Any UserException cause other than level 1 interrupt should panic */
UserFailOtherExceptionCause:
break 1, 1
call0 sdk_user_fatal_exception_handler
break 1, 1
call0 sdk_user_fatal_exception_handler
UserHandleInterrupt:
rsil a0, 1
rsr.intenable a2
rsr.interrupt a3
movi a4, 0x3fff
and a2, a2, a3
and a2, a2, a4 /* a2 = 0x3FFF & INTENABLE & INTERRUPT */
rsil a0, 1
rsr a2, intenable
rsr a3, interrupt
movi a4, 0x3fff
and a2, a2, a3
and a2, a2, a4 # a2 = 0x3FFF & INTENABLE & INTERRUPT
UserHandleTimer:
movi a3, 0xffbf
and a3, a2, a3 /* a3 = a2 & 0xFFBF, ie remove 0x40 from a2 if set */
bnez a3, UserTimerDone /* bits other than 0x40 are set */
movi a3, 0x40
sub a12, a2, a3 /* a12 - a2 - 0x40 - I think a12 _must_ be zero here? */
call0 sdk__xt_timer_int /* tick timer interrupt */
mov a2, a12 /* restore a2 from a12, ie zero */
beqz a2, UserIntDone
movi a3, 0xffbf
and a3, a2, a3 # a3 = a2 with bit 6 cleared
bnez a3, UserTimerDone # If any non-timer interrupt bits set
movi a3, 0x40
sub a12, a2, a3 # a12 = a2 - 0x40 -- Will be zero if bit 6 set
call0 sdk__xt_timer_int # tick timer interrupt
mov a2, a12 # restore a2 from a12, ie zero
beqz a2, UserIntDone
UserTimerDone:
call0 _xt_isr_handler
bnez a2, UserHandleTimer
call0 _xt_isr_handler
bnez a2, UserHandleTimer
UserIntDone:
beqz a2, UserIntExit
break 1, 1 /* non-zero remnant in a2 means fail */
call0 sdk_user_fatal_exception_handler
beqz a2, UserIntExit
/* FIXME: this code will never be reached */
break 1, 1
call0 sdk_user_fatal_exception_handler
UserIntExit:
call0 sdk__xt_int_exit /* calls rfi */
call0 sdk__xt_int_exit # jumps to _xt_user_exit. Never returns here
/* As far as I can tell, the syscall handler is basically a no-op */
UserSyscallHandler:
addi sp, sp, -0x10
s32i a2, sp, 0x08
s32i a2, sp, 0x0c
rsr.epc1 a2
addi a3, a2, 0x3
wsr.epc1 a3
l32i a2, sp, 0x8
l32i a3, sp, 0xc
addi sp, sp, 0x10
movi a0, 0x7f
movnez a2, a0, a2
rsr.excsave1 a0
rfe
.section .text
.global _xt_user_exit
.type _xt_user_exit, @function
/* _xt_user_exit is used to exit interrupt context. */
/* TODO: Find a better place for this to live. */
_xt_user_exit:
l32i a0, sp, 0x8
wsr.ps a0
l32i a0, sp, 0x4
wsr.epc1 a0
l32i a0, sp, 0xc
l32i sp, sp, 0x10
rsync
rfe
.global _xt_user_exit
.type _xt_user_exit, @function
l32i a0, sp, 0x8
wsr a0, ps
l32i a0, sp, 0x4
wsr a0, epc1
l32i a0, sp, 0xc
l32i sp, sp, 0x10
rsync
rfe

View file

@ -10,6 +10,8 @@
#ifndef _COMMON_MACROS_H
#define _COMMON_MACROS_H
#include <sys/cdefs.h>
#define UNUSED __attributed((unused))
#ifndef BIT
@ -20,12 +22,21 @@
* and shift) constants. Used primarily with ESP8266 register access.
*/
#define VAL2FIELD(fieldname, value) (((value) & fieldname##_M) << fieldname##_S)
#define VAL2FIELD(fieldname, value) ((value) << fieldname##_S)
#define FIELD2VAL(fieldname, regbits) (((regbits) >> fieldname##_S) & fieldname##_M)
#define FIELD_MASK(fieldname) (fieldname##_M << fieldname##_S)
#define SET_FIELD(regbits, fieldname, value) (((regbits) & ~FIELD_MASK(fieldname)) | VAL2FIELD(fieldname, value))
/* VAL2FIELD/SET_FIELD do not normally check to make sure that the passed value
* will fit in the specified field (without clobbering other bits). This makes
* them faster and is usually fine. If you do need to make sure that the value
* will not overflow the field, use VAL2FIELD_M or SET_FIELD_M (which will
* first mask the supplied value to only the allowed number of bits) instead.
*/
#define VAL2FIELD_M(fieldname, value) (((value) & fieldname##_M) << fieldname##_S)
#define SET_FIELD_M(regbits, fieldname, value) (((regbits) & ~FIELD_MASK(fieldname)) | VAL2FIELD_M(fieldname, value))
/* Use this macro to store constant values in IROM flash instead
of having them loaded into rodata (which resides in DRAM)
@ -36,7 +47,11 @@
Important to note: IROM flash can only be accessed via 32-bit word
aligned reads. It's up to the user of this attribute to ensure this.
*/
#define IROM __attribute__((section(".irom0.literal"))) const
#ifdef __cplusplus
#define IROM __attribute__((section(".irom0.literal")))
#else
#define IROM __attribute__((section(".irom0.literal"))) const
#endif
#define INLINED inline static __attribute__((always_inline)) __attribute__((unused))

View file

@ -45,7 +45,7 @@ struct DPORT_REGS {
uint32_t volatile OTP_MAC1; // 0x54
uint32_t volatile OTP_CHIPID; // 0x58
uint32_t volatile OTP_MAC2; // 0x5c
} __attribute__ (( packed ));
};
_Static_assert(sizeof(struct DPORT_REGS) == 0x60, "DPORT_REGS is the wrong size");
@ -53,7 +53,7 @@ _Static_assert(sizeof(struct DPORT_REGS) == 0x60, "DPORT_REGS is the wrong size"
/* Currently very little known about this register. The following is based on analysis of the startup code in the Espressif SDK: */
#define DPORT_DPORT0_FIELD0_M 0x0000007f
#define DPORT_DPORT0_FIELD0_M 0x0000001f
#define DPORT_DPORT0_FIELD0_S 0
/* Details for INT_ENABLE register */

View file

@ -27,28 +27,26 @@ typedef enum {
INLINED void gpio_enable(const uint8_t gpio_num, const gpio_direction_t direction)
{
uint32_t iomux_flags;
uint32_t ctrl_val;
switch(direction) {
case GPIO_INPUT:
iomux_flags = 0;
ctrl_val = 0;
break;
case GPIO_OUTPUT:
iomux_flags = IOMUX_PIN_OUTPUT_ENABLE;
ctrl_val = GPIO_CONF_PUSH_PULL;
break;
case GPIO_OUT_OPEN_DRAIN:
iomux_flags = IOMUX_PIN_OUTPUT_ENABLE;
ctrl_val = 0;
break;
case GPIO_INPUT_PULLUP:
iomux_flags = IOMUX_PIN_PULLUP;
ctrl_val = 0;
break;
}
iomux_set_gpio_function(gpio_num, iomux_flags);
GPIO.CONF[gpio_num] = (GPIO.CONF[gpio_num] & FIELD_MASK(GPIO_CONF_INTTYPE)) | ctrl_val;
if(direction == GPIO_OUT_OPEN_DRAIN)
GPIO.CONF[gpio_num] |= GPIO_CONF_OPEN_DRAIN;
else
GPIO.CONF[gpio_num] &= ~GPIO_CONF_OPEN_DRAIN;
if (iomux_flags & IOMUX_PIN_OUTPUT_ENABLE)
GPIO.ENABLE_OUT_SET = BIT(gpio_num);
else
@ -124,7 +122,7 @@ INLINED void gpio_set_interrupt(const uint8_t gpio_num, const gpio_inttype_t int
/* Return the interrupt type set for a pin */
INLINED gpio_inttype_t gpio_get_interrupt(const uint8_t gpio_num)
{
return FIELD2VAL(GPIO_CONF_INTTYPE, GPIO.CONF[gpio_num]);
return (gpio_inttype_t)FIELD2VAL(GPIO_CONF_INTTYPE, GPIO.CONF[gpio_num]);
}
#endif

View file

@ -57,7 +57,7 @@ struct GPIO_REGS {
uint32_t volatile PWM; // 0x68
uint32_t volatile RTC_CALIB; // 0x6c
uint32_t volatile RTC_CALIB_RESULT; // 0x70
} __attribute__ (( packed ));
};
_Static_assert(sizeof(struct GPIO_REGS) == 0x74, "GPIO_REGS is the wrong size");
@ -76,10 +76,15 @@ _Static_assert(sizeof(struct GPIO_REGS) == 0x74, "GPIO_REGS is the wrong size");
* Under what conditions this GPIO input should generate an interrupt.
* (see gpio_inttype_t enum below for values)
*
* GPIO_CONF_PUSH_PULL (boolean)
* When set, a high output state will pull the pin up to +Vcc (3.3V). When
* cleared, output functions in "open drain" mode (low state will pull down
* to ground, but high state allows output to "float").
* GPIO_CONF_OPEN_DRAIN (boolean)
* If this bit is set, the pin is in "open drain" mode - a high output state
* will leave the pin floating but not source any current. If bit is cleared,
* the pin is in push/pull mode so a high output state will drive the pin up
* to +Vcc (3.3V). In either case, a low output state will pull the pin down
* to ground.
*
* GPIO_CONF_OPEN_DRAIN does not appear to work on all pins.
*
*
* GPIO_CONF_SOURCE_PWM (boolean)
* When set, GPIO pin output will be connected to the sigma-delta PWM
@ -93,7 +98,7 @@ _Static_assert(sizeof(struct GPIO_REGS) == 0x74, "GPIO_REGS is the wrong size");
#define GPIO_CONF_WAKEUP_ENABLE BIT(10)
#define GPIO_CONF_INTTYPE_M 0x00000007
#define GPIO_CONF_INTTYPE_S 7
#define GPIO_CONF_PUSH_PULL BIT(2)
#define GPIO_CONF_OPEN_DRAIN BIT(2)
#define GPIO_CONF_SOURCE_PWM BIT(0)
/* Valid values for the GPIO_CONF_INTTYPE field */

30
core/include/esp/hwrand.h Normal file
View file

@ -0,0 +1,30 @@
/** esp/hwrand.h
*
* Hardware Random Number Generator functions.
*
* For documentation, see http://esp8266-re.foogod.com/wiki/Random_Number_Generator
*
* Part of esp-open-rtos
* Copyright (C) 2015 Angus Gratton
* BSD Licensed as described in the file LICENSE
*/
#ifndef _ESP_RNG_H
#define _ESP_RNG_H
#include <stdint.h>
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Return a random 32-bit number */
uint32_t hwrand(void);
/* Fill a variable size buffer with data from the Hardware RNG */
void hwrand_fill(uint8_t *buf, size_t len);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -11,6 +11,10 @@
#include "esp/types.h"
#include "esp/iomux_regs.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Convert a GPIO pin number to an iomux register index.
*
@ -64,4 +68,8 @@ inline static void iomux_set_gpio_function(const uint8_t gpio_number, const uint
declared above */
#include "esp/iomux_private.h"
#ifdef __cplusplus
}
#endif
#endif

View file

@ -20,7 +20,7 @@
struct IOMUX_REGS {
uint32_t volatile CONF; // 0x00
uint32_t volatile PIN[16]; // 0x04 - 0x40
} __attribute__ (( packed ));
};
_Static_assert(sizeof(struct IOMUX_REGS) == 0x44, "IOMUX_REGS is the wrong size");
@ -45,7 +45,7 @@ _Static_assert(sizeof(struct IOMUX_REGS) == 0x44, "IOMUX_REGS is the wrong size"
#define IOMUX_PIN_FUNC_MASK 0x00001030
/* WARNING: Macro evaluates argument twice */
#define IOMUX_FUNC(val) (VAL2FIELD(IOMUX_PIN_FUNC_LOW, val) | VAL2FIELD(IOMUX_PIN_FUNC_HIGH, val))
#define IOMUX_FUNC(val) (VAL2FIELD_M(IOMUX_PIN_FUNC_LOW, val) | VAL2FIELD_M(IOMUX_PIN_FUNC_HIGH, val))
/* WARNING: Macro evaluates argument twice */
#define IOMUX_FUNC_VALUE(regbits) (FIELD2VAL(IOMUX_PIN_FUNC_LOW, regbits) | FIELD2VAL(IOMUX_PIN_FUNC_HIGH, regbits))

View file

@ -5,9 +5,12 @@
*/
#ifndef _ESP_ROM_H
#define _ESP_ROM_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
void Cache_Read_Disable(void);
/* http://esp8266-re.foogod.com/wiki/Cache_Read_Enable
@ -18,4 +21,8 @@ void Cache_Read_Disable(void);
*/
void Cache_Read_Enable(uint32_t odd_even, uint32_t mb_count, uint32_t no_idea);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -67,7 +67,7 @@ struct SPI_REGS {
uint32_t volatile EXT1; // 0xf4
uint32_t volatile EXT2; // 0xf8
uint32_t volatile EXT3; // 0xfc
} __attribute__ (( packed ));
};
_Static_assert(sizeof(struct SPI_REGS) == 0x100, "SPI_REGS is the wrong size");

View file

@ -14,6 +14,10 @@
#include "esp/timer_regs.h"
#include "esp/cpu.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
FRC1 = 0,
FRC2 = 1,
@ -129,4 +133,8 @@ INLINED bool timer_set_timeout(const timer_frc_t frc, uint32_t us);
#include "timer_private.h"
#ifdef __cplusplus
}
#endif
#endif

View file

@ -7,6 +7,10 @@
#ifndef _ESP_TIMER_PRIVATE_H
#define _ESP_TIMER_PRIVATE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
@ -193,7 +197,7 @@ INLINED bool _timer_set_frequency_impl(const timer_frc_t frc, uint32_t freq)
counts = timer_freq_to_count(frc, freq, div);
if(counts == 0)
{
printf("ABORT: No counter for timer %u frequency %lu\r\n", frc, freq);
printf("ABORT: No counter for timer %u frequency %u\r\n", frc, freq);
abort();
}
@ -255,6 +259,8 @@ INLINED bool timer_set_timeout(const timer_frc_t frc, uint32_t us)
return _timer_set_timeout_runtime(frc, us);
}
#ifdef __cplusplus
}
#endif
#endif

View file

@ -36,7 +36,7 @@ struct TIMER_REGS { // FRC1 FRC2
uint32_t volatile CTRL; // 0x08 0x28
uint32_t volatile STATUS; // 0x0c 0x2c
uint32_t volatile ALARM; // 0x30
} __attribute__ (( packed ));
};
_Static_assert(sizeof(struct TIMER_REGS) == 0x14, "TIMER_REGS is the wrong size");

View file

@ -24,7 +24,7 @@
*/
#define UART_BASE 0x60000000
#define UART(i) (*(struct UART_REGS *)(0x60000200 - (i)*0xf00))
#define UART(i) (*(struct UART_REGS *)(UART_BASE + (i)*0xf00))
#define UART0_BASE UART_BASE
#define UART1_BASE (UART_BASE + 0xf00)
@ -46,7 +46,7 @@ struct UART_REGS {
uint32_t volatile _unused[17]; // 0x34 - 0x74
uint32_t volatile DATE; // 0x78
uint32_t volatile ID; // 0x7c
} __attribute__ (( packed ));
};
_Static_assert(sizeof(struct UART_REGS) == 0x80, "UART_REGS is the wrong size");

View file

@ -0,0 +1,42 @@
/* esp/wdev_regs.h
*
* ESP8266 register definitions for the "wdev" region (0x3FF2xxx)
*
* In the DPORT memory space, alongside DPORT regs. However mostly
* concerned with the WiFi hardware interface.
*
* Not compatible with ESP SDK register access code.
*/
#ifndef _ESP_WDEV_REGS_H
#define _ESP_WDEV_REGS_H
#include "esp/types.h"
#include "common_macros.h"
#define WDEV_BASE 0x3FF20000
#define WDEV (*(struct WDEV_REGS *)(WDEV_BASE))
/* Note: This memory region is not currently well understood. Pretty much all
* of the definitions here are from reverse-engineering the Espressif SDK code,
* many are just educated guesses, and almost certainly some are misleading or
* wrong. If you can improve on any of this, please contribute!
*/
struct WDEV_REGS {
uint32_t volatile _unknown0[768]; // 0x0000 - 0x0bfc
uint32_t volatile SYS_TIME; // 0x0c00
uint32_t volatile _unknown1[144]; // 0x0c04 - 0x0e40
uint32_t volatile HWRNG; // 0xe44 HW RNG, see http://esp8266-re.foogod.com/wiki/Random_Number_Generator
} __attribute__ (( packed ));
_Static_assert(sizeof(struct WDEV_REGS) == 0xe48, "WDEV_REGS is the wrong size");
/* Extra paranoid check about the HWRNG address, as if this becomes
wrong there will be no obvious symptoms apart from a lack of
entropy.
*/
_Static_assert(&WDEV.HWRNG == (void*)0x3FF20E44, "HWRNG register is at wrong address");
#endif /* _ESP_WDEV_REGS_H */

View file

@ -27,10 +27,21 @@ struct WDT_REGS {
uint32_t volatile REG2; // 0x08
uint32_t volatile _unused[2]; // 0x0c - 0x10
uint32_t volatile FEED; // 0x14
} __attribute__ (( packed ));
};
_Static_assert(sizeof(struct WDT_REGS) == 0x18, "WDT_REGS is the wrong size");
/* Details for CTRL register */
/* Note: these are currently just guesses based on interpretation of the startup code */
#define WDT_CTRL_ENABLE BIT(0)
#define WDT_CTRL_FIELD0_M 0x00000003
#define WDT_CTRL_FIELD0_S 1
#define WDT_CTRL_FLAG3 BIT(3)
#define WDT_CTRL_FLAG4 BIT(4)
#define WDT_CTRL_FLAG5 BIT(5)
/* Writing WDT_FEED_MAGIC to WDT.FEED register "feeds the dog" and holds off
* triggering for another cycle (unconfirmed) */
#define WDT_FEED_MAGIC 0x73

View file

@ -0,0 +1,6 @@
#ifndef _VERSION_H
#define _VERSION_H
#define OS_VERSION_STR "0.0.1"
#endif /* _VERSION_H */

232
core/include/sdk_internal.h Normal file
View file

@ -0,0 +1,232 @@
#ifndef _INTERNAL_SDK_STRUCTURES_H
#define _INTERNAL_SDK_STRUCTURES_H
#include "espressif/esp_wifi.h"
#include "espressif/spi_flash.h"
#include "lwip/netif.h"
///////////////////////////////////////////////////////////////////////////////
// Internal structures and data objects //
///////////////////////////////////////////////////////////////////////////////
// 'info' is declared in app_main.o at .bss+0x4
struct sdk_info_st {
uint32_t _unknown0;
uint32_t _unknown1;
uint32_t _unknown2;
uint8_t _unknown3[12];
uint8_t softap_mac_addr[6];
uint8_t sta_mac_addr[6];
};
extern struct sdk_info_st sdk_info;
// 'rst_if' is declared in user_interface.o at .bss+0xfc
struct sdk_rst_if_st {
uint32_t version;
uint8_t _unknown[28];
};
extern struct sdk_rst_if_st sdk_rst_if;
// 'g_ic' is declared in libnet80211/ieee80211.o at .bss+0x0
// See also: http://esp8266-re.foogod.com/wiki/G_ic_(IoT_RTOS_SDK_0.9.9)
struct sdk_g_ic_netif_info {
struct netif *netif;
//TODO: rest of this structure is unknown.
};
// This is the portion of g_ic which is not loaded/saved to the flash ROM, and
// starts out zeroed on every boot.
struct sdk_g_ic_volatile_st {
void *_unknown0;
void *_unknown4;
uint8_t _unknown8[8];
struct sdk_g_ic_netif_info *station_netif_info;
struct sdk_g_ic_netif_info *softap_netif_info;
uint8_t _unknown18;
uint32_t _unknown1c;
uint32_t _unknown20;
uint8_t _unknown24[8];
uint8_t _unknown2c;
uint8_t _unknown30[76];
uint8_t _unknown7c;
uint8_t _unknown7d;
uint8_t _unknown7e;
uint8_t _unknown7f;
uint8_t _unknown80[204];
void *_unknown14c;
uint8_t _unknown150[20];
uint32_t _unknown164;
void *_unknown168;
void *_unknown16c;
void *_unknown170;
void *_unknown174;
void *_unknown178;
uint8_t _unknown17c[4];
void *_unknown180;
void *_unknown184;
struct station_info *station_info_head;
struct station_info *station_info_tail;
uint32_t _unknown190;
uint32_t _unknown194;
uint8_t _unknown198[40];
void *_unknown1c0;
void *_unknown1c4;
uint32_t _unknown1c8;
uint8_t _unknown1cc[4];
uint16_t _unknown1d0;
uint8_t _unknown1d2[2];
uint8_t _unknown1d4;
uint8_t _unknown1d5[3];
};
struct sdk_g_ic_unk0_st {
uint32_t _unknown1e4;
uint8_t _unknown1e8[32];
};
// This is the portion of g_ic which is loaded/saved to the flash ROM, and thus
// is preserved across reboots.
struct sdk_g_ic_saved_st {
uint8_t _unknown1d8;
uint8_t boot_info;
uint8_t user0_addr[3];
uint8_t user1_addr[3];
uint8_t wifi_mode;
uint8_t wifi_led_enable;
uint8_t wifi_led_gpio;
uint8_t _unknown1e3;
struct sdk_g_ic_unk0_st _unknown1e4;
uint8_t _unknown208;
uint8_t _unknown209;
uint8_t _unknown20a;
uint8_t _unknown20b;
uint8_t _unknown20c;
uint8_t _unknown20d;
uint8_t _unknown20e;
uint8_t _unknown20f[64];
uint8_t _unknown24f;
uint8_t _unknown250[49];
uint8_t _unknown281;
uint8_t _unknown282[6];
uint32_t _unknown288;
uint8_t _unknown28c;
uint8_t _unknown28d[31];
uint8_t _unknown2ac[64];
uint8_t _unknonwn2ec;
uint8_t _unknown2ed[32];
uint8_t _unknown30d;
uint8_t _unknown30e;
uint8_t _unknown30f;
uint8_t _unknown310;
uint8_t _unknown311[3];
uint8_t ap_number;
uint8_t current_ap_id;
uint8_t _unknown316[502];
uint32_t _unknown50c;
uint16_t _unknown510;
uint16_t _unknown512;
uint16_t _unknown514;
uint8_t _unknown516[2];
uint8_t auto_connect;
uint8_t _unknown519[3];
enum sdk_phy_mode phy_mode;
uint8_t _unknown520[36];
uint16_t _unknown544;
uint8_t _unknown546[2];
};
struct sdk_g_ic_st {
struct sdk_g_ic_volatile_st v; // 0x0 - 0x1d8
struct sdk_g_ic_saved_st s; // 0x1d8 - 0x548
};
extern struct sdk_g_ic_st sdk_g_ic;
///////////////////////////////////////////////////////////////////////////////
// The above structures all refer to data regions outside our control, and a
// simple mistake/misunderstanding editing things here can completely screw up
// how we access them, so do some basic sanity checks to make sure that they
// appear to match up correctly with the actual data structures other parts of
// the SDK are expecting.
///////////////////////////////////////////////////////////////////////////////
_Static_assert(sizeof(struct sdk_info_st) == 0x24, "info_st is the wrong size!");
_Static_assert(sizeof(struct sdk_rst_if_st) == 0x20, "sdk_rst_if_st is the wrong size!");
_Static_assert(sizeof(struct sdk_g_ic_volatile_st) == 0x1d8, "sdk_g_ic_volatile_st is the wrong size!");
_Static_assert(sizeof(struct sdk_g_ic_saved_st) == 0x370, "sdk_g_ic_saved_st is the wrong size!");
_Static_assert(sizeof(struct sdk_g_ic_st) == 0x548, "sdk_g_ic_st is the wrong size!");
///////////////////////////////////////////////////////////////////////////////
// Function Prototypes //
///////////////////////////////////////////////////////////////////////////////
sdk_SpiFlashOpResult sdk_SPIRead(uint32_t src_addr, uint32_t *des_addr, uint32_t size);
sdk_SpiFlashOpResult sdk_SPIWrite(uint32_t des_addr, uint32_t *src_addr, uint32_t size);
void sdk_cnx_attach(struct sdk_g_ic_st *);
void sdk_ets_timer_init(void);
void sdk_ieee80211_ifattach(struct sdk_g_ic_st *, uint8_t *);
void sdk_ieee80211_phy_init(enum sdk_phy_mode);
void sdk_lmacInit(void);
void sdk_phy_disable_agc(void);
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 *);
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);
void sdk_wDevEnableRx(void);
void sdk_wDev_Initialize(void);
void sdk_wifi_mode_set(uint8_t);
void sdk_wifi_softap_cacl_mac(uint8_t *, uint8_t *);
void sdk_wifi_softap_set_default_ssid(void);
void sdk_wifi_softap_start(void);
void sdk_wifi_station_start(void);
#endif /* _INTERNAL_SDK_STRUCTURES_H */

21
core/include/xtensa_ops.h Normal file
View file

@ -0,0 +1,21 @@
/** xtensa_ops.h
*
* Special macros/etc which deal with Xtensa-specific architecture/CPU
* considerations.
*
* Part of esp-open-rtos
* Copyright (C) 2015 Superhouse Automation Pty Ltd
* BSD Licensed as described in the file LICENSE
*/
#ifndef _XTENSA_OPS_H
#define _XTENSA_OPS_H
// GCC macros for reading, writing, and exchanging Xtensa processor special
// registers:
#define RSR(var, reg) asm volatile ("rsr %0, " #reg : "=r" (var));
#define WSR(var, reg) asm volatile ("wsr %0, " #reg : : "r" (var));
#define XSR(var, reg) asm volatile ("xsr %0, " #reg : "+r" (var));
#endif /* _XTENSA_OPS_H */

View file

@ -4,6 +4,8 @@
*
* To have this work from initial reset, without needing an iomux call
* first, choose a pin where iomux defaults to GPIO (ie 0,2,4,5)
*
* Current sets on=LOW, as the GPIO2 pin is active low
*/
LED_GPIO=2
GPIO_DIR_SET = 0x6000030c
@ -19,11 +21,11 @@ GPIO_OUT_CLEAR = 0x60000308
.endm
// Turn LED on. rega, regb will be clobbered
.macro led_on rega, regb
.macro led_off rega, regb
led_op \rega, \regb, GPIO_OUT_SET
.endm
// Turn LED off. rega, regb will be clobbered
.macro led_off rega, regb
// Turn LED on. rega, regb will be clobbered
.macro led_on rega, regb
led_op \rega, \regb, GPIO_OUT_CLEAR
.endm

View file

@ -6,6 +6,7 @@
*/
#include <sys/reent.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <espressif/sdk_private.h>
#include <common_macros.h>
#include <stdlib.h>
@ -37,6 +38,10 @@ IRAM caddr_t _sbrk_r (struct _reent *r, int incr)
*/
long _write_r(struct _reent *r, int fd, const char *ptr, int len )
{
if(fd != r->_stdout->_file) {
r->_errno = EBADF;
return -1;
}
for(int i = 0; i < len; i++)
sdk_os_putc(ptr[i]);
return len;
@ -49,29 +54,31 @@ long _write_r(struct _reent *r, int fd, const char *ptr, int len )
*/
long __attribute__((weak)) _read_r( struct _reent *r, int fd, char *ptr, int len )
{
for(int i = 0; i < len; i++) {
char ch;
while (sdk_uart_rx_one_char(&ch)) ;
ptr[i] = ch;
}
return len;
if(fd != r->_stdin->_file) {
r->_errno = EBADF;
return -1;
}
for(int i = 0; i < len; i++) {
char ch;
while (sdk_uart_rx_one_char(&ch)) ;
ptr[i] = ch;
}
return len;
}
/* These are stub implementations for the reentrant syscalls that
* newlib is configured to expect */
int _fstat_r(struct _reent *r, int fd, void *buf)
/* Stub syscall implementations follow, to allow compiling newlib functions that
pull these in via various codepaths
*/
__attribute__((alias("syscall_returns_enosys"))) int _open_r(struct _reent *r, const char *pathname, int flags, int mode);
__attribute__((alias("syscall_returns_enosys"))) int _fstat_r(struct _reent *r, int fd, void *buf);
__attribute__((alias("syscall_returns_enosys"))) int _close_r(struct _reent *r, int fd);
__attribute__((alias("syscall_returns_enosys"))) off_t _lseek_r(struct _reent *r, int fd, off_t offset, int whence);
/* Generic stub for any newlib syscall that fails with errno ENOSYS
("Function not implemented") and a return value equivalent to
(int)-1. */
static int syscall_returns_enosys(struct _reent *r)
{
r->_errno=ENOSYS;
return -1;
}
int _close_r(struct _reent *r, int fd)
{
return -1;
}
off_t _lseek_r(struct _reent *r, int fd, off_t offset, int whence)
{
return (off_t)-1;
}

View file

@ -14,3 +14,18 @@ void IRAM *zalloc(size_t nbytes)
{
return calloc(1, nbytes);
}
extern void (*__init_array_start)(void);
extern void (*__init_array_end)(void);
/* Do things which should be done as part of the startup code, but aren't.
Can be replaced with _start() once we have open source startup code.
*/
void sdk_compat_initialise()
{
/* Call C++ constructors or C functions marked with __attribute__((constructor)) */
void (**p)(void);
for ( p = &__init_array_start; p != &__init_array_end; ++p)
(*p)();
}