Commit fe602d2d authored by Johny Mattsson's avatar Johny Mattsson
Browse files

Removed all currently-unused code & docs.

Heading towards having only ESP32-aware/capable code in this branch.
parent ddeb26c4
#ifndef _NODEMCU_MDNS_H
#define _NODEMCU_MDNS_H
struct nodemcu_mdns_info {
const char *host_name;
const char *host_desc;
const char *service_name;
uint16 service_port;
const char *txt_data[10];
};
void nodemcu_mdns_close(void);
bool nodemcu_mdns_init(struct nodemcu_mdns_info *);
#endif
// Headers to the various functions in the rom (as we discover them)
#ifndef _ROM_H_
#define _ROM_H_
#include "c_types.h"
#include "ets_sys.h"
/* Change GPIO pin output by setting, clearing, or disabling pins.
* In general, it is expected that a bit will be set in at most one
* of these masks. If a bit is clear in all masks, the output state
* remains unchanged.
*
* There is no particular ordering guaranteed; so if the order of
* writes is significant, calling code should divide a single call
* into multiple calls.
*/
void gpio_output_set(uint32_t set_mask, uint32_t clear_mask, uint32_t enable_mask, uint32_t disable_mask);
/* Set the specified GPIO register to the specified value.
* This is a very general and powerful interface that is not
* expected to be used during normal operation. It is intended
* mainly for debug, or for unusual requirements.
*/
void gpio_register_set(uint32_t reg_id, uint32_t value);
// SHA1 is assumed to match the netbsd sha1.h headers
#define SHA1_DIGEST_LENGTH 20
#define SHA1_DIGEST_STRING_LENGTH 41
typedef struct {
uint32_t state[5];
uint32_t count[2];
uint8_t buffer[64];
} SHA1_CTX;
extern void SHA1Transform(uint32_t[5], const uint8_t[64]);
extern void SHA1Init(SHA1_CTX *);
extern void SHA1Final(uint8_t[SHA1_DIGEST_LENGTH], SHA1_CTX *);
extern void SHA1Update(SHA1_CTX *, const uint8_t *, unsigned int);
// MD5 is assumed to match the NetBSD md5.h header
#define MD5_DIGEST_LENGTH 16
typedef struct
{
uint32_t state[5];
uint32_t count[2];
uint8_t buffer[64];
} MD5_CTX;
extern void MD5Init(MD5_CTX *);
extern void MD5Update(MD5_CTX *, const unsigned char *, unsigned int);
extern void MD5Final(unsigned char[MD5_DIGEST_LENGTH], MD5_CTX *);
// base64_encode/decode derived by Cal
// Appears to match base64.h from netbsd wpa utils.
extern unsigned char * base64_encode(const unsigned char *src, size_t len, size_t *out_len);
extern unsigned char * base64_decode(const unsigned char *src, size_t len, size_t *out_len);
// Unfortunately it that seems to require the ROM memory management to be
// initialized because it uses mem_malloc
// Interrupt Service Routine functions
typedef void (*ets_isr_fn) (void *arg);
extern int ets_isr_attach (unsigned int interrupt, ets_isr_fn, void *arg);
extern void ets_isr_mask (unsigned intr);
extern void ets_isr_unmask (unsigned intr);
// 2, 3 = reset (module dependent?), 4 = wdt
int rtc_get_reset_reason (void);
// Hardware exception handling
struct exception_frame
{
uint32_t epc;
uint32_t ps;
uint32_t sar;
uint32_t unused;
union {
struct {
uint32_t a0;
// note: no a1 here!
uint32_t a2;
uint32_t a3;
uint32_t a4;
uint32_t a5;
uint32_t a6;
uint32_t a7;
uint32_t a8;
uint32_t a9;
uint32_t a10;
uint32_t a11;
uint32_t a12;
uint32_t a13;
uint32_t a14;
uint32_t a15;
};
uint32_t a_reg[15];
};
uint32_t cause;
};
/**
* C-level exception handler callback prototype.
*
* Does not need an RFE instruction - it is called through a wrapper which
* performs state capture & restore, as well as the actual RFE.
*
* @param ef An exception frame containing the relevant state from the
* exception triggering. This state may be manipulated and will
* be applied on return.
* @param cause The exception cause number.
*/
typedef void (*exception_handler_fn) (struct exception_frame *ef, uint32_t cause);
/**
* Sets the exception handler function for a particular exception cause.
* @param handler The exception handler to install.
* If NULL, reverts to the XTOS default handler.
* @returns The previous exception handler, or NULL if none existed prior.
*/
//exception_handler_fn _xtos_set_exception_handler (uint32_t cause, exception_handler_fn handler);
void ets_update_cpu_frequency (uint32_t mhz);
uint32_t ets_get_cpu_frequency (void);
void *ets_memcpy (void *dst, const void *src, size_t n);
void *ets_memmove (void *dst, const void *src, size_t n);
void *ets_memset (void *dst, int c, size_t n);
int ets_memcmp (const void *s1, const void *s2, size_t n);
char *ets_strcpy (char *dst, const char *src);
size_t ets_strlen (const char *s);
int ets_strcmp (const char *s1, const char *s2);
int ets_strncmp (const char *s1, const char *s2, size_t n);
char *ets_strncpy(char *dest, const char *src, size_t n);
char *ets_strstr(const char *haystack, const char *needle);
void ets_delay_us (uint32_t us);
int ets_printf(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
void ets_str2macaddr (uint8_t *dst, const char *str);
typedef void (*ETSTimerFunc)(void *arg);
typedef struct ETSTimer
{
struct ETSTimer *timer_next;
void *timer_handle;
uint32_t timer_expire;
uint32_t timer_period;
ETSTimerFunc timer_func;
bool timer_repeat_flag;
void *timer_arg;
} ETSTimer;
void ets_timer_disarm (ETSTimer *a);
void ets_timer_setfn (ETSTimer *t, ETSTimerFunc *fn, void *parg);
void Cache_Read_Enable(uint32_t b0, uint32_t b1, uint32_t use_40108000);
void Cache_Read_Disable(void);
void ets_intr_lock(void);
void ets_intr_unlock(void);
int rand(void);
void srand(unsigned int);
unsigned int uart_baudrate_detect(unsigned int uart_no, unsigned int async);
/* Returns 0 on success, 1 on failure */
uint8_t SPIRead(uint32_t src_addr, uint32_t *des_addr, uint32_t size);
#endif
#ifndef RTC_ACCESS_H
#define RTC_ACCESS_H
#include <c_types.h>
#define RTC_MMIO_BASE 0x60000700
#define RTC_USER_MEM_BASE 0x60001200
#define RTC_USER_MEM_NUM_DWORDS 128
#define RTC_TARGET_ADDR 0x04
#define RTC_COUNTER_ADDR 0x1c
static inline uint32_t rtc_mem_read(uint32_t addr)
{
return ((uint32_t*)RTC_USER_MEM_BASE)[addr];
}
static inline void rtc_mem_write(uint32_t addr, uint32_t val)
{
((uint32_t*)RTC_USER_MEM_BASE)[addr]=val;
}
static inline uint64_t rtc_make64(uint32_t high, uint32_t low)
{
return (((uint64_t)high)<<32)|low;
}
static inline uint64_t rtc_mem_read64(uint32_t addr)
{
return rtc_make64(rtc_mem_read(addr+1),rtc_mem_read(addr));
}
static inline void rtc_mem_write64(uint32_t addr, uint64_t val)
{
rtc_mem_write(addr+1,val>>32);
rtc_mem_write(addr,val&0xffffffff);
}
static inline void rtc_memw(void)
{
asm volatile ("memw");
}
static inline void rtc_reg_write(uint32_t addr, uint32_t val)
{
rtc_memw();
addr+=RTC_MMIO_BASE;
*((volatile uint32_t*)addr)=val;
rtc_memw();
}
static inline uint32_t rtc_reg_read(uint32_t addr)
{
addr+=RTC_MMIO_BASE;
rtc_memw();
return *((volatile uint32_t*)addr);
}
#endif
/*
* Copyright 2015 Dius Computing Pty Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
* - Neither the name of the copyright holders nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @author Bernd Meyer <bmeyer@dius.com.au>
*/
#ifndef _RTCFIFO_H_
#define _RTCFIFO_H_
#include "rtcaccess.h"
#include "rtctime.h"
// 1: measurement alignment, in microseconds
// 2: timestamp for next sample (seconds). For sensors which sense during the sleep phase. Set to
// 0 to indicate no sample waiting. Simply do not use for sensors which deliver values prior to
// deep sleep.
// 3: Number of samples to take before doing a "real" boot. Decremented as samples are obtained
// 4: Reload value for (10). Needs to be applied by the firmware in the real boot (rtc_restart_samples_to_take())
//
// 5: FIFO location. First FIFO address in bits 0:7, first non-FIFO address in bits 8:15.
// Number of tag spaces in bits 16:23
// 6: Number of samples in FIFO.
// 7: FIFO tail (where next sample will be written. Increments by 1 for each sample)
// 8: FIFO head (where next sample will be read. Increments by 1 for each sample)
// 9: FIFO head timestamp. Used and maintained when pulling things off the FIFO. This is the timestamp of the
// most recent sample pulled off; I.e. the head samples timestamp is this plus that sample's delta_t
// 10: FIFO tail timestamp. Used and maintained when adding things to the FIFO. This is the timestamp of the
// most recent sample to have been added. I.e. a new sample's delta-t is calculated relative to this
// (9/10) are meaningless when (3) is zero
//
#define RTC_FIFO_BASE 10
#define RTC_FIFO_MAGIC 0x44695553
// RTCFIFO storage
#define RTC_FIFO_MAGIC_POS (RTC_FIFO_BASE+0)
#define RTC_ALIGNMENT_POS (RTC_FIFO_BASE+1)
#define RTC_TIMESTAMP_POS (RTC_FIFO_BASE+2)
#define RTC_SAMPLESTOTAKE_POS (RTC_FIFO_BASE+3)
#define RTC_SAMPLESPERBOOT_POS (RTC_FIFO_BASE+4)
#define RTC_FIFOLOC_POS (RTC_FIFO_BASE+5)
#define RTC_FIFOCOUNT_POS (RTC_FIFO_BASE+6)
#define RTC_FIFOTAIL_POS (RTC_FIFO_BASE+7)
#define RTC_FIFOHEAD_POS (RTC_FIFO_BASE+8)
#define RTC_FIFOTAIL_T_POS (RTC_FIFO_BASE+9)
#define RTC_FIFOHEAD_T_POS (RTC_FIFO_BASE+10)
// 32-127: FIFO space. Consisting of a number of tag spaces (see 4), followed by data entries.
// Data entries consist of:
// Bits 28:31 -> tag index. 0-15
// Bits 25:27 -> decimals
// Bits 16:24 -> delta-t in seconds from previous entry
// Bits 0:15 -> sample value
#define RTC_DEFAULT_FIFO_START 32
#define RTC_DEFAULT_FIFO_END 128
#define RTC_DEFAULT_TAGCOUNT 5
#define RTC_DEFAULT_FIFO_LOC (RTC_DEFAULT_FIFO_START + (RTC_DEFAULT_FIFO_END<<8) + (RTC_DEFAULT_TAGCOUNT<<16))
#ifndef RTCTIME_SLEEP_ALIGNED
# define RTCTIME_SLEEP_ALIGNED rtc_time_deep_sleep_until_aligned
#endif
typedef struct
{
uint32_t timestamp;
uint32_t value;
uint32_t decimals;
uint32_t tag;
} sample_t;
static inline void rtc_fifo_clear_content(void);
static inline uint32_t rtc_fifo_get_tail(void)
{
return rtc_mem_read(RTC_FIFOTAIL_POS);
}
static inline void rtc_fifo_put_tail(uint32_t val)
{
rtc_mem_write(RTC_FIFOTAIL_POS,val);
}
static inline uint32_t rtc_fifo_get_head(void)
{
return rtc_mem_read(RTC_FIFOHEAD_POS);
}
static inline void rtc_fifo_put_head(uint32_t val)
{
rtc_mem_write(RTC_FIFOHEAD_POS,val);
}
static inline uint32_t rtc_fifo_get_tail_t(void)
{
return rtc_mem_read(RTC_FIFOTAIL_T_POS);
}
static inline void rtc_fifo_put_tail_t(uint32_t val)
{
rtc_mem_write(RTC_FIFOTAIL_T_POS,val);
}
static inline uint32_t rtc_fifo_get_head_t(void)
{
return rtc_mem_read(RTC_FIFOHEAD_T_POS);
}
static inline void rtc_fifo_put_head_t(uint32_t val)
{
rtc_mem_write(RTC_FIFOHEAD_T_POS,val);
}
static inline uint32_t rtc_fifo_get_count(void)
{
return rtc_mem_read(RTC_FIFOCOUNT_POS);
}
static inline void rtc_fifo_put_count(uint32_t val)
{
rtc_mem_write(RTC_FIFOCOUNT_POS,val);
}
static inline uint32_t rtc_fifo_get_tagcount(void)
{
return (rtc_mem_read(RTC_FIFOLOC_POS)>>16)&0xff;
}
static inline uint32_t rtc_fifo_get_tagpos(void)
{
return (rtc_mem_read(RTC_FIFOLOC_POS)>>0)&0xff;
}
static inline uint32_t rtc_fifo_get_last(void)
{
return (rtc_mem_read(RTC_FIFOLOC_POS)>>8)&0xff;
}
static inline uint32_t rtc_fifo_get_first(void)
{
return rtc_fifo_get_tagpos()+rtc_fifo_get_tagcount();
}
static inline void rtc_fifo_put_loc(uint32_t first, uint32_t last, uint32_t tagcount)
{
rtc_mem_write(RTC_FIFOLOC_POS,first+(last<<8)+(tagcount<<16));
}
static inline uint32_t rtc_fifo_normalise_index(uint32_t index)
{
if (index>=rtc_fifo_get_last())
index=rtc_fifo_get_first();
return index;
}
static inline void rtc_fifo_increment_count(void)
{
rtc_fifo_put_count(rtc_fifo_get_count()+1);
}
static inline void rtc_fifo_decrement_count(void)
{
rtc_fifo_put_count(rtc_fifo_get_count()-1);
}
static inline uint32_t rtc_get_samples_to_take(void)
{
return rtc_mem_read(RTC_SAMPLESTOTAKE_POS);
}
static inline void rtc_put_samples_to_take(uint32_t val)
{
rtc_mem_write(RTC_SAMPLESTOTAKE_POS,val);
}
static inline void rtc_decrement_samples_to_take(void)
{
uint32_t stt=rtc_get_samples_to_take();
if (stt)
rtc_put_samples_to_take(stt-1);
}
static inline void rtc_restart_samples_to_take(void)
{
rtc_put_samples_to_take(rtc_mem_read(RTC_SAMPLESPERBOOT_POS));
}
static inline uint32_t rtc_fifo_get_value(uint32_t entry)
{
return entry&0xffff;
}
static inline uint32_t rtc_fifo_get_decimals(uint32_t entry)
{
return (entry>>25)&0x07;
}
static inline uint32_t rtc_fifo_get_deltat(uint32_t entry)
{
return (entry>>16)&0x1ff;
}
static inline uint32_t rtc_fifo_get_tagindex(uint32_t entry)
{
return (entry>>28)&0x0f;
}
static inline uint32_t rtc_fifo_get_tag_from_entry(uint32_t entry)
{
uint32_t index=rtc_fifo_get_tagindex(entry);
uint32_t tags_at=rtc_fifo_get_tagpos();
return rtc_mem_read(tags_at+index);
}
static inline void rtc_fifo_fill_sample(sample_t* dst, uint32_t entry, uint32_t timestamp)
{
dst->timestamp=timestamp;
dst->value=rtc_fifo_get_value(entry);
dst->decimals=rtc_fifo_get_decimals(entry);
dst->tag=rtc_fifo_get_tag_from_entry(entry);
}
// returns 1 if sample popped, 0 if not
static inline int8_t rtc_fifo_pop_sample(sample_t* dst)
{
uint32_t count=rtc_fifo_get_count();
if (count==0)
return 0;
uint32_t head=rtc_fifo_get_head();
uint32_t timestamp=rtc_fifo_get_head_t();
uint32_t entry=rtc_mem_read(head);
timestamp+=rtc_fifo_get_deltat(entry);
rtc_fifo_fill_sample(dst,entry,timestamp);
head=rtc_fifo_normalise_index(head+1);
rtc_fifo_put_head(head);
rtc_fifo_put_head_t(timestamp);
rtc_fifo_decrement_count();
return 1;
}
// returns 1 if sample is available, 0 if not
static inline int8_t rtc_fifo_peek_sample(sample_t* dst, uint32_t from_top)
{
if (rtc_fifo_get_count()<=from_top)
return 0;
uint32_t head=rtc_fifo_get_head();
uint32_t entry=rtc_mem_read(head);
uint32_t timestamp=rtc_fifo_get_head_t();
timestamp+=rtc_fifo_get_deltat(entry);
while (from_top--)
{
head=rtc_fifo_normalise_index(head+1);
entry=rtc_mem_read(head);
timestamp+=rtc_fifo_get_deltat(entry);
}
rtc_fifo_fill_sample(dst,entry,timestamp);
return 1;
}
static inline void rtc_fifo_drop_samples(uint32_t from_top)
{
uint32_t count=rtc_fifo_get_count();
if (count<=from_top)
from_top=count;
uint32_t head=rtc_fifo_get_head();
uint32_t head_t=rtc_fifo_get_head_t();
while (from_top--)
{
uint32_t entry=rtc_mem_read(head);
head_t+=rtc_fifo_get_deltat(entry);
head=rtc_fifo_normalise_index(head+1);
rtc_fifo_decrement_count();
}
rtc_fifo_put_head(head);
rtc_fifo_put_head_t(head_t);
}
static inline int rtc_fifo_find_tag_index(uint32_t tag)
{
uint32_t tags_at=rtc_fifo_get_tagpos();
uint32_t count=rtc_fifo_get_tagcount();
uint32_t i;
for (i=0;i<count;i++)
{
uint32_t stag=rtc_mem_read(tags_at+i);
if (stag==tag)
return i;
if (stag==0)
{
rtc_mem_write(tags_at+i,tag);
return i;
}
}
return -1;
}
static int32_t rtc_fifo_delta_t(uint32_t t, uint32_t ref_t)
{
uint32_t delta=t-ref_t;
if (delta>0x1ff)
return -1;
return delta;
}
static uint32_t rtc_fifo_construct_entry(uint32_t val, uint32_t tagindex, uint32_t decimals, uint32_t deltat)
{
return (val & 0xffff) + ((deltat & 0x1ff) <<16) +
((decimals & 0x7)<<25) + ((tagindex & 0xf)<<28);
}
static inline void rtc_fifo_store_sample(const sample_t* s)
{
uint32_t head=rtc_fifo_get_head();
uint32_t tail=rtc_fifo_get_tail();
uint32_t count=rtc_fifo_get_count();
int32_t tagindex=rtc_fifo_find_tag_index(s->tag);
if (count==0)
{
rtc_fifo_put_head_t(s->timestamp);
rtc_fifo_put_tail_t(s->timestamp);
}
uint32_t tail_t=rtc_fifo_get_tail_t();
int32_t deltat=rtc_fifo_delta_t(s->timestamp,tail_t);
if (tagindex<0 || deltat<0)
{ // We got something that doesn't fit into the scheme. Might be a long delay, might
// be some sort of dynamic change. In order to go on, we need to start over....
// ets_printf("deltat is %d, tagindex is %d\n",deltat,tagindex);
rtc_fifo_clear_content();
rtc_fifo_put_head_t(s->timestamp);
rtc_fifo_put_tail_t(s->timestamp);
head=rtc_fifo_get_head();
tail=rtc_fifo_get_tail();
count=rtc_fifo_get_count();
tagindex=rtc_fifo_find_tag_index(s->tag); // This should work now
if (tagindex<0)
return; // Uh-oh! This should never happen
}
if (head==tail && count>0)
{ // Full! Need to remove a sample
sample_t dummy;
rtc_fifo_pop_sample(&dummy);
}
rtc_mem_write(tail++,rtc_fifo_construct_entry(s->value,tagindex,s->decimals,deltat));
rtc_fifo_put_tail(rtc_fifo_normalise_index(tail));
rtc_fifo_put_tail_t(s->timestamp);
rtc_fifo_increment_count();
}
static uint32_t rtc_fifo_make_tag(const uint8_t* s)
{
uint32_t tag=0;
int i;
for (i=0;i<4;i++)
{
if (!s[i])
break;
tag+=((uint32_t)(s[i]&0xff))<<(i*8);
}
return tag;
}
static void rtc_fifo_tag_to_string(uint32_t tag, uint8_t s[5])
{
int i;
s[4]=0;
for (i=0;i<4;i++)
s[i]=(tag>>(8*i))&0xff;
}
static inline uint32_t rtc_fifo_get_divisor(const sample_t* s)
{
uint8_t decimals=s->decimals;
uint32_t div=1;
while (decimals--)
div*=10;
return div;
}
static inline void rtc_fifo_clear_tags(void)
{
uint32_t tags_at=rtc_fifo_get_tagpos();
uint32_t count=rtc_fifo_get_tagcount();
while (count--)
rtc_mem_write(tags_at++,0);
}
static inline void rtc_fifo_clear_content(void)
{
uint32_t first=rtc_fifo_get_first();
rtc_fifo_put_tail(first);
rtc_fifo_put_head(first);
rtc_fifo_put_count(0);
rtc_fifo_put_tail_t(0);
rtc_fifo_put_head_t(0);
rtc_fifo_clear_tags();
}
static inline void rtc_fifo_init(uint32_t first, uint32_t last, uint32_t tagcount)
{
rtc_fifo_put_loc(first,last,tagcount);
rtc_fifo_clear_content();
}
static inline void rtc_fifo_init_default(uint32_t tagcount)
{
if (tagcount==0)
tagcount=RTC_DEFAULT_TAGCOUNT;
rtc_fifo_init(RTC_DEFAULT_FIFO_START,RTC_DEFAULT_FIFO_END,tagcount);
}
static inline uint8_t rtc_fifo_check_magic(void)
{
if (rtc_mem_read(RTC_FIFO_MAGIC_POS)==RTC_FIFO_MAGIC)
return 1;
return 0;
}
static inline void rtc_fifo_set_magic(void)
{
rtc_mem_write(RTC_FIFO_MAGIC_POS,RTC_FIFO_MAGIC);
}
static inline void rtc_fifo_unset_magic(void)
{
rtc_mem_write(RTC_FIFO_MAGIC_POS,0);
}
static inline void rtc_fifo_deep_sleep_until_sample(uint32_t min_sleep_us)
{
uint32_t align=rtc_mem_read(RTC_ALIGNMENT_POS);
RTCTIME_SLEEP_ALIGNED(align,min_sleep_us);
}
static inline void rtc_fifo_prepare(uint32_t samples_per_boot, uint32_t us_per_sample, uint32_t tagcount)
{
rtc_mem_write(RTC_SAMPLESPERBOOT_POS,samples_per_boot);
rtc_mem_write(RTC_ALIGNMENT_POS,us_per_sample);
rtc_put_samples_to_take(0);
rtc_fifo_init_default(tagcount);
rtc_fifo_set_magic();
}
#endif
/*
* Copyright 2015 Dius Computing Pty Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
* - Neither the name of the copyright holders nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @author Johny Mattsson <jmattsson@dius.com.au>
*/
#ifndef _RTCTIME_H_
#define _RTCTIME_H_
/* We don't want to expose the raw rtctime interface as it is heavily
* 'static inline' and used by a few things, so instead we wrap the
* relevant functions and expose these instead, through the rtctime.c module.
*/
#include <c_types.h>
#include "sections.h"
#ifndef _RTCTIME_INTERNAL_H_
struct rtc_timeval
{
uint32_t tv_sec;
uint32_t tv_usec;
};
#endif
void TEXT_SECTION_ATTR rtctime_early_startup (void);
void rtctime_late_startup (void);
void rtctime_gettimeofday (struct rtc_timeval *tv);
void rtctime_settimeofday (const struct rtc_timeval *tv);
bool rtctime_have_time (void);
void rtctime_deep_sleep_us (uint32_t us);
void rtctime_deep_sleep_until_aligned_us (uint32_t align_us, uint32_t min_us);
#endif
/*
* Copyright 2015 Dius Computing Pty Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
* - Neither the name of the copyright holders nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @author Bernd Meyer <bmeyer@dius.com.au>
* @author Johny Mattsson <jmattsson@dius.com.au>
*/
#ifndef _RTCTIME_INTERNAL_H_
#define _RTCTIME_INTERNAL_H_
/*
* The ESP8266 has four distinct power states:
*
* 1) Active --- CPU and modem are powered and running
* 2) Modem Sleep --- CPU is active, but the RF section is powered down
* 3) Light Sleep --- CPU is halted, RF section is powered down. CPU gets reactivated by interrupt
* 4) Deep Sleep --- CPU and RF section are powered down, restart requires a full reset
*
* There are also three (relevant) sources of time information
*
* A) CPU Cycle Counter --- this is incremented at the CPU frequency in modes (1) and (2), but is
* halted in state (3), and gets reset in state (4). Highly precise 32 bit counter
* which overflows roughly every minute. Starts counting as soon as the CPU becomes
* active after a reset. Can cause an interrupt when it hits a particular value;
* This interrupt (and the register that determines the comparison value) are not
* used by the system software, and are available for user code to use.
*
* B) Free Running Counter 2 --- This is a peripheral which gets configured to run at 1/256th of the
* CPU frequency. It is also active in states (1) and (2), and is halted in state (3).
* However, the ESP system code will adjust its value across periods of Light Sleep
* that it initiates, so *in effect*, this counter kind-of remains active in (3).
* While in states (1) and (2), it is as precise as the CPU Cycle. While in state (3),
* however, it is only as precise as the system's knowledge of how long the sleep
* period was. This knowledge is limited (it is based on (C), see below).
* The Free Running Counter 2 is a 32 bit counter which overflows roughly every
* 4 hours, and typically has a resolution of 3.2us. It starts counting as soon as
* it gets configured, which is considerably *after* the time of reset, and in fact
* is not done by the ESP boot loader, but rather by the loaded-from-SPI-flash system
* code. This means it is not yet running when the boot loader calls the configured
* entry point, and the time between reset and the counter starting to run depends on
* the size of code/data to be copied into RAM from the flash.
* The FRC2 is also used by the system software for its internal time keeping, i.e. for
* dealing with any registered ETS_Timers (and derived-from-them timer functionality).
*
* C) "Real Time Clock" --- This peripheral runs from an internal low power RC oscillator, at a frequency
* somewhere in the 120-200kHz range. It keeps running in all power states, and is in
* fact the time source responsible for generating an interrupt (state (3)) or reset
* (state (4)) to end Light and Deep Sleep periods. However, it *does* get reset to
* zero after a reset, even one it caused itself.
* The major issue with the RTC is that it is not using a crystal (support for an
* external 32.768kHz crystal was planned at one point, but was removed from the
* final ESP8266 design), and thus the frequency of the oscillator is dependent on
* a number of parameters, including the chip temperature. The ESP's system software
* contains code to "calibrate" exactly how long one cycle of the oscillator is, and
* uses that calibration to work out how many cycles to sleep for modes (3) and (4).
* However, once the chip has entered a low power state, it quickly cools down, which
* results in the oscillator running faster than during calibration, leading to early
* wakeups. This effect is small (even in relative terms) for short sleep periods (because
* the temperature does not change much over a few hundred milliseconds), but can get
* quite large for extended sleeps.
*
* For added fun, a typical ESP8266 module starts up running the CPU (and thus the cycle counter) at 52MHz,
* but usually this will be switched to 80MHz on application startup, and can later be switched to 160MHz
* under user control. Meanwhile, the FRC2 is usually kept running at 80MHz/256, regardless of the CPU
* clock.
*
*
*
* The code in this file implements a best-effort time keeping solution for the ESP. It keeps track of time
* by switching between various time sources. All state is kept in RAM associated with the RTC, which is
* maintained across Deep Sleep periods.
*
* Internally, time is managed in units of cycles of a (hypothetical) 2080MHz clock, e.g. in units
* of 0.4807692307ns. The reason for this choice is that this covers both the FRC2 and the cycle
* counter periods, while running at 52MHz, 80MHz or 160MHz.
*
* At any given time, the time status indicates whether the FRC2 or the Cycle Counter is the current time
* source, how many unit cycles each LSB of the chosen time source "is worth", and what the unix time in
* unit cycles was when the time source was at 0.
* Given that either time source overflows its 32 bit counter in a relatively short time, the code also
* maintains a "last read 32 bit value" for the selected time source, and on each subsequent read will
* check for overflow and, if necessary, adjust the unix-time-at-time-source-being-zero appropriately.
* In order to avoid missing overflows, a timer gets installed which requests time every 40 seconds.
*
* To avoid race conditions, *none* of the code here must be called from an interrupt context unless
* the user can absolutely guarantee that there will never be a clock source rollover (which can be the
* case for sensor applications that only stay awake for a few seconds). And even then, do so at your
* own risk.
*
*
* Deep sleep is handled by moving the time offset forward *before* the sleep to the scheduled wakeup
* time. Due to the nature of the RTC, the actual wakeup time may be a little bit different, but
* it's the best that can be done. The code attempts to come up with a better calibration value if
* authoritative time is available both before and after a sleep; This works reasonably well, but of
* course is still merely a guess, which may well be somewhat wrong.
*
*/
#include <osapi.h>
#include <ets_sys.h>
#include "rom.h"
#include "rtcaccess.h"
#include "user_interface.h"
#include "eagle_soc.h"
#ifndef NOW
# define NOW() READ_PERI_REG(REG_RTC_BASE + FRC2_COUNT_ADDRESS)
#endif
// Layout of the RTC storage space:
//
// 0: Magic, and time source. Meaningful values are
// * RTC_TIME_MAGIC_SLEEP: Indicates that the device went to sleep under RTCTIME control.
// This is the magic expected on deep sleep wakeup; Any other status means we lost track
// of time, and whatever time offset is stored in state is invalid and must be cleared.
// * RTC_TIME_MAGIC_CCOUNT: Time offset is relative to the Cycle Counter.
// * RTC_TIME_MAGIC_FRC2: Time offset is relative to the Free Running Counter.
// Any values other than these indicate that RTCTIME is not in use and no state is available, nor should
// RTCTIME make any changes to any of the RTC memory space.
//
// 1/2: UNIX time in Unit Cycles when time source had value 0 (64 bit, lower 32 bit in 1, upper in 2).
// If 0, then time is unknown.
// 3: Last used value of time source (32 bit unsigned). If current time source is less, then a rollover happened
// 4: Length of a time source cycle in Unit Cycles.
// 5: cached result of sleep clock calibration. Has the format of system_rtc_clock_cali_proc(),
// or 0 if not available (see 6/7 below)
// 6: Number of microseconds we tried to sleep, or 0 if we didn't sleep since last calibration, ffffffff if invalid
// 7: Number of RTC cycles we decided to sleep, or 0 if we didn't sleep since last calibration, ffffffff if invalid
// 8: Number of microseconds which we add to (1/2) to avoid time going backwards
// 9: microsecond value returned in the last gettimeofday() to "user space".
//
// Entries 6-9 are needed because the RTC cycles/second appears quite temperature dependent,
// and thus is heavily influenced by what else the chip is doing. As such, any calibration against
// the crystal-provided clock (which necessarily would have to happen while the chip is active and
// burning a few milliwatts) will be significantly different from the actual frequency during deep
// sleep.
// Thus, in order to calibrate for deep sleep conditions, we keep track of total sleep microseconds
// and total sleep clock cycles between settimeofday() calls (which presumably are NTP driven), and
// adjust the calibration accordingly on each settimeofday(). This will also track frequency changes
// due to ambient temperature changes.
// 8/9 get used when a settimeofday() would result in turning back time. As that can cause all sorts
// of ugly issues, we *do* adjust (1/2), but compensate by making the same adjustment to (8). Then each
// time gettimeofday() is called, we inspect (9) and determine how much time has passed since the last
// call (yes, this gets it wrong if more than a second has passed, but not in a way that causes issues)
// and try to take up to 6% of that time away from (8) until (8) reaches 0. Also, whenever we go to
// deep sleep, we add (8) to the sleep time, thus catching up all in one go.
// Note that for calculating the next sample-aligned wakeup, we need to use the post-adjustment
// timeofday(), but for calculating actual sleep time, we use the pre-adjustment one, thus bringing
// things back into line.
//
#define RTC_TIME_BASE 0 // Where the RTC timekeeping block starts in RTC user memory slots
#define RTC_TIME_MAGIC_CCOUNT 0x44695573
#define RTC_TIME_MAGIC_FRC2 (RTC_TIME_MAGIC_CCOUNT+1)
#define RTC_TIME_MAGIC_SLEEP (RTC_TIME_MAGIC_CCOUNT+2)
#define UNITCYCLE_MHZ 2080
#define CPU_OVERCLOCK_MHZ 160
#define CPU_DEFAULT_MHZ 80
#define CPU_BOOTUP_MHZ 52
// RTCTIME storage
#define RTC_TIME_MAGIC_POS (RTC_TIME_BASE+0)
#define RTC_CYCLEOFFSETL_POS (RTC_TIME_BASE+1)
#define RTC_CYCLEOFFSETH_POS (RTC_TIME_BASE+2)
#define RTC_LASTSOURCEVAL_POS (RTC_TIME_BASE+3)
#define RTC_SOURCECYCLEUNITS_POS (RTC_TIME_BASE+4)
#define RTC_CALIBRATION_POS (RTC_TIME_BASE+5)
#define RTC_SLEEPTOTALUS_POS (RTC_TIME_BASE+6)
#define RTC_SLEEPTOTALCYCLES_POS (RTC_TIME_BASE+7)
#define RTC_TODOFFSETUS_POS (RTC_TIME_BASE+8)
#define RTC_LASTTODUS_POS (RTC_TIME_BASE+9)
struct rtc_timeval
{
uint32_t tv_sec;
uint32_t tv_usec;
};
static inline uint64_t rtc_time_get_now_us_adjusted();
static inline uint32_t rtc_time_get_magic(void)
{
return rtc_mem_read(RTC_TIME_MAGIC_POS);
}
static inline bool rtc_time_check_sleep_magic(void)
{
uint32_t magic=rtc_time_get_magic();
return (magic==RTC_TIME_MAGIC_SLEEP);
}
static inline bool rtc_time_check_wake_magic(void)
{
uint32_t magic=rtc_time_get_magic();
return (magic==RTC_TIME_MAGIC_FRC2 || magic==RTC_TIME_MAGIC_CCOUNT);
}
static inline bool rtc_time_check_magic(void)
{
uint32_t magic=rtc_time_get_magic();
return (magic==RTC_TIME_MAGIC_FRC2 || magic==RTC_TIME_MAGIC_CCOUNT || magic==RTC_TIME_MAGIC_SLEEP);
}
static inline void rtc_time_set_magic(uint32_t new_magic)
{
rtc_mem_write(RTC_TIME_MAGIC_POS,new_magic);
}
static inline void rtc_time_set_sleep_magic(void)
{
rtc_time_set_magic(RTC_TIME_MAGIC_SLEEP);
}
static inline void rtc_time_set_ccount_magic(void)
{
rtc_time_set_magic(RTC_TIME_MAGIC_CCOUNT);
}
static inline void rtc_time_set_frc2_magic(void)
{
rtc_time_set_magic(RTC_TIME_MAGIC_FRC2);
}
static inline void rtc_time_unset_magic(void)
{
rtc_mem_write(RTC_TIME_MAGIC_POS,0);
}
static inline uint32_t rtc_time_read_raw(void)
{
return rtc_reg_read(RTC_COUNTER_ADDR);
}
static inline uint32_t rtc_time_read_raw_ccount(void)
{
return xthal_get_ccount();
}
static inline uint32_t rtc_time_read_raw_frc2(void)
{
return NOW();
}
// Get us the number of Unit Cycles that have elapsed since the source was 0.
// Note: This may in fact adjust the stored cycles-when-source-was-0 entry, so
// we need to make sure we call this before reading that entry
static inline uint64_t rtc_time_source_offset(void)
{
uint32_t magic=rtc_time_get_magic();
uint32_t raw=0;
switch (magic)
{
case RTC_TIME_MAGIC_CCOUNT: raw=rtc_time_read_raw_ccount(); break;
case RTC_TIME_MAGIC_FRC2: raw=rtc_time_read_raw_frc2(); break;
default: return 0; // We are not in a position to offer time
}
uint32_t multiplier=rtc_mem_read(RTC_SOURCECYCLEUNITS_POS);
uint32_t previous=rtc_mem_read(RTC_LASTSOURCEVAL_POS);
if (raw<previous)
{ // We had a rollover.
uint64_t to_add=(1ULL<<32)*multiplier;
uint64_t base=rtc_mem_read64(RTC_CYCLEOFFSETL_POS);
if (base)
rtc_mem_write64(RTC_CYCLEOFFSETL_POS,base+to_add);
}
rtc_mem_write(RTC_LASTSOURCEVAL_POS,raw);
return ((uint64_t)raw)*multiplier;
}
static inline uint64_t rtc_time_unix_unitcycles(void)
{
// Note: The order of these two must be maintained, as the first call might change the outcome of the second
uint64_t offset=rtc_time_source_offset();
uint64_t base=rtc_mem_read64(RTC_CYCLEOFFSETL_POS);
if (!base)
return 0; // No known time
return base+offset;
}
static inline uint64_t rtc_time_unix_us(void)
{
return rtc_time_unix_unitcycles()/UNITCYCLE_MHZ;
}
static inline void rtc_time_register_time_reached(uint32_t s, uint32_t us)
{
rtc_mem_write(RTC_LASTTODUS_POS,us);
}
static inline uint32_t rtc_time_us_since_time_reached(uint32_t s, uint32_t us)
{
uint32_t lastus=rtc_mem_read(RTC_LASTTODUS_POS);
if (us<lastus)
us+=1000000;
return us-lastus;
}
// A small sanity check so sleep times go completely nuts if someone
// has provided wrong timestamps to gettimeofday.
static inline bool rtc_time_calibration_is_sane(uint32_t cali)
{
return (cali>=(4<<12)) && (cali<=(10<<12));
}
static inline uint32_t rtc_time_get_calibration(void)
{
uint32_t cal=rtc_time_check_magic()?rtc_mem_read(RTC_CALIBRATION_POS):0;
if (!cal)
{
// Make a first guess, most likely to be rather bad, but better then nothing.
#if !defined(BOOTLOADER_CODE) && defined(__ESP8266__) // This will pull in way too much of the system for the bootloader to handle.
ets_delay_us(200);
cal=system_rtc_clock_cali_proc();
rtc_mem_write(RTC_CALIBRATION_POS,cal);
#else
cal=6<<12;
#endif
}
return cal;
}
static inline void rtc_time_invalidate_calibration(void)
{
rtc_mem_write(RTC_CALIBRATION_POS,0);
}
static inline uint64_t rtc_time_us_to_ticks(uint64_t us)
{
uint32_t cal=rtc_time_get_calibration();
return (us<<12)/cal;
}
static inline uint64_t rtc_time_get_now_us_raw(void)
{
if (!rtc_time_check_magic())
return 0;
return rtc_time_unix_us();
}
static inline uint64_t rtc_time_get_now_us_adjusted(void)
{
uint64_t raw=rtc_time_get_now_us_raw();
if (!raw)
return 0;
return raw+rtc_mem_read(RTC_TODOFFSETUS_POS);
}
static inline void rtc_time_add_sleep_tracking(uint32_t us, uint32_t cycles)
{
if (rtc_time_check_magic())
{
// us is the one that will grow faster...
uint32_t us_before=rtc_mem_read(RTC_SLEEPTOTALUS_POS);
uint32_t us_after=us_before+us;
uint32_t cycles_after=rtc_mem_read(RTC_SLEEPTOTALCYCLES_POS)+cycles;
if (us_after<us_before) // Give up if it would cause an overflow
{
us_after=cycles_after=0xffffffff;
}
rtc_mem_write(RTC_SLEEPTOTALUS_POS, us_after);
rtc_mem_write(RTC_SLEEPTOTALCYCLES_POS,cycles_after);
}
}
static void rtc_time_enter_deep_sleep_us(uint32_t us)
{
if (rtc_time_check_wake_magic())
rtc_time_set_sleep_magic();
rtc_reg_write(0,0);
rtc_reg_write(0,rtc_reg_read(0)&0xffffbfff);
rtc_reg_write(0,rtc_reg_read(0)|0x30);
rtc_reg_write(0x44,4);
rtc_reg_write(0x0c,0x00010010);
rtc_reg_write(0x48,(rtc_reg_read(0x48)&0xffff01ff)|0x0000fc00);
rtc_reg_write(0x48,(rtc_reg_read(0x48)&0xfffffe00)|0x00000080);
rtc_reg_write(RTC_TARGET_ADDR,rtc_time_read_raw()+136);
rtc_reg_write(0x18,8);
rtc_reg_write(0x08,0x00100010);
ets_delay_us(20);
rtc_reg_write(0x9c,17);
rtc_reg_write(0xa0,3);
rtc_reg_write(0x0c,0x640c8);
rtc_reg_write(0,rtc_reg_read(0)&0xffffffcf);
uint32_t cycles=rtc_time_us_to_ticks(us);
rtc_time_add_sleep_tracking(us,cycles);
rtc_reg_write(RTC_TARGET_ADDR,rtc_time_read_raw()+cycles);
rtc_reg_write(0x9c,17);
rtc_reg_write(0xa0,3);
// Clear bit 0 of DPORT 0x04. Doesn't seem to be necessary
// wm(0x3fff0004,bitrm(0x3fff0004),0xfffffffe));
rtc_reg_write(0x40,-1);
rtc_reg_write(0x44,32);
rtc_reg_write(0x10,0);
rtc_reg_write(0x18,8);
rtc_reg_write(0x08,0x00100000); // go to sleep
}
static inline void rtc_time_deep_sleep_us(uint32_t us)
{
if (rtc_time_check_magic())
{
uint32_t to_adjust=rtc_mem_read(RTC_TODOFFSETUS_POS);
if (to_adjust)
{
us+=to_adjust;
rtc_mem_write(RTC_TODOFFSETUS_POS,0);
}
uint64_t now=rtc_time_get_now_us_raw(); // Now the same as _adjusted()
if (now)
{ // Need to maintain the clock first. When we wake up, counter will be 0
uint64_t wakeup=now+us;
uint64_t wakeup_cycles=wakeup*UNITCYCLE_MHZ;
rtc_mem_write64(RTC_CYCLEOFFSETL_POS,wakeup_cycles);
}
}
rtc_time_enter_deep_sleep_us(us);
}
static inline void rtc_time_deep_sleep_until_aligned(uint32_t align, uint32_t min_sleep_us)
{
uint64_t now=rtc_time_get_now_us_adjusted();
uint64_t then=now+min_sleep_us;
if (align)
{
then+=align-1;
then-=(then%align);
}
rtc_time_deep_sleep_us(then-now);
}
static inline void rtc_time_reset(bool clear_cali)
{
rtc_mem_write64(RTC_CYCLEOFFSETL_POS,0);
rtc_mem_write(RTC_SLEEPTOTALUS_POS,0);
rtc_mem_write(RTC_SLEEPTOTALCYCLES_POS,0);
rtc_mem_write(RTC_TODOFFSETUS_POS,0);
rtc_mem_write(RTC_LASTTODUS_POS,0);
rtc_mem_write(RTC_SOURCECYCLEUNITS_POS,0);
rtc_mem_write(RTC_LASTSOURCEVAL_POS,0);
if (clear_cali)
rtc_mem_write(RTC_CALIBRATION_POS,0);
}
static inline bool rtc_time_have_time(void)
{
return (rtc_time_check_magic() && rtc_mem_read64(RTC_CYCLEOFFSETL_POS)!=0);
}
static inline void rtc_time_select_frc2_source()
{
// FRC2 always runs at 1/256th of the default 80MHz clock, even if the actual clock is different
uint32_t new_multiplier=(256*UNITCYCLE_MHZ+CPU_DEFAULT_MHZ/2)/CPU_DEFAULT_MHZ;
uint64_t now;
uint32_t before;
uint32_t after;
// Deal with race condition here...
do {
before=rtc_time_read_raw_frc2();
now=rtc_time_unix_unitcycles();
after=rtc_time_read_raw_frc2();
} while (before>after);
if (rtc_time_have_time())
{
uint64_t offset=(uint64_t)after*new_multiplier;
rtc_mem_write64(RTC_CYCLEOFFSETL_POS,now-offset);
rtc_mem_write(RTC_LASTSOURCEVAL_POS,after);
}
rtc_mem_write(RTC_SOURCECYCLEUNITS_POS,new_multiplier);
rtc_mem_write(RTC_TIME_MAGIC_POS,RTC_TIME_MAGIC_FRC2);
}
static inline void rtc_time_select_ccount_source(uint32_t mhz, bool first)
{
uint32_t new_multiplier=(UNITCYCLE_MHZ+mhz/2)/mhz;
// Check that
if (new_multiplier*mhz!=UNITCYCLE_MHZ)
ets_printf("Trying to use unsuitable frequency: %dMHz\n",mhz);
if (first)
{ // The ccounter has been running at this rate since startup, and the offset is set accordingly
rtc_mem_write(RTC_LASTSOURCEVAL_POS,0);
rtc_mem_write(RTC_SOURCECYCLEUNITS_POS,new_multiplier);
rtc_mem_write(RTC_TIME_MAGIC_POS,RTC_TIME_MAGIC_CCOUNT);
return;
}
uint64_t now;
uint32_t before;
uint32_t after;
// Deal with race condition here...
do {
before=rtc_time_read_raw_ccount();
now=rtc_time_unix_unitcycles();
after=rtc_time_read_raw_ccount();
} while (before>after);
if (rtc_time_have_time())
{
uint64_t offset=(uint64_t)after*new_multiplier;
rtc_mem_write64(RTC_CYCLEOFFSETL_POS,now-offset);
rtc_mem_write(RTC_LASTSOURCEVAL_POS,after);
}
rtc_mem_write(RTC_SOURCECYCLEUNITS_POS,new_multiplier);
rtc_mem_write(RTC_TIME_MAGIC_POS,RTC_TIME_MAGIC_CCOUNT);
}
static inline void rtc_time_switch_to_ccount_frequency(uint32_t mhz)
{
if (rtc_time_check_magic())
rtc_time_select_ccount_source(mhz,false);
}
static inline void rtc_time_switch_to_system_clock(void)
{
if (rtc_time_check_magic())
rtc_time_select_frc2_source();
}
static inline void rtc_time_tmrfn(void* arg)
{
rtc_time_source_offset();
}
static inline void rtc_time_install_timer(void)
{
static os_timer_t tmr;
os_timer_setfn(&tmr,rtc_time_tmrfn,NULL);
os_timer_arm(&tmr,10000,1);
}
#if 0 // Kept around for reference....
static inline void rtc_time_ccount_wrap_handler(void* dst_v, uint32_t sp)
{
uint32_t off_h=rtc_mem_read(RTC_CYCLEOFFSETH_POS);
if (rtc_time_check_magic() && off_h)
{
rtc_mem_write(RTC_CYCLEOFFSETH_POS,off_h+1);
}
xthal_set_ccompare(0,0); // This resets the interrupt condition
}
static inline void rtc_time_install_wrap_handler(void)
{
xthal_set_ccompare(0,0); // Recognise a ccounter wraparound
ets_isr_attach(RTC_TIME_CCOMPARE_INT,rtc_time_ccount_wrap_handler,NULL);
ets_isr_unmask(1<<RTC_TIME_CCOMPARE_INT);
}
#endif
// This switches from MAGIC_SLEEP to MAGIC_CCOUNT, with ccount running at bootup frequency (i.e. 52MHz).
// To be called as early as possible, potententially as the first thing in an overridden entry point.
static inline void rtc_time_register_bootup(void)
{
uint32_t reset_reason=rtc_get_reset_reason();
#ifndef BOOTLOADER_CODE
static const bool erase_calibration=true;
#else
// In the boot loader, any leftover calibration is going to be better than anything we can
// come up with....
static const bool erase_calibration=false;
#endif
if (rtc_time_check_sleep_magic())
{
if (reset_reason!=2) // This was *not* a proper wakeup from a deep sleep. All our time keeping is f*cked!
rtc_time_reset(erase_calibration); // Possibly keep the calibration, it should still be good
rtc_time_select_ccount_source(CPU_BOOTUP_MHZ,true);
return;
}
if (rtc_time_check_magic())
{
// We did not go to sleep properly. All our time keeping is f*cked!
rtc_time_reset(erase_calibration); // Possibly keep the calibration, it should still be good
}
}
// Call this from the nodemcu entry point, i.e. just before we switch from 52MHz to 80MHz
static inline void rtc_time_switch_clocks(void)
{
rtc_time_switch_to_ccount_frequency(CPU_DEFAULT_MHZ);
}
// Call this exactly once, from user_init, i.e. once the operating system is up and running
static inline void rtc_time_switch_system(void)
{
rtc_time_install_timer();
rtc_time_switch_to_system_clock();
}
static inline void rtc_time_prepare(void)
{
rtc_time_reset(true);
rtc_time_select_frc2_source();
}
static inline void rtc_time_gettimeofday(struct rtc_timeval* tv)
{
uint64_t now=rtc_time_get_now_us_adjusted();
uint32_t sec=now/1000000;
uint32_t usec=now%1000000;
uint32_t to_adjust=rtc_mem_read(RTC_TODOFFSETUS_POS);
if (to_adjust)
{
uint32_t us_passed=rtc_time_us_since_time_reached(sec,usec);
uint32_t adjust=us_passed>>4;
if (adjust)
{
if (adjust>to_adjust)
adjust=to_adjust;
to_adjust-=adjust;
now-=adjust;
now/1000000;
now%1000000;
rtc_mem_write(RTC_TODOFFSETUS_POS,to_adjust);
}
}
tv->tv_sec=sec;
tv->tv_usec=usec;
rtc_time_register_time_reached(sec,usec);
}
static inline void rtc_time_settimeofday(const struct rtc_timeval* tv)
{
if (!rtc_time_check_magic())
return;
uint32_t sleep_us=rtc_mem_read(RTC_SLEEPTOTALUS_POS);
uint32_t sleep_cycles=rtc_mem_read(RTC_SLEEPTOTALCYCLES_POS);
// At this point, the CPU clock will definitely be at the default rate (nodemcu fully booted)
uint64_t now_esp_us=rtc_time_get_now_us_adjusted();
uint64_t now_ntp_us=((uint64_t)tv->tv_sec)*1000000+tv->tv_usec;
int64_t diff_us=now_esp_us-now_ntp_us;
// Store the *actual* time.
uint64_t target_unitcycles=now_ntp_us*UNITCYCLE_MHZ;
uint64_t sourcecycles=rtc_time_source_offset();
rtc_mem_write64(RTC_CYCLEOFFSETL_POS,target_unitcycles-sourcecycles);
// calibrate sleep period based on difference between expected time and actual time
if (sleep_us>0 && sleep_us<0xffffffff &&
sleep_cycles>0 && sleep_cycles<0xffffffff)
{
uint64_t actual_sleep_us=sleep_us-diff_us;
uint32_t cali=(actual_sleep_us<<12)/sleep_cycles;
if (rtc_time_calibration_is_sane(cali))
rtc_mem_write(RTC_CALIBRATION_POS,cali);
}
rtc_mem_write(RTC_SLEEPTOTALUS_POS,0);
rtc_mem_write(RTC_SLEEPTOTALCYCLES_POS,0);
// Deal with time adjustment if necessary
if (diff_us>0) // Time went backwards. Avoid that....
{
if (diff_us>0xffffffffULL)
diff_us=0xffffffffULL;
now_ntp_us+=diff_us;
}
else
diff_us=0;
rtc_mem_write(RTC_TODOFFSETUS_POS,diff_us);
uint32_t now_s=now_ntp_us/1000000;
uint32_t now_us=now_ntp_us%1000000;
rtc_time_register_time_reached(now_s,now_us);
}
#endif
#ifndef _RTOS_DBG_H_
#define _RTOS_DBG_H_
#include <stdint.h>
#define rtos_dbg_here() do{rtos_dbg_task_print(__FILE__,__LINE__);} while(0)
#define rtos_dbg_stack_here() do{rtos_dbg_stack_print(__FILE__,__LINE__);} while(0)
void rtos_dbg_task_print (const char *file, uint32_t line);
void rtos_dbg_stack_print (const char *file, uint32_t line);
#endif
#ifndef _SECTIONS_H_
#define _SECTIONS_H_
#define TEXT_SECTION_ATTR __attribute__((section(".text")))
#endif
#ifndef _TASK_H_
#define _TASK_H_
#include <stdint.h>
#include <stdbool.h>
#include <c_types.h>
/* use LOW / MEDIUM / HIGH since it isn't clear from the docs which is higher */
typedef enum {
TASK_PRIORITY_LOW,
TASK_PRIORITY_MEDIUM,
TASK_PRIORITY_HIGH,
TASK_PRIORITY_COUNT
} task_prio_t;
typedef uint32_t task_handle_t;
typedef intptr_t task_param_t;
/*
* Signals are a 32-bit number of the form header:14; count:18. The header
* is just a fixed fingerprint and the count is allocated serially by the
* task_get_id() function.
*/
bool task_post(task_prio_t priority, task_handle_t handle, task_param_t param);
#define task_post_low(handle,param) task_post(TASK_PRIORITY_LOW, handle, param)
#define task_post_medium(handle,param) task_post(TASK_PRIORITY_MEDIUM, handle, param)
#define task_post_high(handle,param) task_post(TASK_PRIORITY_HIGH, handle, param)
typedef void (*task_callback_t)(task_param_t param, task_prio_t prio);
bool task_init_handler(task_prio_t priority, uint8 qlen);
task_handle_t task_get_id(task_callback_t t);
/* RTOS loop to pump task messages until infinity */
void task_pump_messages (void);
#endif
#ifndef __U8G_CONFIG_H__
#define __U8G_CONFIG_H__
// ***************************************************************************
// Configure U8glib fonts
//
// Add a U8G_FONT_TABLE_ENTRY for each font you want to compile into the image
#define U8G_FONT_TABLE_ENTRY(font)
#define U8G_FONT_TABLE \
U8G_FONT_TABLE_ENTRY(font_6x10) \
U8G_FONT_TABLE_ENTRY(font_chikita)
#undef U8G_FONT_TABLE_ENTRY
//
// ***************************************************************************
// ***************************************************************************
// Enable display drivers
//
// Uncomment the U8G_DISPLAY_TABLE_ENTRY for the device(s) you want to
// compile into the firmware.
// Stick to the assignments to *_I2C and *_SPI tables.
//
// I2C based displays go into here:
// U8G_DISPLAY_TABLE_ENTRY(sh1106_128x64_i2c) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_i2c) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1306_64x48_i2c) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1309_128x64_i2c) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1327_96x96_gr_i2c) \
// U8G_DISPLAY_TABLE_ENTRY(uc1611_dogm240_i2c) \
// U8G_DISPLAY_TABLE_ENTRY(uc1611_dogxl240_i2c) \
#define U8G_DISPLAY_TABLE_ENTRY(device)
#define U8G_DISPLAY_TABLE_I2C \
U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_i2c) \
// SPI based displays go into here:
// U8G_DISPLAY_TABLE_ENTRY(ld7032_60x32_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(pcd8544_84x48_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(pcf8812_96x65_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(sh1106_128x64_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1306_64x48_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1309_128x64_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1322_nhd31oled_bw_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1322_nhd31oled_gr_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1325_nhd27oled_bw_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1325_nhd27oled_gr_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1327_96x96_gr_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1351_128x128_332_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1351_128x128gh_332_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1351_128x128_hicolor_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1351_128x128gh_hicolor_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1353_160x128_332_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(ssd1353_160x128_hicolor_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(st7565_64128n_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(st7565_dogm128_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(st7565_dogm132_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(st7565_lm6059_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(st7565_lm6063_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(st7565_nhd_c12832_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(st7565_nhd_c12864_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(uc1601_c128032_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(uc1608_240x128_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(uc1608_240x64_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(uc1610_dogxl160_bw_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(uc1610_dogxl160_gr_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(uc1611_dogm240_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(uc1611_dogxl240_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(uc1701_dogs102_hw_spi) \
// U8G_DISPLAY_TABLE_ENTRY(uc1701_mini12864_hw_spi) \
#define U8G_DISPLAY_TABLE_SPI \
U8G_DISPLAY_TABLE_ENTRY(ssd1306_128x64_hw_spi) \
#undef U8G_DISPLAY_TABLE_ENTRY
//
// ***************************************************************************
#endif /* __U8G_CONFIG_H__ */
#ifndef __UCG_CONFIG_H__
#define __UCG_CONFIG_H__
// ***************************************************************************
// Configure Ucglib fonts
//
// Add a UCG_FONT_TABLE_ENTRY for each font you want to compile into the image
#define UCG_FONT_TABLE_ENTRY(font)
#define UCG_FONT_TABLE \
UCG_FONT_TABLE_ENTRY(font_7x13B_tr) \
UCG_FONT_TABLE_ENTRY(font_helvB08_hr) \
UCG_FONT_TABLE_ENTRY(font_helvB10_hr) \
UCG_FONT_TABLE_ENTRY(font_helvB12_hr) \
UCG_FONT_TABLE_ENTRY(font_helvB18_hr) \
UCG_FONT_TABLE_ENTRY(font_ncenB24_tr) \
UCG_FONT_TABLE_ENTRY(font_ncenR12_tr) \
UCG_FONT_TABLE_ENTRY(font_ncenR14_hr)
#undef UCG_FONT_TABLE_ENTRY
//
// ***************************************************************************
// ***************************************************************************
// Enable display drivers
//
// Uncomment the UCG_DISPLAY_TABLE_ENTRY for the device(s) you want to
// compile into the firmware.
//
// UCG_DISPLAY_TABLE_ENTRY(ili9163_18x128x128_hw_spi, ucg_dev_ili9163_18x128x128, ucg_ext_ili9163_18) \
// UCG_DISPLAY_TABLE_ENTRY(ili9341_18x240x320_hw_spi, ucg_dev_ili9341_18x240x320, ucg_ext_ili9341_18) \
// UCG_DISPLAY_TABLE_ENTRY(pcf8833_16x132x132_hw_spi, ucg_dev_pcf8833_16x132x132, ucg_ext_pcf8833_16) \
// UCG_DISPLAY_TABLE_ENTRY(seps225_16x128x128_uvis_hw_spi, ucg_dev_seps225_16x128x128_univision, ucg_ext_seps225_16) \
// UCG_DISPLAY_TABLE_ENTRY(ssd1351_18x128x128_hw_spi, ucg_dev_ssd1351_18x128x128_ilsoft, ucg_ext_ssd1351_18) \
// UCG_DISPLAY_TABLE_ENTRY(ssd1351_18x128x128_ft_hw_spi, ucg_dev_ssd1351_18x128x128_ft, ucg_ext_ssd1351_18) \
// UCG_DISPLAY_TABLE_ENTRY(ssd1331_18x96x64_uvis_hw_spi, ucg_dev_ssd1331_18x96x64_univision, ucg_ext_ssd1331_18) \
// UCG_DISPLAY_TABLE_ENTRY(st7735_18x128x160_hw_spi, ucg_dev_st7735_18x128x160, ucg_ext_st7735_18) \
#define UCG_DISPLAY_TABLE_ENTRY(binding, device, extension)
#define UCG_DISPLAY_TABLE \
UCG_DISPLAY_TABLE_ENTRY(ili9341_18x240x320_hw_spi, ucg_dev_ili9341_18x240x320, ucg_ext_ili9341_18) \
UCG_DISPLAY_TABLE_ENTRY(st7735_18x128x160_hw_spi, ucg_dev_st7735_18x128x160, ucg_ext_st7735_18) \
#undef UCG_DISPLAY_TABLE_ENTRY
//
// ***************************************************************************
#endif /* __UCG_CONFIG_H__ */
#ifndef __USER_CONFIG_H__
#define __USER_CONFIG_H__
// #define DEVKIT_VERSION_0_9 1 // define this only if you use NodeMCU devkit v0.9
// #define FLASH_512K
// #define FLASH_1M
// #define FLASH_2M
// #define FLASH_4M
// #define FLASH_8M
// #define FLASH_16M
#define FLASH_AUTOSIZE
#define FLASH_SAFE_API
// This adds the asserts in LUA. It also adds some useful extras to the
// node module. This is all silent in normal operation and so can be enabled
// without any harm (except for the code size increase and slight slowdown)
//#define DEVELOPMENT_TOOLS
#ifdef DEVELOPMENT_TOOLS
extern void luaL_assertfail(const char *file, int line, const char *message);
#define lua_assert(x) ((x) ? (void) 0 : luaL_assertfail(__FILE__, __LINE__, #x))
#endif
// This enables lots of debug output and changes the serial bit rate. This
// is normally only used by hardcore developers
// #define DEVELOP_VERSION
#ifdef DEVELOP_VERSION
#define NODE_DEBUG
#define COAP_DEBUG
#endif /* DEVELOP_VERSION */
#define BIT_RATE_DEFAULT BIT_RATE_115200
// This enables automatic baud rate detection at startup
#define BIT_RATE_AUTOBAUD
#define NODE_ERROR
#ifdef NODE_DEBUG
#define NODE_DBG printf
#else
#define NODE_DBG
#endif /* NODE_DEBUG */
#ifdef NODE_ERROR
#define NODE_ERR printf
#else
#define NODE_ERR
#endif /* NODE_ERROR */
#define GPIO_INTERRUPT_ENABLE
#define GPIO_INTERRUPT_HOOK_ENABLE
// #define GPIO_SAFE_NO_INTR_ENABLE
#define ICACHE_STORE_TYPEDEF_ATTR __attribute__((aligned(4),packed))
#define ICACHE_STORE_ATTR __attribute__((aligned(4)))
#define ICACHE_RAM_ATTR __attribute__((section(".iram0.text")))
#ifdef GPIO_SAFE_NO_INTR_ENABLE
#define NO_INTR_CODE ICACHE_RAM_ATTR __attribute__ ((noinline))
#else
#define NO_INTR_CODE inline
#endif
//#define CLIENT_SSL_ENABLE
//#define MD2_ENABLE
#define SHA2_ENABLE
#define BUILD_SPIFFS 1
#define SPIFFS_CACHE 1
// #define LUA_NUMBER_INTEGRAL
#define READLINE_INTERVAL 80
#define LUA_TASK_PRIO USER_TASK_PRIO_0
#define LUA_PROCESS_LINE_SIG 2
#define LUA_OPTIMIZE_DEBUG 2
#ifdef DEVKIT_VERSION_0_9
#define KEYLED_INTERVAL 80
#define KEY_SHORT_MS 200
#define KEY_LONG_MS 3000
#define KEY_SHORT_COUNT (KEY_SHORT_MS / READLINE_INTERVAL)
#define KEY_LONG_COUNT (KEY_LONG_MS / READLINE_INTERVAL)
#define LED_HIGH_COUNT_DEFAULT 10
#define LED_LOW_COUNT_DEFAULT 0
#endif
#define ENDUSER_SETUP_AP_SSID "SetupGadget"
/*
* A valid hostname only contains alphanumeric and hyphen(-) characters, with no hyphens at first or last char
* if WIFI_STA_HOSTNAME not defined: hostname will default to NODE-xxxxxx (xxxxxx being last 3 octets of MAC address)
* if WIFI_STA_HOSTNAME defined: hostname must only contain alphanumeric characters
* if WIFI_STA_HOSTNAME_APPEND_MAC not defined: Hostname MUST be 32 chars or less
* if WIFI_STA_HOSTNAME_APPEND_MAC defined: Hostname MUST be 26 chars or less, since last 3 octets of MAC address will be appended
* if defined hostname is invalid: hostname will default to NODE-xxxxxx (xxxxxx being last 3 octets of MAC address)
*/
//#define WIFI_STA_HOSTNAME "NodeMCU"
//#define WIFI_STA_HOSTNAME_APPEND_MAC
//#define WIFI_SMART_ENABLE
#define WIFI_STATION_STATUS_MONITOR_ENABLE
#define WIFI_SDK_EVENT_MONITOR_ENABLE
#define WIFI_EVENT_MONITOR_DISCONNECT_REASON_LIST_ENABLE
#define STRBUF_DEFAULT_INCREMENT 32
#endif /* __USER_CONFIG_H__ */
#ifndef __USER_MODULES_H__
#define __USER_MODULES_H__
#define LUA_USE_BUILTIN_STRING // for string.xxx()
#define LUA_USE_BUILTIN_TABLE // for table.xxx()
#define LUA_USE_BUILTIN_COROUTINE // for coroutine.xxx()
#define LUA_USE_BUILTIN_MATH // for math.xxx(), partially work
// #define LUA_USE_BUILTIN_IO // for io.xxx(), partially work
// #define LUA_USE_BUILTIN_OS // for os.xxx(), not work
// #define LUA_USE_BUILTIN_DEBUG
#define LUA_USE_BUILTIN_DEBUG_MINIMAL // for debug.getregistry() and debug.traceback()
#ifndef LUA_CROSS_COMPILER
// The default configuration is designed to run on all ESP modules including the 512 KB modules like ESP-01 and only
// includes general purpose interface modules which require at most two GPIO pins.
// See https://github.com/nodemcu/nodemcu-firmware/pull/1127 for discussions.
// New modules should be disabled by default and added in alphabetical order.
#define LUA_USE_MODULES_ADC
//#define LUA_USE_MODULES_AM2320
//#define LUA_USE_MODULES_APA102
#define LUA_USE_MODULES_BIT
//#define LUA_USE_MODULES_BMP085
//#define LUA_USE_MODULES_BME280
//#define LUA_USE_MODULES_CJSON
//#define LUA_USE_MODULES_COAP
//#define LUA_USE_MODULES_CRYPTO
#define LUA_USE_MODULES_DHT
//#define LUA_USE_MODULES_ENCODER
//#define LUA_USE_MODULES_ENDUSER_SETUP // USE_DNS in dhcpserver.h needs to be enabled for this module to work.
#define LUA_USE_MODULES_FILE
#define LUA_USE_MODULES_GPIO
//#define LUA_USE_MODULES_HTTP
//#define LUA_USE_MODULES_HX711
#define LUA_USE_MODULES_I2C
//#define LUA_USE_MODULES_MDNS
#define LUA_USE_MODULES_MQTT
#define LUA_USE_MODULES_NET
#define LUA_USE_MODULES_NODE
#define LUA_USE_MODULES_OW
//#define LUA_USE_MODULES_PERF
//#define LUA_USE_MODULES_PWM
//#define LUA_USE_MODULES_RC
//#define LUA_USE_MODULES_ROTARY
//#define LUA_USE_MODULES_RTCFIFO
//#define LUA_USE_MODULES_RTCMEM
//#define LUA_USE_MODULES_RTCTIME
//#define LUA_USE_MODULES_SIGMA_DELTA
#define LUA_USE_MODULES_SNTP
#define LUA_USE_MODULES_SPI
//#define LUA_USE_MODULES_STRUCT
#define LUA_USE_MODULES_TMR
//#define LUA_USE_MODULES_TSL2561
//#define LUA_USE_MODULES_U8G
#define LUA_USE_MODULES_UART
//#define LUA_USE_MODULES_UCG
#define LUA_USE_MODULES_WIFI
//#define LUA_USE_MODULES_WS2801
//#define LUA_USE_MODULES_WS2812
#endif /* LUA_CROSS_COMPILER */
#endif /* __USER_MODULES_H__ */
#ifndef __USER_VERSION_H__
#define __USER_VERSION_H__
#define NODE_VERSION_MAJOR 1U
#define NODE_VERSION_MINOR 5U
#define NODE_VERSION_REVISION 1U
#define NODE_VERSION_INTERNAL 0U
#define NODE_VERSION "NodeMCU 1.5.1"
#ifndef BUILD_DATE
#define BUILD_DATE "unspecified"
#endif
#define SDK_VERSION "RTOS"
#endif /* __USER_VERSION_H__ */
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of images to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
ifndef PDIR
GEN_LIBS = libjson.a
endif
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#DEFINES +=
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile
/*
* Copyright (c) 2011-2012, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*/
#ifdef JSON_FORMAT
#include "json/jsonparse.h"
#include "osapi.h"
//#include <stdlib.h>
//#include <string.h>
/*--------------------------------------------------------------------*/
static int ICACHE_FLASH_ATTR
push(struct jsonparse_state *state, char c)
{
state->stack[state->depth] = c;
state->depth++;
state->vtype = 0;
return state->depth < JSONPARSE_MAX_DEPTH;
}
/*--------------------------------------------------------------------*/
static char ICACHE_FLASH_ATTR
pop(struct jsonparse_state *state)
{
if(state->depth == 0) {
return JSON_TYPE_ERROR;
}
state->depth--;
return state->stack[state->depth];
}
/*--------------------------------------------------------------------*/
/* will pass by the value and store the start and length of the value for
atomic types */
/*--------------------------------------------------------------------*/
static void ICACHE_FLASH_ATTR
atomic(struct jsonparse_state *state, char type)
{
char c;
state->vstart = state->pos;
state->vtype = type;
if(type == JSON_TYPE_STRING || type == JSON_TYPE_PAIR_NAME) {
while((c = state->json[state->pos++]) && c != '"') {
if(c == '\\') {
state->pos++; /* skip current char */
}
}
state->vlen = state->pos - state->vstart - 1;
} else if(type == JSON_TYPE_NUMBER) {
do {
c = state->json[state->pos];
if((c < '0' || c > '9') && c != '.') {
c = 0;
} else {
state->pos++;
}
} while(c);
/* need to back one step since first char is already gone */
state->vstart--;
state->vlen = state->pos - state->vstart;
}
/* no other types for now... */
}
/*--------------------------------------------------------------------*/
static void ICACHE_FLASH_ATTR
skip_ws(struct jsonparse_state *state)
{
char c;
while(state->pos < state->len &&
((c = state->json[state->pos]) == ' ' || c == '\n')) {
state->pos++;
}
}
/*--------------------------------------------------------------------*/
void ICACHE_FLASH_ATTR
jsonparse_setup(struct jsonparse_state *state, const char *json, int len)
{
state->json = json;
state->len = len;
state->pos = 0;
state->depth = 0;
state->error = 0;
state->stack[0] = 0;
}
/*--------------------------------------------------------------------*/
int ICACHE_FLASH_ATTR
jsonparse_next(struct jsonparse_state *state)
{
char c;
char s;
skip_ws(state);
c = state->json[state->pos];
s = jsonparse_get_type(state);
state->pos++;
switch(c) {
case '{':
push(state, c);
return c;
case '}':
if(s == ':' && state->vtype != 0) {
/* printf("Popping vtype: '%c'\n", state->vtype); */
pop(state);
s = jsonparse_get_type(state);
}
if(s == '{') {
pop(state);
} else {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
return c;
case ']':
if(s == '[') {
pop(state);
} else {
state->error = JSON_ERROR_UNEXPECTED_END_OF_ARRAY;
return JSON_TYPE_ERROR;
}
return c;
case ':':
push(state, c);
return c;
case ',':
/* if x:y ... , */
if(s == ':' && state->vtype != 0) {
pop(state);
} else if(s == '[') {
/* ok! */
} else {
state->error = JSON_ERROR_SYNTAX;
return JSON_TYPE_ERROR;
}
return c;
case '"':
if(s == '{' || s == '[' || s == ':') {
atomic(state, c = (s == '{' ? JSON_TYPE_PAIR_NAME : c));
} else {
state->error = JSON_ERROR_UNEXPECTED_STRING;
return JSON_TYPE_ERROR;
}
return c;
case '[':
if(s == '{' || s == '[' || s == ':') {
push(state, c);
} else {
state->error = JSON_ERROR_UNEXPECTED_ARRAY;
return JSON_TYPE_ERROR;
}
return c;
default:
if(s == ':' || s == '[') {
if(c <= '9' && c >= '0') {
atomic(state, JSON_TYPE_NUMBER);
return JSON_TYPE_NUMBER;
}
}
}
return 0;
}
/*--------------------------------------------------------------------*/
/* get the json value of the current position
* works only on "atomic" values such as string, number, null, false, true
*/
/*--------------------------------------------------------------------*/
int ICACHE_FLASH_ATTR
jsonparse_copy_value(struct jsonparse_state *state, char *str, int size)
{
int i;
char z = 0;
char y = 0;
if(state->vtype == 0) {
return 0;
}
size = size <= state->vlen ? (size - 1) : state->vlen;
for(i = 0; i < size; i++) {
if (y == 0 && state->json[state->vstart + i] == '\\') {
y = 1;
z++;
continue;
}
y = 0;
str[i - z] = state->json[state->vstart + i];
}
str[i - z] = 0;
return state->vtype;
}
/*--------------------------------------------------------------------*/
int ICACHE_FLASH_ATTR
jsonparse_get_value_as_int(struct jsonparse_state *state)
{
if(state->vtype != JSON_TYPE_NUMBER) {
return 0;
}
return atoi(&state->json[state->vstart]);
}
/*--------------------------------------------------------------------*/
long ICACHE_FLASH_ATTR
jsonparse_get_value_as_long(struct jsonparse_state *state)
{
if(state->vtype != JSON_TYPE_NUMBER) {
return 0;
}
return atol(&state->json[state->vstart]);
}
/*--------------------------------------------------------------------*/
unsigned long ICACHE_FLASH_ATTR
jsonparse_get_value_as_ulong(struct jsonparse_state *state)
{
if(state->vtype != JSON_TYPE_NUMBER) {
return 0;
}
return strtoul(&state->json[state->vstart], '\0', 0);
}
/*--------------------------------------------------------------------*/
/* strcmp - assume no strange chars that needs to be stuffed in string... */
/*--------------------------------------------------------------------*/
int ICACHE_FLASH_ATTR
jsonparse_strcmp_value(struct jsonparse_state *state, const char *str)
{
if(state->vtype == 0) {
return -1;
}
return os_strncmp(str, &state->json[state->vstart], state->vlen);
}
/*--------------------------------------------------------------------*/
int ICACHE_FLASH_ATTR
jsonparse_get_len(struct jsonparse_state *state)
{
return state->vlen;
}
/*--------------------------------------------------------------------*/
int ICACHE_FLASH_ATTR
jsonparse_get_type(struct jsonparse_state *state)
{
if(state->depth == 0) {
return 0;
}
return state->stack[state->depth - 1];
}
/*--------------------------------------------------------------------*/
int ICACHE_FLASH_ATTR
jsonparse_has_next(struct jsonparse_state *state)
{
return state->pos < state->len;
}
/*--------------------------------------------------------------------*/
#endif
/*
* Copyright (c) 2011-2012, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*/
/**
* \file
* JSON output generation
* \author
* Niclas Finne <nfi@sics.se>
* Joakim Eriksson <joakime@sics.se>
*/
#ifdef JSON_FORMAT
//#include "contiki.h"
#include "json/jsontree.h"
#include "json/jsonparse.h"
#include "osapi.h"
//#include <string.h>
#define DEBUG 0
#if DEBUG
//#include <stdio.h>
#define PRINTF(...) os_printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
/*---------------------------------------------------------------------------*/
void ICACHE_FLASH_ATTR
jsontree_write_atom(const struct jsontree_context *js_ctx, const char *text)
{
if(text == NULL) {
js_ctx->putchar('0');
} else {
while(*text != '\0') {
js_ctx->putchar(*text++);
}
}
}
/*---------------------------------------------------------------------------*/
void ICACHE_FLASH_ATTR
jsontree_write_string(const struct jsontree_context *js_ctx, const char *text)
{
js_ctx->putchar('"');
if(text != NULL) {
while(*text != '\0') {
if(*text == '"') {
js_ctx->putchar('\\');
}
js_ctx->putchar(*text++);
}
}
js_ctx->putchar('"');
}
/*---------------------------------------------------------------------------*/
void ICACHE_FLASH_ATTR
jsontree_write_int(const struct jsontree_context *js_ctx, int value)
{
char buf[10];
int l;
if(value < 0) {
js_ctx->putchar('-');
value = -value;
}
l = sizeof(buf) - 1;
do {
buf[l--] = '0' + (value % 10);
value /= 10;
} while(value > 0 && l >= 0);
while(++l < sizeof(buf)) {
js_ctx->putchar(buf[l]);
}
}
/*---------------------------------------------------------------------------*/
void ICACHE_FLASH_ATTR
jsontree_write_int_array(const struct jsontree_context *js_ctx, const int *text, uint32 length)
{
uint32 i = 0;
if(text == NULL) {
js_ctx->putchar('0');
} else {
for (i = 0; i < length - 1; i ++) {
jsontree_write_int(js_ctx, *text++);
js_ctx->putchar(',');
}
jsontree_write_int(js_ctx, *text);
}
}
/*---------------------------------------------------------------------------*/
void ICACHE_FLASH_ATTR
jsontree_setup(struct jsontree_context *js_ctx, struct jsontree_value *root,
int (* putchar)(int))
{
js_ctx->values[0] = root;
js_ctx->putchar = putchar;
js_ctx->path = 0;
jsontree_reset(js_ctx);
}
/*---------------------------------------------------------------------------*/
void ICACHE_FLASH_ATTR
jsontree_reset(struct jsontree_context *js_ctx)
{
js_ctx->depth = 0;
js_ctx->index[0] = 0;
}
/*---------------------------------------------------------------------------*/
const char *ICACHE_FLASH_ATTR
jsontree_path_name(const struct jsontree_context *js_ctx, int depth)
{
if(depth < js_ctx->depth && js_ctx->values[depth]->type == JSON_TYPE_OBJECT) {
return ((struct jsontree_object *)js_ctx->values[depth])->
pairs[js_ctx->index[depth]].name;
}
return "";
}
/*---------------------------------------------------------------------------*/
int ICACHE_FLASH_ATTR
jsontree_print_next(struct jsontree_context *js_ctx)
{
struct jsontree_value *v;
int index;
v = js_ctx->values[js_ctx->depth];
/* Default operation after switch is to back up one level */
switch(v->type) {
case JSON_TYPE_OBJECT:
case JSON_TYPE_ARRAY: {
struct jsontree_array *o = (struct jsontree_array *)v;
struct jsontree_value *ov;
index = js_ctx->index[js_ctx->depth];
if(index == 0) {
js_ctx->putchar(v->type);
js_ctx->putchar('\n');
}
if(index >= o->count) {
js_ctx->putchar('\n');
js_ctx->putchar(v->type + 2);
/* Default operation: back up one level! */
break;
}
if(index > 0) {
js_ctx->putchar(',');
js_ctx->putchar('\n');
}
if(v->type == JSON_TYPE_OBJECT) {
jsontree_write_string(js_ctx,
((struct jsontree_object *)o)->pairs[index].name);
js_ctx->putchar(':');
ov = ((struct jsontree_object *)o)->pairs[index].value;
} else {
ov = o->values[index];
}
/* TODO check max depth */
js_ctx->depth++; /* step down to value... */
js_ctx->index[js_ctx->depth] = 0; /* and init index */
js_ctx->values[js_ctx->depth] = ov;
/* Continue on this new level */
return 1;
}
case JSON_TYPE_STRING:
jsontree_write_string(js_ctx, ((struct jsontree_string *)v)->value);
/* Default operation: back up one level! */
break;
case JSON_TYPE_INT:
jsontree_write_int(js_ctx, ((struct jsontree_int *)v)->value);
/* Default operation: back up one level! */
break;
case JSON_TYPE_CALLBACK: { /* pre-formatted json string currently */
struct jsontree_callback *callback;
callback = (struct jsontree_callback *)v;
if(js_ctx->index[js_ctx->depth] == 0) {
/* First call: reset the callback status */
js_ctx->callback_state = 0;
}
if(callback->output == NULL) {
jsontree_write_string(js_ctx, "");
} else if(callback->output(js_ctx)) {
/* The callback wants to output more */
js_ctx->index[js_ctx->depth]++;
return 1;
}
/* Default operation: back up one level! */
break;
}
default:
PRINTF("\nError: Illegal json type:'%c'\n", v->type);
return 0;
}
/* Done => back up one level! */
if(js_ctx->depth > 0) {
js_ctx->depth--;
js_ctx->index[js_ctx->depth]++;
return 1;
}
return 0;
}
/*---------------------------------------------------------------------------*/
static struct jsontree_value *ICACHE_FLASH_ATTR
find_next(struct jsontree_context *js_ctx)
{
struct jsontree_value *v;
int index;
do {
v = js_ctx->values[js_ctx->depth];
/* Default operation after switch is to back up one level */
switch(v->type) {
case JSON_TYPE_OBJECT:
case JSON_TYPE_ARRAY: {
struct jsontree_array *o = (struct jsontree_array *)v;
struct jsontree_value *ov;
index = js_ctx->index[js_ctx->depth];
if(index >= o->count) {
/* Default operation: back up one level! */
break;
}
if(v->type == JSON_TYPE_OBJECT) {
ov = ((struct jsontree_object *)o)->pairs[index].value;
} else {
ov = o->values[index];
}
/* TODO check max depth */
js_ctx->depth++; /* step down to value... */
js_ctx->index[js_ctx->depth] = 0; /* and init index */
js_ctx->values[js_ctx->depth] = ov;
/* Continue on this new level */
return ov;
}
default:
/* Default operation: back up one level! */
break;
}
/* Done => back up one level! */
if(js_ctx->depth > 0) {
js_ctx->depth--;
js_ctx->index[js_ctx->depth]++;
} else {
return NULL;
}
} while(1);
}
/*---------------------------------------------------------------------------*/
struct jsontree_value *ICACHE_FLASH_ATTR
jsontree_find_next(struct jsontree_context *js_ctx, int type)
{
struct jsontree_value *v;
while((v = find_next(js_ctx)) != NULL && v->type != type &&
js_ctx->path < js_ctx->depth) {
/* search */
}
js_ctx->callback_state = 0;
return js_ctx->path < js_ctx->depth ? v : NULL;
}
/*---------------------------------------------------------------------------*/
#endif
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of images to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
ifndef PDIR
GEN_LIBS = liblua.a
endif
STD_CFLAGS=-std=gnu11 -Wimplicit
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#DEFINES +=
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
INCLUDES += -I ../spiffs
INCLUDES += -I ../libc
INCLUDES += -I ../modules
INCLUDES += -I ../platform
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile
/**
* define start/end address of ro data.
*/
#ifndef __COMPILER_H__
#define __COMPILER_H__
#if defined(__ESP8266__)
extern char _irom0_text_start;
extern char _irom0_text_end;
#define RODATA_START_ADDRESS (&_irom0_text_start)
#define RODATA_END_ADDRESS (&_irom0_text_end)
#elif defined(__ESP32__)
extern char _drom0_text_start;
extern char _drom0_text_end;
#define RODATA_START_ADDRESS (&_drom0_text_start)
#define RODATA_END_ADDRESS (&_drom0_text_end)
#else // other compilers
/* Firstly, modify rodata's start/end address. Then, comment the line below */
#error "Please modify RODATA_START_ADDRESS and RODATA_END_ADDRESS below."
/* Perhaps you can use start/end address of flash */
#define RODATA_START_ADDRESS ((char*)0x40200000)
#define RODATA_END_ADDRESS ((char*)0x40280000)
#endif
#endif // __COMPILER_H__
/*
** $Id: lapi.c,v 2.55.1.5 2008/07/04 18:41:18 roberto Exp $
** Lua API
** See Copyright Notice in lua.h
*/
#define lapi_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
//#include C_HEADER_ASSERT
#include C_HEADER_MATH
#include C_HEADER_STRING
#include "lapi.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lgc.h"
#include "lmem.h"
#include "lobject.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lundump.h"
#include "lvm.h"
#include "lrotable.h"
#if 0
const char lua_ident[] =
"$Lua: " LUA_RELEASE " " LUA_COPYRIGHT " $\n"
"$Authors: " LUA_AUTHORS " $\n"
"$URL: www.lua.org $\n";
#endif
#define api_checknelems(L, n) api_check(L, (n) <= (L->top - L->base))
#define api_checkvalidindex(L, i) api_check(L, (i) != luaO_nilobject)
#define api_incr_top(L) {api_check(L, L->top < L->ci->top); L->top++;}
static TValue *index2adr (lua_State *L, int idx) {
if (idx > 0) {
TValue *o = L->base + (idx - 1);
api_check(L, idx <= L->ci->top - L->base);
if (o >= L->top) return cast(TValue *, luaO_nilobject);
else return o;
}
else if (idx > LUA_REGISTRYINDEX) {
api_check(L, idx != 0 && -idx <= L->top - L->base);
return L->top + idx;
}
else switch (idx) { /* pseudo-indices */
case LUA_REGISTRYINDEX: return registry(L);
case LUA_ENVIRONINDEX: {
Closure *func = curr_func(L);
sethvalue(L, &L->env, func ? func->c.env : hvalue(gt(L)));
return &L->env;
}
case LUA_GLOBALSINDEX: return gt(L);
default: {
Closure *func = curr_func(L);
if (!func) return cast(TValue *, luaO_nilobject);
idx = LUA_GLOBALSINDEX - idx;
return (idx <= func->c.nupvalues)
? &func->c.upvalue[idx-1]
: cast(TValue *, luaO_nilobject);
}
}
}
static Table *getcurrenv (lua_State *L) {
if (L->ci == L->base_ci) /* no enclosing function? */
return hvalue(gt(L)); /* use global table as environment */
else {
Closure *func = curr_func(L);
return func ? func->c.env : hvalue(gt(L));
}
}
void luaA_pushobject (lua_State *L, const TValue *o) {
setobj2s(L, L->top, o);
api_incr_top(L);
}
LUA_API int lua_checkstack (lua_State *L, int size) {
int res = 1;
lua_lock(L);
if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK)
res = 0; /* stack overflow */
else if (size > 0) {
luaD_checkstack(L, size);
if (L->ci->top < L->top + size)
L->ci->top = L->top + size;
}
lua_unlock(L);
return res;
}
LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) {
int i;
if (from == to) return;
lua_lock(to);
api_checknelems(from, n);
api_check(from, G(from) == G(to));
api_check(from, to->ci->top - to->top >= n);
from->top -= n;
for (i = 0; i < n; i++) {
setobj2s(to, to->top++, from->top + i);
}
lua_unlock(to);
}
LUA_API void lua_setlevel (lua_State *from, lua_State *to) {
to->nCcalls = from->nCcalls;
}
LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) {
lua_CFunction old;
lua_lock(L);
old = G(L)->panic;
G(L)->panic = panicf;
lua_unlock(L);
return old;
}
LUA_API lua_State *lua_newthread (lua_State *L) {
lua_State *L1;
lua_lock(L);
luaC_checkGC(L);
L1 = luaE_newthread(L);
setthvalue(L, L->top, L1);
api_incr_top(L);
lua_unlock(L);
luai_userstatethread(L, L1);
return L1;
}
/*
** basic stack manipulation
*/
LUA_API int lua_gettop (lua_State *L) {
return cast_int(L->top - L->base);
}
LUA_API void lua_settop (lua_State *L, int idx) {
lua_lock(L);
if (idx >= 0) {
api_check(L, idx <= L->stack_last - L->base);
while (L->top < L->base + idx)
setnilvalue(L->top++);
L->top = L->base + idx;
}
else {
api_check(L, -(idx+1) <= (L->top - L->base));
L->top += idx+1; /* `subtract' index (index is negative) */
}
lua_unlock(L);
}
LUA_API void lua_remove (lua_State *L, int idx) {
StkId p;
lua_lock(L);
p = index2adr(L, idx);
api_checkvalidindex(L, p);
while (++p < L->top) setobjs2s(L, p-1, p);
L->top--;
lua_unlock(L);
}
LUA_API void lua_insert (lua_State *L, int idx) {
StkId p;
StkId q;
lua_lock(L);
p = index2adr(L, idx);
api_checkvalidindex(L, p);
for (q = L->top; q>p; q--) setobjs2s(L, q, q-1);
setobjs2s(L, p, L->top);
lua_unlock(L);
}
LUA_API void lua_replace (lua_State *L, int idx) {
StkId o;
lua_lock(L);
/* explicit test for incompatible code */
if (idx == LUA_ENVIRONINDEX && L->ci == L->base_ci)
luaG_runerror(L, "no calling environment");
api_checknelems(L, 1);
o = index2adr(L, idx);
api_checkvalidindex(L, o);
if (idx == LUA_ENVIRONINDEX) {
Closure *func = curr_func(L);
if (!func)
luaG_runerror(L, "attempt to set environment on lightfunction");
else {
api_check(L, ttistable(L->top - 1));
func->c.env = hvalue(L->top - 1);
luaC_barrier(L, func, L->top - 1);
}
}
else {
setobj(L, o, L->top - 1);
if (curr_func(L) && idx < LUA_GLOBALSINDEX) /* function upvalue? */
luaC_barrier(L, curr_func(L), L->top - 1);
}
L->top--;
lua_unlock(L);
}
LUA_API void lua_pushvalue (lua_State *L, int idx) {
lua_lock(L);
setobj2s(L, L->top, index2adr(L, idx));
api_incr_top(L);
lua_unlock(L);
}
/*
** access functions (stack -> C)
*/
LUA_API int lua_type (lua_State *L, int idx) {
StkId o = index2adr(L, idx);
return (o == luaO_nilobject) ? LUA_TNONE : ttype(o);
}
LUA_API const char *lua_typename (lua_State *L, int t) {
UNUSED(L);
return (t == LUA_TNONE) ? "no value" : luaT_typenames[t];
}
LUA_API int lua_iscfunction (lua_State *L, int idx) {
StkId o = index2adr(L, idx);
return iscfunction(o);
}
LUA_API int lua_isnumber (lua_State *L, int idx) {
TValue n;
const TValue *o = index2adr(L, idx);
return tonumber(o, &n);
}
LUA_API int lua_isstring (lua_State *L, int idx) {
int t = lua_type(L, idx);
return (t == LUA_TSTRING || t == LUA_TNUMBER);
}
LUA_API int lua_isuserdata (lua_State *L, int idx) {
const TValue *o = index2adr(L, idx);
return (ttisuserdata(o) || ttislightuserdata(o));
}
LUA_API int lua_rawequal (lua_State *L, int index1, int index2) {
StkId o1 = index2adr(L, index1);
StkId o2 = index2adr(L, index2);
return (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0
: luaO_rawequalObj(o1, o2);
}
LUA_API int lua_equal (lua_State *L, int index1, int index2) {
StkId o1, o2;
int i;
lua_lock(L); /* may call tag method */
o1 = index2adr(L, index1);
o2 = index2adr(L, index2);
i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 : equalobj(L, o1, o2);
lua_unlock(L);
return i;
}
LUA_API int lua_lessthan (lua_State *L, int index1, int index2) {
StkId o1, o2;
int i;
lua_lock(L); /* may call tag method */
o1 = index2adr(L, index1);
o2 = index2adr(L, index2);
i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0
: luaV_lessthan(L, o1, o2);
lua_unlock(L);
return i;
}
LUA_API lua_Number lua_tonumber (lua_State *L, int idx) {
TValue n;
const TValue *o = index2adr(L, idx);
if (tonumber(o, &n))
return nvalue(o);
else
return 0;
}
LUA_API lua_Integer lua_tointeger (lua_State *L, int idx) {
TValue n;
const TValue *o = index2adr(L, idx);
if (tonumber(o, &n)) {
lua_Integer res;
lua_Number num = nvalue(o);
lua_number2integer(res, num);
return res;
}
else
return 0;
}
LUA_API int lua_toboolean (lua_State *L, int idx) {
const TValue *o = index2adr(L, idx);
return !l_isfalse(o);
}
LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {
StkId o = index2adr(L, idx);
if (!ttisstring(o)) {
lua_lock(L); /* `luaV_tostring' may create a new string */
if (!luaV_tostring(L, o)) { /* conversion failed? */
if (len != NULL) *len = 0;
lua_unlock(L);
return NULL;
}
luaC_checkGC(L);
o = index2adr(L, idx); /* previous call may reallocate the stack */
lua_unlock(L);
}
if (len != NULL) *len = tsvalue(o)->len;
return svalue(o);
}
LUA_API size_t lua_objlen (lua_State *L, int idx) {
StkId o = index2adr(L, idx);
switch (ttype(o)) {
case LUA_TSTRING: return tsvalue(o)->len;
case LUA_TUSERDATA: return uvalue(o)->len;
case LUA_TTABLE: return luaH_getn(hvalue(o));
case LUA_TROTABLE: return luaH_getn_ro(rvalue(o));
case LUA_TNUMBER: {
size_t l;
lua_lock(L); /* `luaV_tostring' may create a new string */
l = (luaV_tostring(L, o) ? tsvalue(o)->len : 0);
lua_unlock(L);
return l;
}
default: return 0;
}
}
LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) {
StkId o = index2adr(L, idx);
return (!iscfunction(o)) ? NULL : clvalue(o)->c.f;
}
LUA_API void *lua_touserdata (lua_State *L, int idx) {
StkId o = index2adr(L, idx);
switch (ttype(o)) {
case LUA_TUSERDATA: return (rawuvalue(o) + 1);
case LUA_TLIGHTUSERDATA: return pvalue(o);
default: return NULL;
}
}
LUA_API lua_State *lua_tothread (lua_State *L, int idx) {
StkId o = index2adr(L, idx);
return (!ttisthread(o)) ? NULL : thvalue(o);
}
LUA_API const void *lua_topointer (lua_State *L, int idx) {
StkId o = index2adr(L, idx);
switch (ttype(o)) {
case LUA_TTABLE: return hvalue(o);
case LUA_TFUNCTION: return clvalue(o);
case LUA_TTHREAD: return thvalue(o);
case LUA_TUSERDATA:
case LUA_TLIGHTUSERDATA:
return lua_touserdata(L, idx);
case LUA_TROTABLE:
case LUA_TLIGHTFUNCTION:
return pvalue(o);
default: return NULL;
}
}
/*
** push functions (C -> stack)
*/
LUA_API void lua_pushnil (lua_State *L) {
lua_lock(L);
setnilvalue(L->top);
api_incr_top(L);
lua_unlock(L);
}
LUA_API void lua_pushnumber (lua_State *L, lua_Number n) {
lua_lock(L);
setnvalue(L->top, n);
api_incr_top(L);
lua_unlock(L);
}
LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) {
lua_lock(L);
setnvalue(L->top, cast_num(n));
api_incr_top(L);
lua_unlock(L);
}
LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len) {
lua_lock(L);
luaC_checkGC(L);
setsvalue2s(L, L->top, luaS_newlstr(L, s, len));
api_incr_top(L);
lua_unlock(L);
}
LUA_API void lua_pushrolstring (lua_State *L, const char *s, size_t len) {
lua_lock(L);
luaC_checkGC(L);
setsvalue2s(L, L->top, luaS_newrolstr(L, s, len));
api_incr_top(L);
lua_unlock(L);
}
LUA_API void lua_pushstring (lua_State *L, const char *s) {
if (s == NULL)
lua_pushnil(L);
else
lua_pushlstring(L, s, strlen(s));
}
LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt,
va_list argp) {
const char *ret;
lua_lock(L);
luaC_checkGC(L);
ret = luaO_pushvfstring(L, fmt, argp);
lua_unlock(L);
return ret;
}
LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) {
const char *ret;
va_list argp;
lua_lock(L);
luaC_checkGC(L);
va_start(argp, fmt);
ret = luaO_pushvfstring(L, fmt, argp);
va_end(argp);
lua_unlock(L);
return ret;
}
LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
Closure *cl;
lua_lock(L);
luaC_checkGC(L);
api_checknelems(L, n);
cl = luaF_newCclosure(L, n, getcurrenv(L));
cl->c.f = fn;
L->top -= n;
while (n--)
setobj2n(L, &cl->c.upvalue[n], L->top+n);
setclvalue(L, L->top, cl);
lua_assert(iswhite(obj2gco(cl)));
api_incr_top(L);
lua_unlock(L);
}
LUA_API void lua_pushboolean (lua_State *L, int b) {
lua_lock(L);
setbvalue(L->top, (b != 0)); /* ensure that true is 1 */
api_incr_top(L);
lua_unlock(L);
}
LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
lua_lock(L);
setpvalue(L->top, p);
api_incr_top(L);
lua_unlock(L);
}
LUA_API void lua_pushrotable (lua_State *L, void *p) {
lua_lock(L);
setrvalue(L->top, p);
api_incr_top(L);
lua_unlock(L);
}
LUA_API void lua_pushlightfunction(lua_State *L, void *p) {
lua_lock(L);
setfvalue(L->top, p);
api_incr_top(L);
lua_unlock(L);
}
LUA_API int lua_pushthread (lua_State *L) {
lua_lock(L);
setthvalue(L, L->top, L);
api_incr_top(L);
lua_unlock(L);
return (G(L)->mainthread == L);
}
/*
** get functions (Lua -> stack)
*/
LUA_API void lua_gettable (lua_State *L, int idx) {
StkId t;
lua_lock(L);
t = index2adr(L, idx);
api_checkvalidindex(L, t);
luaV_gettable(L, t, L->top - 1, L->top - 1);
lua_unlock(L);
}
LUA_API void lua_getfield (lua_State *L, int idx, const char *k) {
StkId t;
TValue key;
lua_lock(L);
t = index2adr(L, idx);
api_checkvalidindex(L, t);
fixedstack(L);
setsvalue(L, &key, luaS_new(L, k));
unfixedstack(L);
luaV_gettable(L, t, &key, L->top);
api_incr_top(L);
lua_unlock(L);
}
LUA_API void lua_rawget (lua_State *L, int idx) {
StkId t;
const TValue *res;
lua_lock(L);
t = index2adr(L, idx);
api_check(L, ttistable(t) || ttisrotable(t));
res = ttistable(t) ? luaH_get(hvalue(t), L->top - 1) : luaH_get_ro(rvalue(t), L->top - 1);
setobj2s(L, L->top - 1, res);
lua_unlock(L);
}
LUA_API void lua_rawgeti (lua_State *L, int idx, int n) {
StkId o;
lua_lock(L);
o = index2adr(L, idx);
api_check(L, ttistable(o) || ttisrotable(o));
setobj2s(L, L->top, ttistable(o) ? luaH_getnum(hvalue(o), n) : luaH_getnum_ro(rvalue(o), n))
api_incr_top(L);
lua_unlock(L);
}
LUA_API void lua_createtable (lua_State *L, int narray, int nrec) {
lua_lock(L);
luaC_checkGC(L);
sethvalue(L, L->top, luaH_new(L, narray, nrec));
api_incr_top(L);
lua_unlock(L);
}
LUA_API int lua_getmetatable (lua_State *L, int objindex) {
const TValue *obj;
Table *mt = NULL;
int res;
lua_lock(L);
obj = index2adr(L, objindex);
switch (ttype(obj)) {
case LUA_TTABLE:
mt = hvalue(obj)->metatable;
break;
case LUA_TUSERDATA:
mt = uvalue(obj)->metatable;
break;
case LUA_TROTABLE:
mt = (Table*)luaR_getmeta(rvalue(obj));
break;
default:
mt = G(L)->mt[ttype(obj)];
break;
}
if (mt == NULL)
res = 0;
else {
if(luaR_isrotable(mt))
setrvalue(L->top, mt)
else
sethvalue(L, L->top, mt)
api_incr_top(L);
res = 1;
}
lua_unlock(L);
return res;
}
LUA_API void lua_getfenv (lua_State *L, int idx) {
StkId o;
lua_lock(L);
o = index2adr(L, idx);
api_checkvalidindex(L, o);
switch (ttype(o)) {
case LUA_TFUNCTION:
sethvalue(L, L->top, clvalue(o)->c.env);
break;
case LUA_TUSERDATA:
sethvalue(L, L->top, uvalue(o)->env);
break;
case LUA_TTHREAD:
setobj2s(L, L->top, gt(thvalue(o)));
break;
default:
setnilvalue(L->top);
break;
}
api_incr_top(L);
lua_unlock(L);
}
/*
** set functions (stack -> Lua)
*/
LUA_API void lua_settable (lua_State *L, int idx) {
StkId t;
lua_lock(L);
api_checknelems(L, 2);
t = index2adr(L, idx);
api_checkvalidindex(L, t);
luaV_settable(L, t, L->top - 2, L->top - 1);
L->top -= 2; /* pop index and value */
lua_unlock(L);
}
LUA_API void lua_setfield (lua_State *L, int idx, const char *k) {
StkId t;
lua_lock(L);
api_checknelems(L, 1);
t = index2adr(L, idx);
api_checkvalidindex(L, t);
setsvalue2s(L, L->top, luaS_new(L, k));
api_incr_top(L);
luaV_settable(L, t, L->top - 1, L->top - 2);
L->top -= 2; /* pop key and value */
lua_unlock(L);
}
LUA_API void lua_rawset (lua_State *L, int idx) {
StkId t;
lua_lock(L);
api_checknelems(L, 2);
t = index2adr(L, idx);
api_check(L, ttistable(t));
fixedstack(L);
setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1);
unfixedstack(L);
luaC_barriert(L, hvalue(t), L->top-1);
L->top -= 2;
lua_unlock(L);
}
LUA_API void lua_rawseti (lua_State *L, int idx, int n) {
StkId o;
lua_lock(L);
api_checknelems(L, 1);
o = index2adr(L, idx);
api_check(L, ttistable(o));
fixedstack(L);
setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top-1);
unfixedstack(L);
luaC_barriert(L, hvalue(o), L->top-1);
L->top--;
lua_unlock(L);
}
LUA_API int lua_setmetatable (lua_State *L, int objindex) {
TValue *obj;
Table *mt;
int isrometa = 0;
lua_lock(L);
api_checknelems(L, 1);
obj = index2adr(L, objindex);
api_checkvalidindex(L, obj);
if (ttisnil(L->top - 1))
mt = NULL;
else {
api_check(L, ttistable(L->top - 1) || ttisrotable(L->top - 1));
if (ttistable(L->top - 1))
mt = hvalue(L->top - 1);
else {
mt = (Table*)rvalue(L->top - 1);
isrometa = 1;
}
}
switch (ttype(obj)) {
case LUA_TTABLE: {
hvalue(obj)->metatable = mt;
if (mt && !isrometa)
luaC_objbarriert(L, hvalue(obj), mt);
break;
}
case LUA_TUSERDATA: {
uvalue(obj)->metatable = mt;
if (mt && !isrometa)
luaC_objbarrier(L, rawuvalue(obj), mt);
break;
}
default: {
G(L)->mt[ttype(obj)] = mt;
break;
}
}
L->top--;
lua_unlock(L);
return 1;
}
LUA_API int lua_setfenv (lua_State *L, int idx) {
StkId o;
int res = 1;
lua_lock(L);
api_checknelems(L, 1);
o = index2adr(L, idx);
api_checkvalidindex(L, o);
api_check(L, ttistable(L->top - 1));
switch (ttype(o)) {
case LUA_TFUNCTION:
clvalue(o)->c.env = hvalue(L->top - 1);
break;
case LUA_TUSERDATA:
uvalue(o)->env = hvalue(L->top - 1);
break;
case LUA_TTHREAD:
sethvalue(L, gt(thvalue(o)), hvalue(L->top - 1));
break;
default:
res = 0;
break;
}
if (res) luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1));
L->top--;
lua_unlock(L);
return res;
}
/*
** `load' and `call' functions (run Lua code)
*/
#define adjustresults(L,nres) \
{ if (nres == LUA_MULTRET && L->top >= L->ci->top) L->ci->top = L->top; }
#define checkresults(L,na,nr) \
api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)))
LUA_API void lua_call (lua_State *L, int nargs, int nresults) {
StkId func;
lua_lock(L);
api_checknelems(L, nargs+1);
checkresults(L, nargs, nresults);
func = L->top - (nargs+1);
luaD_call(L, func, nresults);
adjustresults(L, nresults);
lua_unlock(L);
}
/*
** Execute a protected call.
*/
struct CallS { /* data to `f_call' */
StkId func;
int nresults;
};
static void f_call (lua_State *L, void *ud) {
struct CallS *c = cast(struct CallS *, ud);
luaD_call(L, c->func, c->nresults);
}
LUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) {
struct CallS c;
int status;
ptrdiff_t func;
lua_lock(L);
api_checknelems(L, nargs+1);
checkresults(L, nargs, nresults);
if (errfunc == 0)
func = 0;
else {
StkId o = index2adr(L, errfunc);
api_checkvalidindex(L, o);
func = savestack(L, o);
}
c.func = L->top - (nargs+1); /* function to be called */
c.nresults = nresults;
status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
adjustresults(L, nresults);
lua_unlock(L);
return status;
}
/*
** Execute a protected C call.
*/
struct CCallS { /* data to `f_Ccall' */
lua_CFunction func;
void *ud;
};
static void f_Ccall (lua_State *L, void *ud) {
struct CCallS *c = cast(struct CCallS *, ud);
Closure *cl;
cl = luaF_newCclosure(L, 0, getcurrenv(L));
cl->c.f = c->func;
setclvalue(L, L->top, cl); /* push function */
api_incr_top(L);
setpvalue(L->top, c->ud); /* push only argument */
api_incr_top(L);
luaD_call(L, L->top - 2, 0);
}
LUA_API int lua_cpcall (lua_State *L, lua_CFunction func, void *ud) {
struct CCallS c;
int status;
lua_lock(L);
c.func = func;
c.ud = ud;
status = luaD_pcall(L, f_Ccall, &c, savestack(L, L->top), 0);
lua_unlock(L);
return status;
}
LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
const char *chunkname) {
ZIO z;
int status;
lua_lock(L);
if (!chunkname) chunkname = "?";
luaZ_init(L, &z, reader, data);
status = luaD_protectedparser(L, &z, chunkname);
lua_unlock(L);
return status;
}
LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data) {
int status;
TValue *o;
lua_lock(L);
api_checknelems(L, 1);
o = L->top - 1;
if (isLfunction(o))
status = luaU_dump(L, clvalue(o)->l.p, writer, data, 0);
else
status = 1;
lua_unlock(L);
return status;
}
LUA_API int lua_status (lua_State *L) {
return L->status;
}
/*
** Garbage-collection function
*/
LUA_API int lua_gc (lua_State *L, int what, int data) {
int res = 0;
global_State *g;
lua_lock(L);
g = G(L);
switch (what) {
case LUA_GCSTOP: {
set_block_gc(L);
break;
}
case LUA_GCRESTART: {
unset_block_gc(L);
break;
}
case LUA_GCCOLLECT: {
luaC_fullgc(L);
break;
}
case LUA_GCCOUNT: {
/* GC values are expressed in Kbytes: #bytes/2^10 */
res = cast_int(g->totalbytes >> 10);
break;
}
case LUA_GCCOUNTB: {
res = cast_int(g->totalbytes & 0x3ff);
break;
}
case LUA_GCSTEP: {
if(is_block_gc(L)) {
res = 1; /* gc is block so we need to pretend that the collection cycle finished. */
break;
}
lu_mem a = (cast(lu_mem, data) << 10);
if (a <= g->totalbytes)
g->GCthreshold = g->totalbytes - a;
else
g->GCthreshold = 0;
while (g->GCthreshold <= g->totalbytes) {
luaC_step(L);
if (g->gcstate == GCSpause) { /* end of cycle? */
res = 1; /* signal it */
break;
}
}
break;
}
case LUA_GCSETPAUSE: {
res = g->gcpause;
g->gcpause = data;
break;
}
case LUA_GCSETSTEPMUL: {
res = g->gcstepmul;
g->gcstepmul = data;
break;
}
case LUA_GCSETMEMLIMIT: {
/* GC values are expressed in Kbytes: #bytes/2^10 */
lu_mem new_memlimit = (cast(lu_mem, data) << 10);
if(new_memlimit > 0 && new_memlimit < g->totalbytes) {
/* run a full GC to make totalbytes < the new limit. */
luaC_fullgc(L);
if(new_memlimit < g->totalbytes)
new_memlimit = (g->totalbytes + 1024) & ~(1024-1); /* round up to next multiple of 1024 */
}
g->memlimit = new_memlimit;
/* new memlimit might be > then requested memlimit. */
res = cast_int(new_memlimit >> 10);
break;
}
case LUA_GCGETMEMLIMIT: {
res = cast_int(g->memlimit >> 10);
break;
}
default: res = -1; /* invalid option */
}
lua_unlock(L);
return res;
}
/*
** miscellaneous functions
*/
LUA_API int lua_error (lua_State *L) {
lua_lock(L);
api_checknelems(L, 1);
luaG_errormsg(L);
lua_unlock(L);
return 0; /* to avoid warnings */
}
LUA_API int lua_next (lua_State *L, int idx) {
StkId t;
int more;
lua_lock(L);
t = index2adr(L, idx);
api_check(L, ttistable(t) || ttisrotable(t));
more = ttistable(t) ? luaH_next(L, hvalue(t), L->top - 1) : luaH_next_ro(L, rvalue(t), L->top - 1);
if (more) {
api_incr_top(L);
}
else /* no more elements */
L->top -= 1; /* remove key */
lua_unlock(L);
return more;
}
LUA_API void lua_concat (lua_State *L, int n) {
lua_lock(L);
api_checknelems(L, n);
if (n >= 2) {
luaC_checkGC(L);
luaV_concat(L, n, cast_int(L->top - L->base) - 1);
L->top -= (n-1);
}
else if (n == 0) { /* push empty string */
setsvalue2s(L, L->top, luaS_newlstr(L, "", 0));
api_incr_top(L);
}
/* else n == 1; nothing to do */
lua_unlock(L);
}
LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) {
lua_Alloc f;
lua_lock(L);
if (ud) *ud = G(L)->ud;
f = G(L)->frealloc;
lua_unlock(L);
return f;
}
LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) {
lua_lock(L);
G(L)->ud = ud;
G(L)->frealloc = f;
lua_unlock(L);
}
LUA_API void *lua_newuserdata (lua_State *L, size_t size) {
Udata *u;
lua_lock(L);
luaC_checkGC(L);
u = luaS_newudata(L, size, getcurrenv(L));
setuvalue(L, L->top, u);
api_incr_top(L);
lua_unlock(L);
return u + 1;
}
static const char *aux_upvalue (StkId fi, int n, TValue **val) {
Closure *f;
if (!ttisfunction(fi)) return NULL;
f = clvalue(fi);
if (f->c.isC) {
if (!(1 <= n && n <= f->c.nupvalues)) return NULL;
*val = &f->c.upvalue[n-1];
return "";
}
else {
Proto *p = f->l.p;
if (!(1 <= n && n <= p->sizeupvalues)) return NULL;
*val = f->l.upvals[n-1]->v;
return getstr(p->upvalues[n-1]);
}
}
LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) {
const char *name;
TValue *val;
lua_lock(L);
name = aux_upvalue(index2adr(L, funcindex), n, &val);
if (name) {
setobj2s(L, L->top, val);
api_incr_top(L);
}
lua_unlock(L);
return name;
}
LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
const char *name;
TValue *val;
StkId fi;
lua_lock(L);
fi = index2adr(L, funcindex);
api_checknelems(L, 1);
name = aux_upvalue(fi, n, &val);
if (name) {
L->top--;
setobj(L, val, L->top);
luaC_barrier(L, clvalue(fi), L->top);
}
lua_unlock(L);
return name;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment