Unverified Commit 67027c0d authored by Marcel Stör's avatar Marcel Stör Committed by GitHub
Browse files

Merge pull request #2340 from nodemcu/dev

2.2 master snap
parents 5073c199 18f33f5f
......@@ -31,6 +31,14 @@ static evt_queue_t *wifi_event_queue_head; //pointer to beginning of queue
static evt_queue_t *wifi_event_queue_tail; //pointer to end of queue
static int wifi_event_cb_ref[EVENT_MAX+1] = { [0 ... EVENT_MAX] = LUA_NOREF}; //holds references to registered Lua callbacks
#ifdef LUA_USE_MODULES_WIFI_MONITOR
static int (*hook_fn)(System_Event_t *);
void wifi_event_monitor_register_hook(int (*fn)(System_Event_t*)) {
hook_fn = fn;
}
#endif
// wifi.eventmon.register()
int wifi_event_monitor_register(lua_State* L)
{
......@@ -58,6 +66,12 @@ static void wifi_event_monitor_handle_event_cb(System_Event_t *evt)
{
EVENT_DBG("\n\twifi_event_monitor_handle_event_cb is called\n");
#ifdef LUA_USE_MODULES_WIFI_MONITOR
if (hook_fn && hook_fn(evt)) {
return;
}
#endif
if((wifi_event_cb_ref[evt->event] != LUA_NOREF) || ((wifi_event_cb_ref[EVENT_MAX] != LUA_NOREF) &&
!(evt->event == EVENT_STAMODE_CONNECTED || evt->event == EVENT_STAMODE_DISCONNECTED ||
evt->event == EVENT_STAMODE_AUTHMODE_CHANGE || evt->event == EVENT_STAMODE_GOT_IP ||
......
// Module for interfacing with WIFI
#include "module.h"
#include "lauxlib.h"
#include "lapi.h"
#include "platform.h"
#include "c_string.h"
#include "c_stdlib.h"
#include "ctype.h"
#include "c_types.h"
#include "user_interface.h"
#include "wifi_common.h"
#include "sys/network_80211.h"
static int recv_cb;
static uint8 mon_offset;
static uint8 mon_value;
static uint8 mon_mask;
static task_handle_t tasknumber;
#define SNIFFER_BUF2_BUF_SIZE 112
#define BITFIELD(byte, start, len) 8 * (byte) + (start), (len)
#define BYTEFIELD(byte, len) 8 * (byte), 8 * (len)
#define IS_SIGNED 1
#define IS_STRING 2
#define IS_HEXSTRING 3
#define IE_TABLE 4
#define SINGLE_IE 5
#define IS_HEADER 6
#define ANY_FRAME 16
#define IE(id) 0, (id), SINGLE_IE
static void (*on_disconnected)(void);
typedef struct {
const char *key;
uint8 frametype;
} typekey_t;
typedef struct {
const char *name;
unsigned int start : 12;
unsigned int length : 12;
unsigned int opts : 3;
unsigned int frametype : 5; // 16 is *any*
} field_t;
// must be sorted alphabetically
static const field_t fields[] = {
{ "aggregation", BITFIELD(7, 3, 1), 0, ANY_FRAME},
{ "ampdu_cnt", BITFIELD(9, 0, 8), 0, ANY_FRAME},
{ "association_id", BYTEFIELD(40, 2), 0, FRAME_SUBTYPE_ASSOC_RESPONSE},
{ "association_id", BYTEFIELD(40, 2), 0, FRAME_SUBTYPE_REASSOC_RESPONSE},
{ "authentication_algorithm", BYTEFIELD(36, 2), 0, FRAME_SUBTYPE_AUTHENTICATION},
{ "authentication_transaction", BYTEFIELD(38, 2), 0, FRAME_SUBTYPE_AUTHENTICATION},
{ "beacon_interval", BYTEFIELD(44, 2), 0, FRAME_SUBTYPE_PROBE_RESPONSE},
{ "beacon_interval", BYTEFIELD(44, 2), 0, FRAME_SUBTYPE_BEACON},
{ "bssid", BYTEFIELD(28, 6), IS_STRING, ANY_FRAME},
{ "bssid_hex", BYTEFIELD(28, 6), IS_HEXSTRING, ANY_FRAME},
{ "bssidmatch0", BITFIELD(3, 6, 1), 0, ANY_FRAME},
{ "bssidmatch1", BITFIELD(3, 7, 1), 0, ANY_FRAME},
{ "capability", BYTEFIELD(36, 2), 0, FRAME_SUBTYPE_ASSOC_REQUEST},
{ "capability", BYTEFIELD(36, 2), 0, FRAME_SUBTYPE_ASSOC_RESPONSE},
{ "capability", BYTEFIELD(36, 2), 0, FRAME_SUBTYPE_REASSOC_REQUEST},
{ "capability", BYTEFIELD(36, 2), 0, FRAME_SUBTYPE_REASSOC_RESPONSE},
{ "capability", BYTEFIELD(46, 2), 0, FRAME_SUBTYPE_PROBE_RESPONSE},
{ "capability", BYTEFIELD(46, 2), 0, FRAME_SUBTYPE_BEACON},
{ "channel", BITFIELD(10, 0, 4), 0, ANY_FRAME},
{ "current_ap", BYTEFIELD(40, 6), IS_STRING, FRAME_SUBTYPE_REASSOC_REQUEST},
{ "cwb", BITFIELD(4, 7, 1), 0, ANY_FRAME},
{ "dmatch0", BITFIELD(3, 4, 1), 0, ANY_FRAME},
{ "dmatch1", BITFIELD(3, 5, 1), 0, ANY_FRAME},
{ "dstmac", BYTEFIELD(22, 6), IS_STRING, ANY_FRAME},
{ "dstmac_hex", BYTEFIELD(22, 6), IS_HEXSTRING, ANY_FRAME},
{ "duration", BYTEFIELD(14, 2), 0, ANY_FRAME},
{ "fec_coding", BITFIELD(7, 6, 1), 0, ANY_FRAME},
{ "frame", BYTEFIELD(12, 112), IS_STRING, ANY_FRAME},
{ "frame_hex", BYTEFIELD(12, 112), IS_HEXSTRING, ANY_FRAME},
{ "fromds", BITFIELD(13, 1, 1), 0, ANY_FRAME},
{ "header", BYTEFIELD(12, 0), IS_HEADER, ANY_FRAME},
{ "ht_length", BITFIELD(5, 0, 16), 0, ANY_FRAME},
{ "ie_20_40_bss_coexistence", IE(72), ANY_FRAME},
{ "ie_20_40_bss_intolerant_channel_report", IE(73), ANY_FRAME},
{ "ie_advertisement_protocol", IE(108), ANY_FRAME},
{ "ie_aid", IE(197), ANY_FRAME},
{ "ie_antenna", IE(64), ANY_FRAME},
{ "ie_ap_channel_report", IE(51), ANY_FRAME},
{ "ie_authenticated_mesh_peering_exchange", IE(139), ANY_FRAME},
{ "ie_beacon_timing", IE(120), ANY_FRAME},
{ "ie_bss_ac_access_delay", IE(68), ANY_FRAME},
{ "ie_bss_available_admission_capacity", IE(67), ANY_FRAME},
{ "ie_bss_average_access_delay", IE(63), ANY_FRAME},
{ "ie_bss_load", IE(11), ANY_FRAME},
{ "ie_bss_max_idle_period", IE(90), ANY_FRAME},
{ "ie_cf_parameter_set", IE(4), ANY_FRAME},
{ "ie_challenge_text", IE(16), ANY_FRAME},
{ "ie_channel_switch_announcement", IE(37), ANY_FRAME},
{ "ie_channel_switch_timing", IE(104), ANY_FRAME},
{ "ie_channel_switch_wrapper", IE(196), ANY_FRAME},
{ "ie_channel_usage", IE(97), ANY_FRAME},
{ "ie_collocated_interference_report", IE(96), ANY_FRAME},
{ "ie_congestion_notification", IE(116), ANY_FRAME},
{ "ie_country", IE(7), ANY_FRAME},
{ "ie_destination_uri", IE(141), ANY_FRAME},
{ "ie_diagnostic_report", IE(81), ANY_FRAME},
{ "ie_diagnostic_request", IE(80), ANY_FRAME},
{ "ie_dms_request", IE(99), ANY_FRAME},
{ "ie_dms_response", IE(100), ANY_FRAME},
{ "ie_dse_registered_location", IE(58), ANY_FRAME},
{ "ie_dsss_parameter_set", IE(3), ANY_FRAME},
{ "ie_edca_parameter_set", IE(12), ANY_FRAME},
{ "ie_emergency_alart_identifier", IE(112), ANY_FRAME},
{ "ie_erp_information", IE(42), ANY_FRAME},
{ "ie_event_report", IE(79), ANY_FRAME},
{ "ie_event_request", IE(78), ANY_FRAME},
{ "ie_expedited_bandwidth_request", IE(109), ANY_FRAME},
{ "ie_extended_bss_load", IE(193), ANY_FRAME},
{ "ie_extended_capabilities", IE(127), ANY_FRAME},
{ "ie_extended_channel_switch_announcement", IE(60), ANY_FRAME},
{ "ie_extended_supported_rates", IE(50), ANY_FRAME},
{ "ie_fast_bss_transition", IE(55), ANY_FRAME},
{ "ie_fh_parameter_set", IE(2), ANY_FRAME},
{ "ie_fms_descriptor", IE(86), ANY_FRAME},
{ "ie_fms_request", IE(87), ANY_FRAME},
{ "ie_fms_response", IE(88), ANY_FRAME},
{ "ie_gann", IE(125), ANY_FRAME},
{ "ie_he_capabilities", IE(255), ANY_FRAME},
{ "ie_hopping_pattern_parameters", IE(8), ANY_FRAME},
{ "ie_hopping_pattern_table", IE(9), ANY_FRAME},
{ "ie_ht_capabilities", IE(45), ANY_FRAME},
{ "ie_ht_operation", IE(61), ANY_FRAME},
{ "ie_ibss_dfs", IE(41), ANY_FRAME},
{ "ie_ibss_parameter_set", IE(6), ANY_FRAME},
{ "ie_interworking", IE(107), ANY_FRAME},
{ "ie_link_identifier", IE(101), ANY_FRAME},
{ "ie_location_parameters", IE(82), ANY_FRAME},
{ "ie_management_mic", IE(76), ANY_FRAME},
{ "ie_mccaop", IE(124), ANY_FRAME},
{ "ie_mccaop_advertisement", IE(123), ANY_FRAME},
{ "ie_mccaop_advertisement_overview", IE(174), ANY_FRAME},
{ "ie_mccaop_setup_reply", IE(122), ANY_FRAME},
{ "ie_mccaop_setup_request", IE(121), ANY_FRAME},
{ "ie_measurement_pilot_transmission", IE(66), ANY_FRAME},
{ "ie_measurement_report", IE(39), ANY_FRAME},
{ "ie_measurement_request", IE(38), ANY_FRAME},
{ "ie_mesh_awake_window", IE(119), ANY_FRAME},
{ "ie_mesh_channel_switch_parameters", IE(118), ANY_FRAME},
{ "ie_mesh_configuration", IE(113), ANY_FRAME},
{ "ie_mesh_id", IE(114), ANY_FRAME},
{ "ie_mesh_link_metric_report", IE(115), ANY_FRAME},
{ "ie_mesh_peering_management", IE(117), ANY_FRAME},
{ "ie_mic", IE(140), ANY_FRAME},
{ "ie_mobility_domain", IE(54), ANY_FRAME},
{ "ie_multiple_bssid", IE(71), ANY_FRAME},
{ "ie_multiple_bssid_index", IE(85), ANY_FRAME},
{ "ie_neighbor_report", IE(52), ANY_FRAME},
{ "ie_nontransmitted_bssid_capability", IE(83), ANY_FRAME},
{ "ie_operating_mode_notification", IE(199), ANY_FRAME},
{ "ie_overlapping_bss_scan_parameters", IE(74), ANY_FRAME},
{ "ie_perr", IE(132), ANY_FRAME},
{ "ie_power_capability", IE(33), ANY_FRAME},
{ "ie_power_constraint", IE(32), ANY_FRAME},
{ "ie_prep", IE(131), ANY_FRAME},
{ "ie_preq", IE(130), ANY_FRAME},
{ "ie_proxy_update", IE(137), ANY_FRAME},
{ "ie_proxy_update_confirmation", IE(138), ANY_FRAME},
{ "ie_pti_control", IE(105), ANY_FRAME},
{ "ie_qos_capability", IE(46), ANY_FRAME},
{ "ie_qos_map_set", IE(110), ANY_FRAME},
{ "ie_qos_traffic_capability", IE(89), ANY_FRAME},
{ "ie_quiet", IE(40), ANY_FRAME},
{ "ie_quiet_channel", IE(198), ANY_FRAME},
{ "ie_rann", IE(126), ANY_FRAME},
{ "ie_rcpi", IE(53), ANY_FRAME},
{ "ie_request", IE(10), ANY_FRAME},
{ "ie_ric_data", IE(57), ANY_FRAME},
{ "ie_ric_descriptor", IE(75), ANY_FRAME},
{ "ie_rm_enabled_capacities", IE(70), ANY_FRAME},
{ "ie_roaming_consortium", IE(111), ANY_FRAME},
{ "ie_rsn", IE(48), ANY_FRAME},
{ "ie_rsni", IE(65), ANY_FRAME},
{ "ie_schedule", IE(15), ANY_FRAME},
{ "ie_secondary_channel_offset", IE(62), ANY_FRAME},
{ "ie_ssid", IE(0), ANY_FRAME},
{ "ie_ssid_list", IE(84), ANY_FRAME},
{ "ie_supported_channels", IE(36), ANY_FRAME},
{ "ie_supported_operating_classes", IE(59), ANY_FRAME},
{ "ie_supported_rates", IE(1), ANY_FRAME},
{ "ie_table", BITFIELD(0, 0, 0), IE_TABLE, ANY_FRAME},
{ "ie_tclas", IE(14), ANY_FRAME},
{ "ie_tclas_processing", IE(44), ANY_FRAME},
{ "ie_tfs_request", IE(91), ANY_FRAME},
{ "ie_tfs_response", IE(92), ANY_FRAME},
{ "ie_tim", IE(5), ANY_FRAME},
{ "ie_tim_broadcast_request", IE(94), ANY_FRAME},
{ "ie_tim_broadcast_response", IE(95), ANY_FRAME},
{ "ie_time_advertisement", IE(69), ANY_FRAME},
{ "ie_time_zone", IE(98), ANY_FRAME},
{ "ie_timeout_interval", IE(56), ANY_FRAME},
{ "ie_tpc_report", IE(35), ANY_FRAME},
{ "ie_tpc_request", IE(34), ANY_FRAME},
{ "ie_tpu_buffer_status", IE(106), ANY_FRAME},
{ "ie_ts_delay", IE(43), ANY_FRAME},
{ "ie_tspec", IE(13), ANY_FRAME},
{ "ie_uapsd_coexistence", IE(142), ANY_FRAME},
{ "ie_vendor_specific", IE(221), ANY_FRAME},
{ "ie_vht_capabilities", IE(191), ANY_FRAME},
{ "ie_vht_operation", IE(192), ANY_FRAME},
{ "ie_vht_transmit_power_envelope", IE(195), ANY_FRAME},
{ "ie_wakeup_schedule", IE(102), ANY_FRAME},
{ "ie_wide_bandwidth_channel_switch", IE(194), ANY_FRAME},
{ "ie_wnm_sleep_mode", IE(93), ANY_FRAME},
{ "is_group", BITFIELD(1, 4, 1), 0, ANY_FRAME},
{ "legacy_length", BITFIELD(2, 0, 12), 0, ANY_FRAME},
{ "listen_interval", BYTEFIELD(38, 2), 0, FRAME_SUBTYPE_ASSOC_REQUEST},
{ "listen_interval", BYTEFIELD(38, 2), 0, FRAME_SUBTYPE_REASSOC_REQUEST},
{ "mcs", BITFIELD(4, 0, 7), 0, ANY_FRAME},
{ "moredata", BITFIELD(13, 5, 1), 0, ANY_FRAME},
{ "moreflag", BITFIELD(13, 2, 1), 0, ANY_FRAME},
{ "not_counding", BITFIELD(7, 1, 1), 0, ANY_FRAME},
{ "number", BYTEFIELD(34, 2), 0, ANY_FRAME},
{ "order", BITFIELD(13, 7, 1), 0, ANY_FRAME},
{ "protectedframe", BITFIELD(13, 6, 1), 0, ANY_FRAME},
{ "protocol", BITFIELD(12, 0, 2), 0, ANY_FRAME},
{ "pwrmgmt", BITFIELD(13, 4, 1), 0, ANY_FRAME},
{ "radio", BYTEFIELD(0, 12), IS_STRING, ANY_FRAME},
{ "rate", BITFIELD(1, 0, 4), 0, ANY_FRAME},
{ "reason", BYTEFIELD(36, 2), 0, FRAME_SUBTYPE_DEAUTHENTICATION},
{ "retry", BITFIELD(13, 3, 1), 0, ANY_FRAME},
{ "rssi", BYTEFIELD(0, 1), IS_SIGNED, ANY_FRAME},
{ "rxend_state", BITFIELD(8, 0, 8), 0, ANY_FRAME},
{ "sgi", BITFIELD(7, 7, 1), 0, ANY_FRAME},
{ "sig_mode", BITFIELD(1, 6, 2), 0, ANY_FRAME},
{ "smoothing", BITFIELD(7, 0, 1), 0, ANY_FRAME},
{ "srcmac", BYTEFIELD(16, 6), IS_STRING, ANY_FRAME},
{ "srcmac_hex", BYTEFIELD(16, 6), IS_HEXSTRING, ANY_FRAME},
{ "status", BYTEFIELD(38, 2), 0, FRAME_SUBTYPE_ASSOC_RESPONSE},
{ "status", BYTEFIELD(38, 2), 0, FRAME_SUBTYPE_REASSOC_RESPONSE},
{ "status", BYTEFIELD(40, 2), 0, FRAME_SUBTYPE_AUTHENTICATION},
{ "stbc", BITFIELD(7, 4, 2), 0, ANY_FRAME},
{ "subtype", BITFIELD(12, 4, 4), 0, ANY_FRAME},
{ "timestamp", BYTEFIELD(36, 8), IS_STRING, FRAME_SUBTYPE_PROBE_RESPONSE},
{ "timestamp", BYTEFIELD(36, 8), IS_STRING, FRAME_SUBTYPE_BEACON},
{ "tods", BITFIELD(13, 0, 1), 0, ANY_FRAME},
{ "type", BITFIELD(12, 2, 2), 0, ANY_FRAME}
};
static int8 variable_start[16] = {
4, // assoc req
6, // assoc response
10, // reassoc req
6, // reassoc resp
0, // probe req
12, // probe resp
-1,
-1,
12, // beacon
-1, // ATIM
2, // Disassociation
6, // authentication
2, // Deauthentication
2, // action
-1,
-1
};
typedef struct {
uint16 len;
uint8 buf[];
} packet_t;
static const LUA_REG_TYPE packet_function_map[];
static void wifi_rx_cb(uint8 *buf, uint16 len) {
if (len != sizeof(struct sniffer_buf2)) {
return;
}
struct sniffer_buf2 *snb = (struct sniffer_buf2 *) buf;
management_request_t *mgt = (management_request_t *) snb->buf;
if (mon_offset > len) {
return;
}
if ((buf[mon_offset] & mon_mask) != mon_value) {
return;
}
packet_t *packet = (packet_t *) c_malloc(len + sizeof(packet_t));
if (packet) {
packet->len = len;
memcpy(packet->buf, buf, len);
if (!task_post_medium(tasknumber, (ETSParam) packet)) {
c_free(packet);
}
}
}
static void monitor_task(os_param_t param, uint8_t prio)
{
packet_t *input = (packet_t *) param;
(void) prio;
lua_State *L = lua_getstate();
if (recv_cb != LUA_NOREF) {
lua_rawgeti(L, LUA_REGISTRYINDEX, recv_cb);
packet_t *packet = (packet_t *) lua_newuserdata(L, input->len + sizeof(packet_t));
packet->len = input->len;
memcpy(packet->buf, input->buf, input->len);
luaL_getmetatable(L, "wifi.packet");
lua_setmetatable(L, -2);
c_free(input);
lua_call(L, 1, 0);
} else {
c_free(input);
}
}
static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) {
/* relative string position: negative means back from end */
if (pos < 0) pos += (ptrdiff_t)len + 1;
return (pos >= 0) ? pos : 0;
}
static int packet_sub(lua_State *L, int buf_offset, int buf_length) {
packet_t *packet = luaL_checkudata(L, 1, "wifi.packet");
ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), buf_length);
ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), buf_length);
if (start < 1) start = 1;
if (end > buf_length) end = buf_length;
if (start <= end) {
lua_pushlstring(L, packet->buf+start-1 + buf_offset, end-start+1);
} else {
lua_pushliteral(L, "");
}
return 1;
}
static int packet_frame_sub(lua_State *L) {
return packet_sub(L, sizeof(struct RxControl), SNIFFER_BUF2_BUF_SIZE);
}
static int packet_radio_sub(lua_State *L) {
return packet_sub(L, 0, sizeof(struct RxControl));
}
static void push_hex_string(lua_State *L, const uint8 *buf, int len, char *sep) {
luaL_Buffer b;
luaL_buffinit(L, &b);
int i;
for (i = 0; i < len; i++) {
if (i && sep && *sep) {
luaL_addstring(&b, sep);
}
char hex[3];
uint8 c = buf[i];
hex[0] = "0123456789abcdef"[c >> 4];
hex[1] = "0123456789abcdef"[c & 0xf];
hex[2] = 0;
luaL_addstring(&b, hex);
}
luaL_pushresult(&b);
}
static void push_hex_string_colon(lua_State *L, const uint8 *buf, int len) {
push_hex_string(L, buf, len, ":");
}
static int comparator(const void *typekey, const void *obj) {
field_t *f = (field_t *) obj;
const char *name = f->name;
const typekey_t *tk = (const typekey_t *) typekey;
const char *key = tk->key;
if (!((uint32)key & 3) && !((uint32)name & 3)) {
// Since all strings are 3 characters or more, can do accelerated first comparison
uint32 key32 = htonl(*(uint32 *) key);
uint32 name32 = htonl(*(uint32 *) name);
if (key32 < name32) {
return -1;
}
if (key32 > name32) {
return 1;
}
}
int rc = strcmp((const char *) key, name);
if (rc) {
return rc;
}
if (f->frametype == ANY_FRAME) {
return 0;
}
return tk->frametype - f->frametype;
}
static bool push_field_value_string(lua_State *L, const uint8 *pkt,
const uint8 *packet_end, const char *field) {
const struct RxControl *rxc = (struct RxControl *) pkt;
const management_request_t *mgt = (management_request_t *) (rxc + 1);
typekey_t tk;
tk.key = field;
tk.frametype = mgt->framectrl.Subtype;
field_t *f = bsearch(&tk, fields, sizeof(fields) / sizeof(fields[0]), sizeof(fields[0]), comparator);
if (f) {
if (f->opts == SINGLE_IE) {
int varstart = variable_start[mgt->framectrl.Subtype];
if (varstart >= 0) {
const uint8 *var = (uint8 *) (mgt + 1) + varstart;
while (var + 2 <= packet_end && var + 2 + var[1] <= packet_end) {
if (*var == f->length) {
lua_pushlstring(L, var + 2, var[1]);
return true;
}
var += 2 + var[1];
}
}
lua_pushnil(L);
return true;
}
if (f->opts == IE_TABLE) {
lua_newtable(L);
int varstart = variable_start[mgt->framectrl.Subtype];
if (varstart >= 0) {
const uint8 *var = (uint8 *) (mgt + 1) + varstart;
while (var + 2 <= packet_end && var + 2 + var[1] <= packet_end) {
lua_pushlstring(L, var + 2, var[1]);
lua_rawseti(L, -2, *var);
var += 2 + var[1];
}
}
return true;
}
if (f->opts == IS_STRING) {
int use = f->length >> 3;
const uint8 *start = pkt + (f->start >> 3);
if (start + use > packet_end) {
use = packet_end - start;
}
lua_pushlstring(L, start, use);
return true;
}
if (f->opts == IS_HEXSTRING) {
int use = f->length >> 3;
const uint8 *start = pkt + (f->start >> 3);
if (start + use > packet_end) {
use = packet_end - start;
}
push_hex_string_colon(L, start, use);
return true;
}
if (f->opts == IS_HEADER) {
int varstart = variable_start[mgt->framectrl.Subtype];
if (varstart >= 0) {
lua_pushlstring(L, (const uint8 *) (mgt + 1), varstart);
} else {
lua_pushnil(L);
}
return true;
}
if (f->opts == 0 || f->opts == IS_SIGNED) {
// bits start from the bottom of the byte.
int value = 0;
int bits = 0;
int bitoff = f->start & 7;
int byteoff = f->start >> 3;
while (bits < f->length) {
uint8 b = pkt[byteoff];
value |= (b >> bitoff) << bits;
bits += (8 - bitoff);
bitoff = 0;
byteoff++;
}
// get rid of excess bits
value &= (1 << f->length) - 1;
if (f->opts & IS_SIGNED) {
if (value & (1 << (f->length - 1))) {
value |= - (1 << f->length);
}
}
lua_pushinteger(L, value);
return true;
}
}
return false;
}
static bool push_field_value_int(lua_State *L, management_request_t *mgt,
const uint8 *packet_end, int field) {
int varstart = variable_start[mgt->framectrl.Subtype];
if (varstart >= 0) {
uint8 *var = (uint8 *) (mgt + 1) + varstart;
while (var + 2 <= packet_end && var + 2 + var[1] <= packet_end) {
if (*var == field) {
lua_pushlstring(L, var + 2, var[1]);
return true;
}
var += var[1] + 2;
}
}
return false;
}
static int packet_map_lookup(lua_State *L) {
packet_t *packet = luaL_checkudata(L, 1, "wifi.packet");
struct RxControl *rxc = (struct RxControl *) packet->buf;
management_request_t *mgt = (management_request_t *) (rxc + 1);
const uint8 *packet_end = packet->buf + packet->len;
if ((void *) (mgt + 1) > (void *) packet_end) {
return 0;
}
if (mgt->framectrl.Type != FRAME_TYPE_MANAGEMENT) {
return 0;
}
if (lua_type(L, 2) == LUA_TNUMBER) {
int field = luaL_checkinteger(L, 2);
if (push_field_value_int(L, mgt, packet_end, field)) {
return 1;
}
} else {
const char *field = luaL_checkstring(L, 2);
if (push_field_value_string(L, packet->buf, packet_end, field)) {
return 1;
}
// Now search the packet function map
const TValue *res = luaR_findentry((void *) packet_function_map, field, 0, NULL);
if (res) {
luaA_pushobject(L, res);
return 1;
}
}
return 0;
}
static int packet_byte(lua_State *L, int buf_offset, int buf_length) {
packet_t *packet = luaL_checkudata(L, 1, "wifi.packet");
int offset = luaL_checkinteger(L, 2);
if (offset < 1 || offset > buf_length) {
return 0;
}
lua_pushinteger(L, packet->buf[offset - 1 + buf_offset]);
return 1;
}
static int packet_frame_byte(lua_State *L) {
return packet_byte(L, sizeof(struct RxControl), SNIFFER_BUF2_BUF_SIZE);
}
static int packet_radio_byte(lua_State *L) {
return packet_byte(L, 0, sizeof(struct RxControl));
}
static int packet_subhex(lua_State *L, int buf_offset, int buf_length) {
packet_t *packet = luaL_checkudata(L, 1, "wifi.packet");
ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), buf_length);
ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), buf_length);
const char *sep = luaL_optstring(L, 4, "");
if (start < 1) start = 1;
if (end > buf_length) end = buf_length;
if (start <= end) {
luaL_Buffer b;
luaL_buffinit(L, &b);
int i;
for (i = start - 1; i < end; i++) {
char hex[3];
if (i >= start) {
luaL_addstring(&b, sep);
}
uint8 c = packet->buf[i + buf_offset];
hex[0] = "0123456789abcdef"[c >> 4];
hex[1] = "0123456789abcdef"[c & 0xf];
hex[2] = 0;
luaL_addstring(&b, hex);
}
luaL_pushresult(&b);
} else {
lua_pushliteral(L, "");
}
return 1;
}
static int packet_frame_subhex(lua_State *L) {
return packet_subhex(L, sizeof(struct RxControl), SNIFFER_BUF2_BUF_SIZE);
}
static int packet_radio_subhex(lua_State *L) {
return packet_subhex(L, 0, sizeof(struct RxControl));
}
static void start_actually_monitoring() {
wifi_set_channel(1);
wifi_promiscuous_enable(1);
}
static int wifi_event_monitor_handle_event_cb_hook(System_Event_t *evt)
{
if (evt->event == EVENT_STAMODE_DISCONNECTED) {
if (on_disconnected) {
on_disconnected();
on_disconnected = NULL;
return 1; // We did handle the event
}
}
return 0; // We did not handle the event
}
// This is a bit ugly as we have to use a bit of the event monitor infrastructure
#ifdef WIFI_SDK_EVENT_MONITOR_ENABLE
extern void wifi_event_monitor_register_hook(int (*fn)(System_Event_t*));
static void eventmon_setup() {
wifi_event_monitor_register_hook(wifi_event_monitor_handle_event_cb_hook);
}
#else
static void wifi_event_monitor_handle_event_cb(System_Event_t *evt)
{
wifi_event_monitor_handle_event_cb_hook(evt);
}
static void eventmon_setup() {
wifi_set_event_handler_cb(wifi_event_monitor_handle_event_cb);
}
#endif
static void eventmon_call_on_disconnected(void (*fn)(void)) {
on_disconnected = fn;
}
static int wifi_monitor_start(lua_State *L) {
int argno = 1;
if (lua_type(L, argno) == LUA_TNUMBER) {
int offset = luaL_checkinteger(L, argno);
argno++;
if (lua_type(L, argno) == LUA_TNUMBER) {
int value = luaL_checkinteger(L, argno);
int mask = 0xff;
argno++;
if (lua_type(L, argno) == LUA_TNUMBER) {
mask = luaL_checkinteger(L, argno);
argno++;
}
mon_offset = offset - 1;
mon_value = value;
mon_mask = mask;
} else {
return luaL_error(L, "Must supply offset and value");
}
} else {
// Management frames by default
mon_offset = 12;
mon_value = 0x00;
mon_mask = 0x0C;
}
if (lua_type(L, argno) == LUA_TFUNCTION || lua_type(L, argno) == LUA_TLIGHTFUNCTION)
{
lua_pushvalue(L, argno); // copy argument (func) to the top of stack
recv_cb = luaL_ref(L, LUA_REGISTRYINDEX);
uint8 connect_status = wifi_station_get_connect_status();
wifi_station_set_auto_connect(0);
wifi_set_opmode_current(1);
wifi_promiscuous_enable(0);
wifi_station_disconnect();
wifi_set_promiscuous_rx_cb(wifi_rx_cb);
// Now we have to wait until we get the EVENT_STAMODE_DISCONNECTED event
// before we can go further.
if (connect_status == STATION_IDLE) {
start_actually_monitoring();
} else {
eventmon_call_on_disconnected(start_actually_monitoring);
}
return 0;
}
return luaL_error(L, "Missing callback");
}
static int wifi_monitor_channel(lua_State *L) {
lua_pushinteger(L, wifi_get_channel());
if (lua_type(L, 1) == LUA_TNUMBER) {
int channel = luaL_checkinteger(L, 1);
if (channel < 1 || channel > 15) {
return luaL_error(L, "Channel number (%d) is out of range", channel);
}
wifi_set_channel(channel);
}
return 1;
}
static int wifi_monitor_stop(lua_State *L) {
wifi_promiscuous_enable(0);
wifi_set_opmode_current(1);
luaL_unref(L, LUA_REGISTRYINDEX, recv_cb);
recv_cb = LUA_NOREF;
return 0;
}
static const LUA_REG_TYPE packet_function_map[] = {
{ LSTRKEY( "radio_byte" ), LFUNCVAL( packet_radio_byte ) },
{ LSTRKEY( "frame_byte" ), LFUNCVAL( packet_frame_byte ) },
{ LSTRKEY( "radio_sub" ), LFUNCVAL( packet_radio_sub ) },
{ LSTRKEY( "frame_sub" ), LFUNCVAL( packet_frame_sub ) },
{ LSTRKEY( "radio_subhex" ), LFUNCVAL( packet_radio_subhex ) },
{ LSTRKEY( "frame_subhex" ), LFUNCVAL( packet_frame_subhex ) },
{ LNILKEY, LNILVAL }
};
static const LUA_REG_TYPE packet_map[] = {
{ LSTRKEY( "__index" ), LFUNCVAL( packet_map_lookup ) },
{ LNILKEY, LNILVAL }
};
// Module function map
const LUA_REG_TYPE wifi_monitor_map[] = {
{ LSTRKEY( "start" ), LFUNCVAL( wifi_monitor_start ) },
{ LSTRKEY( "stop" ), LFUNCVAL( wifi_monitor_stop ) },
{ LSTRKEY( "channel" ), LFUNCVAL( wifi_monitor_channel ) },
{ LNILKEY, LNILVAL }
};
int wifi_monitor_init(lua_State *L)
{
luaL_rometatable(L, "wifi.packet", (void *)packet_map);
tasknumber = task_get_id(monitor_task);
eventmon_setup();
#ifdef CHECK_TABLE_IN_ORDER
// verify that the table is in order
typekey_t tk;
tk.key = "";
tk.frametype = 0;
int i;
for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++) {
if (comparator(&tk, &fields[i]) >= 0) {
dbg_printf("Wrong order: %s,%d should be after %s,%d\n", tk.key, tk.frametype, fields[i].name, fields[i].frametype);
}
tk.key = fields[i].name;
tk.frametype = fields[i].frametype;
}
#endif
return 0;
}
......@@ -3,26 +3,22 @@
#include "lmem.h"
#include "platform.h"
#include "c_stdlib.h"
#include "c_math.h"
#include "c_string.h"
#include "user_interface.h"
#include "driver/uart.h"
#include "osapi.h"
#include "ws2812.h"
#define CANARY_VALUE 0x32383132
#define MODE_SINGLE 0
#define MODE_DUAL 1
#define FADE_IN 1
#define FADE_OUT 0
#define SHIFT_LOGICAL 0
#define SHIFT_CIRCULAR 1
typedef struct {
int size;
uint8_t colorsPerLed;
uint8_t values[0];
} ws2812_buffer;
// Init UART1 to be able to stream WS2812 data to GPIO2 pin
// If DUAL mode is selected, init UART0 to stream to TXD0 as well
......@@ -65,7 +61,7 @@ static int ws2812_init(lua_State* L) {
// ws2812.init() should be called first
//
// NODE_DEBUG should not be activated because it also uses UART1
static void ICACHE_RAM_ATTR ws2812_write_data(const uint8_t *pixels, uint32_t length, const uint8_t *pixels2, uint32_t length2) {
void ICACHE_RAM_ATTR ws2812_write_data(const uint8_t *pixels, uint32_t length, const uint8_t *pixels2, uint32_t length2) {
// Data are sent LSB first, with a start bit at 0, an end bit at 1 and all inverted
// 0b00110111 => 110111 => [0]111011[1] => 10001000 => 00
......@@ -194,6 +190,7 @@ static ws2812_buffer *allocate_buffer(lua_State *L, int leds, int colorsPerLed)
return buffer;
}
// Handle a buffer where we can store led values
static int ws2812_new_buffer(lua_State *L) {
const int leds = luaL_checkint(L, 1);
......@@ -209,17 +206,11 @@ static int ws2812_new_buffer(lua_State *L) {
return 1;
}
static int ws2812_buffer_fill(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
int ws2812_buffer_fill(ws2812_buffer * buffer, int * colors) {
// Grab colors
int i, j;
int * colors = luaM_malloc(L, buffer->colorsPerLed * sizeof(int));
for (i = 0; i < buffer->colorsPerLed; i++)
{
colors[i] = luaL_checkinteger(L, 2+i);
}
// Fill buffer
uint8_t * p = &buffer->values[0];
......@@ -231,6 +222,23 @@ static int ws2812_buffer_fill(lua_State* L) {
}
}
return 0;
}
static int ws2812_buffer_fill_lua(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
// Grab colors
int i;
int * colors = luaM_malloc(L, buffer->colorsPerLed * sizeof(int));
for (i = 0; i < buffer->colorsPerLed; i++)
{
colors[i] = luaL_checkinteger(L, 2+i);
}
ws2812_buffer_fill(buffer, colors);
// Free memory
luaM_free(L, colors);
......@@ -266,13 +274,10 @@ static int ws2812_buffer_fade(lua_State* L) {
}
static int ws2812_buffer_shift(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
const int shiftValue = luaL_checkinteger(L, 2);
const unsigned shift_type = luaL_optinteger( L, 3, SHIFT_LOGICAL );
int ws2812_buffer_shift(ws2812_buffer * buffer, int shiftValue, unsigned shift_type, int pos_start, int pos_end) {
ptrdiff_t start = posrelat(luaL_optinteger(L, 4, 1), buffer->size);
ptrdiff_t end = posrelat(luaL_optinteger(L, 5, -1), buffer->size);
ptrdiff_t start = posrelat(pos_start, buffer->size);
ptrdiff_t end = posrelat(pos_end, buffer->size);
if (start < 1) start = 1;
if (end > (ptrdiff_t)buffer->size) end = (ptrdiff_t)buffer->size;
......@@ -280,7 +285,7 @@ static int ws2812_buffer_shift(lua_State* L) {
int size = end - start;
size_t offset = start * buffer->colorsPerLed;
luaL_argcheck(L, shiftValue > 0-size && shiftValue < size, 2, "shifting more elements than buffer size");
//luaL_argcheck(L, shiftValue > 0-size && shiftValue < size, 2, "shifting more elements than buffer size");
int shift = shiftValue >= 0 ? shiftValue : -shiftValue;
......@@ -290,7 +295,7 @@ static int ws2812_buffer_shift(lua_State* L) {
return 0;
}
uint8_t * tmp_pixels = luaM_malloc(L, buffer->colorsPerLed * sizeof(uint8_t) * shift);
uint8_t * tmp_pixels = c_malloc(buffer->colorsPerLed * sizeof(uint8_t) * shift);
int i,j;
size_t shift_len, remaining_len;
// calculate length of shift section and remaining section
......@@ -330,11 +335,27 @@ static int ws2812_buffer_shift(lua_State* L) {
}
}
// Free memory
luaM_free(L, tmp_pixels);
c_free(tmp_pixels);
return 0;
}
static int ws2812_buffer_shift_lua(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
const int shiftValue = luaL_checkinteger(L, 2);
const unsigned shift_type = luaL_optinteger( L, 3, SHIFT_LOGICAL );
const int pos_start = luaL_optinteger(L, 4, 1);
const int pos_end = luaL_optinteger(L, 5, -1);
ws2812_buffer_shift(buffer, shiftValue, shift_type, pos_start, pos_end);
return 0;
}
static int ws2812_buffer_dump(lua_State* L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
......@@ -392,24 +413,26 @@ static int ws2812_buffer_mix(lua_State* L) {
ws2812_buffer *src_buffer = (ws2812_buffer*) luaL_checkudata(L, pos + 1, "ws2812.buffer");
luaL_argcheck(L, src_buffer->size == buffer->size && src_buffer->colorsPerLed == buffer->colorsPerLed, pos + 1, "Buffer not same shape");
source[src].factor = factor;
source[src].values = src_buffer->values;
}
size_t i;
for (i = 0; i < cells; i++) {
int val = 0;
int32_t val = 0;
for (src = 0; src < n_sources; src++) {
val += ((int)(source[src].values[i] * source[src].factor) >> 8);
val += (int32_t)(source[src].values[i] * source[src].factor);
}
val >>= 8;
if (val < 0) {
val = 0;
} else if (val > 255) {
val = 255;
}
buffer->values[i] = val;
buffer->values[i] = (uint8_t)val;
}
return 0;
......@@ -526,7 +549,7 @@ static int ws2812_buffer_concat(lua_State* L) {
int colorsPerLed = lhs->colorsPerLed;
int leds = lhs->size + rhs->size;
ws2812_buffer * buffer = allocate_buffer(L, leds, colorsPerLed);
c_memcpy(buffer->values, lhs->values, lhs->colorsPerLed * lhs->size);
......@@ -567,17 +590,18 @@ static int ws2812_buffer_tostring(lua_State* L) {
return 1;
}
static const LUA_REG_TYPE ws2812_buffer_map[] =
{
{ LSTRKEY( "dump" ), LFUNCVAL( ws2812_buffer_dump )},
{ LSTRKEY( "fade" ), LFUNCVAL( ws2812_buffer_fade )},
{ LSTRKEY( "fill" ), LFUNCVAL( ws2812_buffer_fill )},
{ LSTRKEY( "fill" ), LFUNCVAL( ws2812_buffer_fill_lua )},
{ LSTRKEY( "get" ), LFUNCVAL( ws2812_buffer_get )},
{ LSTRKEY( "replace" ), LFUNCVAL( ws2812_buffer_replace )},
{ LSTRKEY( "mix" ), LFUNCVAL( ws2812_buffer_mix )},
{ LSTRKEY( "power" ), LFUNCVAL( ws2812_buffer_power )},
{ LSTRKEY( "set" ), LFUNCVAL( ws2812_buffer_set )},
{ LSTRKEY( "shift" ), LFUNCVAL( ws2812_buffer_shift )},
{ LSTRKEY( "shift" ), LFUNCVAL( ws2812_buffer_shift_lua )},
{ LSTRKEY( "size" ), LFUNCVAL( ws2812_buffer_size )},
{ LSTRKEY( "sub" ), LFUNCVAL( ws2812_buffer_sub )},
{ LSTRKEY( "__concat" ),LFUNCVAL( ws2812_buffer_concat )},
......@@ -586,6 +610,7 @@ static const LUA_REG_TYPE ws2812_buffer_map[] =
{ LNILKEY, LNILVAL}
};
static const LUA_REG_TYPE ws2812_map[] =
{
{ LSTRKEY( "init" ), LFUNCVAL( ws2812_init )},
......
#ifndef APP_MODULES_WS2812_H_
#define APP_MODULES_WS2812_H_
#include "module.h"
#include "lauxlib.h"
#include "lmem.h"
#include "platform.h"
#include "c_stdlib.h"
#include "c_math.h"
#include "c_string.h"
#include "user_interface.h"
#include "driver/uart.h"
#include "osapi.h"
#define FADE_IN 1
#define FADE_OUT 0
#define SHIFT_LOGICAL 0
#define SHIFT_CIRCULAR 1
typedef struct {
int size;
uint8_t colorsPerLed;
uint8_t values[0];
} ws2812_buffer;
void ICACHE_RAM_ATTR ws2812_write_data(const uint8_t *pixels, uint32_t length, const uint8_t *pixels2, uint32_t length2);
int ws2812_buffer_shift(ws2812_buffer * buffer, int shiftValue, unsigned shift_type, int pos_start, int pos_end);
int ws2812_buffer_fill(ws2812_buffer * buffer, int * colors);
#endif /* APP_MODULES_WS2812_H_ */
#include "module.h"
#include "lauxlib.h"
#include "lmem.h"
#include "platform.h"
#include "c_stdlib.h"
#include "c_math.h"
#include "c_string.h"
#include "user_interface.h"
#include "driver/uart.h"
#include "osapi.h"
#include "swTimer/swTimer.h"
#include "ws2812.h"
#include "color_utils.h"
#define CANARY_VALUE 0x32372132
#define DEFAULT_MODE 0
#define DEFAULT_COLOR 0xFF0000
#define UINT32_MAX 4294967295U
#define SPEED_MIN 0
#define SPEED_MAX 255
#define SPEED_DEFAULT 150
#define DELAY_DEFAULT 100
#define BRIGHTNESS_MIN 0
#define BRIGHTNESS_MAX 255
#define BRIGHTNESS_DEFAULT 100
#define EFFECT_PARAM_INVALID -10000
#define LIBRARY_NOT_INITIALIZED_ERROR_MSG "please call init() first"
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
#define abs(a) ((a) > 0 ? (a) : (0-a))
#define min3(a,b, c) min((a), min((b), (c)))
#define max3(a,b, c) max((a), max((b), (c)))
typedef struct {
ws2812_buffer *buffer;
int buffer_ref;
uint32_t mode_delay;
uint32_t counter_mode_call;
uint32_t counter_mode_step;
uint8_t mode_color_index;
uint8_t speed;
uint8_t brightness;
os_timer_t os_t;
uint8_t running;
uint8_t effect_type;
uint8_t color[4];
int effect_int_param1;
} ws2812_effects;
enum ws2812_effects_type {
WS2812_EFFECT_STATIC,
WS2812_EFFECT_BLINK,
WS2812_EFFECT_GRADIENT,
WS2812_EFFECT_GRADIENT_RGB,
WS2812_EFFECT_RANDOM_COLOR,
WS2812_EFFECT_RAINBOW,
WS2812_EFFECT_RAINBOW_CYCLE,
WS2812_EFFECT_FLICKER,
WS2812_EFFECT_FIRE_FLICKER,
WS2812_EFFECT_FIRE_FLICKER_SOFT,
WS2812_EFFECT_FIRE_FLICKER_INTENSE,
WS2812_EFFECT_HALLOWEEN,
WS2812_EFFECT_CIRCUS_COMBUSTUS,
WS2812_EFFECT_LARSON_SCANNER,
WS2812_EFFECT_CYCLE,
WS2812_EFFECT_COLOR_WIPE,
WS2812_EFFECT_RANDOM_DOT
};
static ws2812_effects *state;
//-----------------
// UTILITY METHODS
//-----------------
static int ws2812_write(ws2812_buffer* buffer) {
size_t length1, length2;
const char *buffer1, *buffer2;
buffer1 = 0;
length1 = 0;
buffer1 = buffer->values;
length1 = buffer->colorsPerLed*buffer->size;
buffer2 = 0;
length2 = 0;
// Send the buffers
ws2812_write_data(buffer1, length1, buffer2, length2);
return 0;
}
static int ws2812_set_pixel(int pixel, uint32_t color) {
ws2812_buffer * buffer = state->buffer;
uint8_t g = ((color & 0x00FF0000) >> 16);
uint8_t r = ((color & 0x0000FF00) >> 8);
uint8_t b = (color & 0x000000FF);
uint8_t w = buffer->colorsPerLed == 4 ? ((color & 0xFF000000) >> 24) : 0;
int offset = pixel * buffer->colorsPerLed;
buffer->values[offset] = g;
buffer->values[offset+1] = r;
buffer->values[offset+2] = b;
if (buffer->colorsPerLed == 4) {
buffer->values[offset+3] = w;
}
return 0;
}
/*
* Returns a new, random color wheel index with a minimum distance of 42 from pos.
*/
static uint8_t get_random_wheel_index(uint8_t pos)
{
uint8_t r = 0;
uint8_t x = 0;
uint8_t y = 0;
uint8_t d = 0;
while(d < 42) {
r = rand() % 360;
x = abs(pos - r);
y = 360 - x;
d = min(x, y);
}
return r;
}
//-----------------
// EFFECTS LIBRARY
//-----------------
/**
* initialized ws2812_effects with the buffer to use
*/
static int ws2812_effects_init(lua_State *L) {
ws2812_buffer * buffer = (ws2812_buffer*)luaL_checkudata(L, 1, "ws2812.buffer");
luaL_argcheck(L, buffer != NULL, 1, "no valid buffer provided");
// get rid of old state
if (state != NULL) {
luaL_unref(L, LUA_REGISTRYINDEX, state->buffer_ref);
os_free((void *) state);
}
// Allocate memory and set all to zero
size_t size = sizeof(ws2812_effects) + buffer->colorsPerLed*sizeof(uint8_t);
state = (ws2812_effects *) os_zalloc(size);
// initialize
state->speed = SPEED_DEFAULT;
state->mode_delay = DELAY_DEFAULT;
state->brightness = BRIGHTNESS_DEFAULT;
state->buffer = buffer;
state->buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX);
return 0;
}
/*
* set color for single color effects
*/
static int ws2812_effects_set_color(lua_State* L) {
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
uint8_t g = luaL_checkinteger(L, 1);
uint8_t r = luaL_checkinteger(L, 2);
uint8_t b = luaL_checkinteger(L, 3);
uint8_t w = luaL_optinteger(L, 4, 0 );
state->color[0] = g;
state->color[1] = r;
state->color[2] = b;
state->color[3] = w;
return 0;
}
static int ws2812_effects_get_speed(lua_State* L) {
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
lua_pushnumber(L, state->speed);
return 1;
}
static int ws2812_effects_set_speed(lua_State* L) {
uint8_t speed = luaL_checkinteger(L, 1);
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
luaL_argcheck(L, speed >= 0 && speed <= 255, 1, "should be a 0-255");
state->speed = speed;
state->mode_delay = 10;
return 0;
}
static int ws2812_effects_get_delay(lua_State* L) {
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
lua_pushnumber(L, state->mode_delay);
return 1;
}
static int ws2812_effects_set_delay(lua_State* L) {
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
const int delay = luaL_checkinteger(L, 1);
luaL_argcheck(L, delay >= 10, 1, "must be equal / larger than 10");
state->mode_delay = delay;
state->speed = 0;
return 1;
}
static int ws2812_effects_set_brightness(lua_State* L) {
uint8_t brightness = luaL_checkint(L, 1);
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
luaL_argcheck(L, brightness >= 0 && brightness < 256, 1, "should be a 0-255");
state->brightness = brightness;
return 0;
}
static int ws2812_effects_fill_buffer(uint32_t color) {
ws2812_buffer * buffer = state->buffer;
uint8_t g = ((color & 0x00FF0000) >> 16);
uint8_t r = ((color & 0x0000FF00) >> 8);
uint8_t b = (color & 0x000000FF);
uint8_t w = buffer->colorsPerLed == 4 ? ((color & 0xFF000000) >> 24) : 0;
// Fill buffer
int i;
uint8_t * p = &buffer->values[0];
for(i = 0; i < buffer->size; i++) {
*p++ = g * state->brightness / 255;
*p++ = r * state->brightness / 255;
*p++ = b * state->brightness / 255;
if (buffer->colorsPerLed == 4) {
*p++ = w * state->brightness / 255;
}
}
return 0;
}
//------------------
// basic methods
//------------------
/*
* Cycles all LEDs at once through a rainbow.
*/
static int ws2812_effects_fill_color() {
uint8_t g = state->color[0];
uint8_t r = state->color[1];
uint8_t b = state->color[2];
uint8_t w = state->color[3];
uint32_t color = (w << 24) | (g << 16) | (r << 8) | b;
ws2812_effects_fill_buffer(color);
return 0;
}
//-----------------
// EFFECFTS
//-----------------
/*
* blink with set color
*/
static int ws2812_effects_mode_blink() {
if(state->counter_mode_call % 2 == 1) {
// on
ws2812_effects_fill_color();
}
else {
// off
ws2812_buffer * buffer = state->buffer;
c_memset(&buffer->values[0], 0, buffer->size * buffer->colorsPerLed);
}
return 0;
}
static int ws2812_effects_gradient(const char *gradient_spec, size_t length1) {
ws2812_buffer * buffer = state->buffer;
int segments = (length1 / buffer->colorsPerLed) - 1;
int segmentSize = buffer->size / segments;
uint8_t g1, r1, b1, g2, r2, b2;
int i,j,k;
g2 = *gradient_spec++;
r2 = *gradient_spec++;
b2 = *gradient_spec++;
// skip non-rgb components
for (j = 3; j < buffer->colorsPerLed; j++)
{
*gradient_spec++;
}
// reference to buffer memory
uint8_t * p = &buffer->values[0];
uint16_t h1,h2;
uint8_t s,v,s1,v1,s2,v2;
for (k = 0; k < segments; k++) {
g1 = g2;
r1 = r2;
b1 = b2;
uint32_t hsv1 = grb2hsv(g1, r1, b1);
h1 = (hsv1 & 0xFFFF0000) >> 16;
s1 = (hsv1 & 0x0000FF00) >> 8;
v1 = (hsv1 & 0x000000FF);
g2 = *gradient_spec++;
r2 = *gradient_spec++;
b2 = *gradient_spec++;
for (j = 3; j < buffer->colorsPerLed; j++)
{
*gradient_spec++;
}
uint32_t hsv2 = grb2hsv(g2, r2, b2);
h2 = (hsv2 & 0xFFFF0000) >> 16;
s2 = (hsv1 & 0x0000FF00) >> 8;
v2 = (hsv1 & 0x000000FF);
// get distance and direction to use
int maxCCW = h1 > h2 ? h1 - h2 : 360 + h1 - h2;
int maxCW = h1 > h2 ? 360 + h2 - h1 : h2 - h1;
// Fill buffer
int numPixels = segmentSize;
// make sure we fill the strip correctly in case of rounding errors
if (k == segments - 1) {
numPixels = buffer->size - (segmentSize * (segments - 1));
}
int steps = numPixels - 1;
for(i = 0; i < numPixels; i++) {
// calculate HSV values
//h = h1 + ((h2-h1) * i / fillSize);
int h = maxCCW > maxCW ? h1 + ((maxCW * i / steps) % 360) : h1 - (maxCCW * i / steps);
if (h < 0) h = h + 360;
if (h > 359) h = h - 360;
s = s1 + ((s2-s1) * i / steps);
v = v1 + ((v2-v1) * i / steps);
// convert to RGB
uint32_t grb = hsv2grb(h, s, v);
*p++ = ((grb & 0x00FF0000) >> 16) * state->brightness / 255;
*p++ = ((grb & 0x0000FF00) >> 8) * state->brightness / 255;
*p++ = (grb & 0x000000FF) * state->brightness / 255;
for (j = 3; j < buffer->colorsPerLed; j++) {
*p++ = 0;
}
}
}
return 0;
}
static int ws2812_effects_gradient_rgb(const char *buffer1, size_t length1) {
ws2812_buffer * buffer = state->buffer;
int segments = (length1 / buffer->colorsPerLed) - 1;
int segmentSize = buffer->size / segments;
uint8_t g1, r1, b1, g2, r2, b2;
int i,j,k;
g2 = *buffer1++;
r2 = *buffer1++;
b2 = *buffer1++;
// skip non-rgb components
for (j = 3; j < buffer->colorsPerLed; j++)
{
*buffer1++;
}
// reference to buffer memory
uint8_t * p = &buffer->values[0];
for (k = 0; k < segments; k++) {
g1 = g2;
r1 = r2;
b1 = b2;
g2 = *buffer1++;
r2 = *buffer1++;
b2 = *buffer1++;
for (j = 3; j < buffer->colorsPerLed; j++) {
*buffer1++;
}
// Fill buffer
int numPixels = segmentSize;
// make sure we fill the strip correctly in case of rounding errors
if (k == segments - 1) {
numPixels = buffer->size - (segmentSize * (segments - 1));
}
int steps = numPixels - 1;
for(i = 0; i < numPixels; i++) {
*p++ = (g1 + ((g2-g1) * i / steps)) * state->brightness / 255;
*p++ = (r1 + ((r2-r1) * i / steps)) * state->brightness / 255;
*p++ = (b1 + ((b2-b1) * i / steps)) * state->brightness / 255;
for (j = 3; j < buffer->colorsPerLed; j++)
{
*p++ = 0;
}
}
}
return 0;
}
/*
* Lights all LEDs in one random color up. Then switches them
* to the next random color.
*/
static int ws2812_effects_mode_random_color() {
state->mode_color_index = get_random_wheel_index(state->mode_color_index);
ws2812_buffer * buffer = state->buffer;
uint32_t color = color_wheel(state->mode_color_index);
uint8_t r = ((color & 0x00FF0000) >> 16) * state->brightness / 255;
uint8_t g = ((color & 0x0000FF00) >> 8) * state->brightness / 255;
uint8_t b = ((color & 0x000000FF) >> 0) * state->brightness / 255;
// Fill buffer
int i,j;
uint8_t * p = &buffer->values[0];
for(i = 0; i < buffer->size; i++) {
*p++ = g;
*p++ = r;
*p++ = b;
for (j = 3; j < buffer->colorsPerLed; j++)
{
*p++ = 0;
}
}
}
/*
* Cycles all LEDs at once through a rainbow.
*/
static int ws2812_effects_mode_rainbow() {
ws2812_buffer * buffer = state->buffer;
uint32_t color = color_wheel(state->counter_mode_step);
uint8_t r = (color & 0x00FF0000) >> 16;
uint8_t g = (color & 0x0000FF00) >> 8;
uint8_t b = (color & 0x000000FF) >> 0;
// Fill buffer
int i,j;
uint8_t * p = &buffer->values[0];
for(i = 0; i < buffer->size; i++) {
*p++ = g * state->brightness / 255;
*p++ = r * state->brightness / 255;
*p++ = b * state->brightness / 255;
for (j = 3; j < buffer->colorsPerLed; j++)
{
*p++ = 0;
}
}
state->counter_mode_step = (state->counter_mode_step + 1) % 360;
return 0;
}
/*
* Cycles a rainbow over the entire string of LEDs.
*/
static int ws2812_effects_mode_rainbow_cycle(int repeat_count) {
ws2812_buffer * buffer = state->buffer;
int i,j;
uint8_t * p = &buffer->values[0];
for(i = 0; i < buffer->size; i++) {
uint16_t wheel_index = (i * 360 / buffer->size * repeat_count) % 360;
uint32_t color = color_wheel(wheel_index);
uint8_t r = ((color & 0x00FF0000) >> 16) * state->brightness / 255;
uint8_t g = ((color & 0x0000FF00) >> 8) * state->brightness / 255;
uint8_t b = ((color & 0x000000FF) >> 0) * state->brightness / 255;
*p++ = g;
*p++ = r;
*p++ = b;
for (j = 3; j < buffer->colorsPerLed; j++)
{
*p++ = 0;
}
}
return 0;
}
/*
* Random flickering.
*/
static int ws2812_effects_mode_flicker_int(uint8_t max_flicker) {
ws2812_buffer * buffer = state->buffer;
uint8_t p_g = state->color[0];
uint8_t p_r = state->color[1];
uint8_t p_b = state->color[2];
// Fill buffer
int i,j;
uint8_t * p = &buffer->values[0];
for(i = 0; i < buffer->size; i++) {
int flicker = rand() % (max_flicker > 0 ? max_flicker : 1);
int r1 = p_r-flicker;
int g1 = p_g-flicker;
int b1 = p_b-flicker;
if(g1<0) g1=0;
if(r1<0) r1=0;
if(b1<0) b1=0;
*p++ = g1 * state->brightness / 255;
*p++ = r1 * state->brightness / 255;
*p++ = b1 * state->brightness / 255;
for (j = 3; j < buffer->colorsPerLed; j++) {
*p++ = 0;
}
}
return 0;
}
/**
* Halloween effect
*/
static int ws2812_effects_mode_halloween() {
ws2812_buffer * buffer = state->buffer;
int g1 = 50 * state->brightness / 255;
int r1 = 255 * state->brightness / 255;
int b1 = 0 * state->brightness / 255;
int g2 = 0 * state->brightness / 255;
int r2 = 255 * state->brightness / 255;
int b2 = 130 * state->brightness / 255;
// Fill buffer
int i,j;
uint8_t * p = &buffer->values[0];
for(i = 0; i < buffer->size; i++) {
*p++ = (i % 4 < 2) ? g1 : g2;
*p++ = (i % 4 < 2) ? r1 : r2;
*p++ = (i % 4 < 2) ? b1 : b2;
for (j = 3; j < buffer->colorsPerLed; j++)
{
*p++ = 0;
}
}
return 0;
}
static int ws2812_effects_mode_circus_combustus() {
ws2812_buffer * buffer = state->buffer;
int g1 = 0 * state->brightness / 255;
int r1 = 255 * state->brightness / 255;
int b1 = 0 * state->brightness / 255;
int g2 = 255 * state->brightness / 255;
int r2 = 255 * state->brightness / 255;
int b2 = 255 * state->brightness / 255;
// Fill buffer
int i,j;
uint8_t * p = &buffer->values[0];
for(i = 0; i < buffer->size; i++) {
if (i % 6 < 2) {
*p++ = g1;
*p++ = r1;
*p++ = b1;
}
else if (i % 6 < 4) {
*p++ = g2;
*p++ = r2;
*p++ = b2;
}
else {
*p++ = 0;
*p++ = 0;
*p++ = 0;
}
for (j = 3; j < buffer->colorsPerLed; j++)
{
*p++ = 0;
}
}
return 0;
}
/*
* K.I.T.T.
*/
static int ws2812_effects_mode_larson_scanner() {
ws2812_buffer * buffer = state->buffer;
int led_index = 0;
for(int i=0; i < buffer->size * buffer->colorsPerLed; i++) {
buffer->values[i] = buffer->values[i] >> 1;
}
uint16_t pos = 0;
if(state->counter_mode_step < buffer->size) {
pos = state->counter_mode_step;
} else {
pos = (buffer->size * 2) - state->counter_mode_step - 2;
}
pos = pos * buffer->colorsPerLed;
buffer->values[pos + 1] = state->color[1];
buffer->values[pos] = state->color[0];
buffer->values[pos + 2] = state->color[2];
state->counter_mode_step = (state->counter_mode_step + 1) % ((buffer->size * 2) - 2);
}
static int ws2812_effects_mode_color_wipe() {
ws2812_buffer * buffer = state->buffer;
int led_index = (state->counter_mode_step % buffer->size) * buffer->colorsPerLed;
if (state->counter_mode_step >= buffer->size)
{
buffer->values[led_index] = 0;
buffer->values[led_index + 1] = 0;
buffer->values[led_index + 2] = 0;
}
else
{
uint8_t px_r = state->color[1] * state->brightness / 255;
uint8_t px_g = state->color[0] * state->brightness / 255;
uint8_t px_b = state->color[2] * state->brightness / 255;
buffer->values[led_index] = px_g;
buffer->values[led_index + 1] = px_r;
buffer->values[led_index + 2] = px_b;
}
state->counter_mode_step = (state->counter_mode_step + 1) % (buffer->size * 2);
}
static int ws2812_effects_mode_random_dot(uint8_t dots) {
ws2812_buffer * buffer = state->buffer;
// fade out
for(int i=0; i < buffer->size * buffer->colorsPerLed; i++) {
buffer->values[i] = buffer->values[i] >> 1;
}
for(int i=0; i < dots; i++) {
// pick random pixel
int led_index = rand() % buffer->size;
uint32_t color = (state->color[0] << 16) | (state->color[1] << 8) | state->color[2];
if (buffer->colorsPerLed == 4) {
color = color | (state->color[3] << 24);
}
ws2812_set_pixel(led_index, color);
}
state->counter_mode_step = (state->counter_mode_step + 1) % ((buffer->size * 2) - 2);
}
static uint32_t ws2812_effects_mode_delay()
{
// check if delay has been set explicitly
if (state->speed == 0 && state->mode_delay > 0)
{
return state->mode_delay;
}
uint32_t delay = 10;
switch (state->effect_type) {
case WS2812_EFFECT_BLINK:
case WS2812_EFFECT_RAINBOW:
case WS2812_EFFECT_RAINBOW_CYCLE:
delay = 10 + ((1000 * (uint32_t)(SPEED_MAX - state->speed)) / SPEED_MAX);
break;
case WS2812_EFFECT_FLICKER:
case WS2812_EFFECT_FIRE_FLICKER:
case WS2812_EFFECT_FIRE_FLICKER_SOFT:
case WS2812_EFFECT_FIRE_FLICKER_INTENSE:
delay = 30 + (rand() % 100) + (200 * (SPEED_MAX - state->speed) / SPEED_MAX);
break;
case WS2812_EFFECT_RANDOM_COLOR:
case WS2812_EFFECT_HALLOWEEN:
case WS2812_EFFECT_CIRCUS_COMBUSTUS:
case WS2812_EFFECT_LARSON_SCANNER:
case WS2812_EFFECT_CYCLE:
case WS2812_EFFECT_COLOR_WIPE:
case WS2812_EFFECT_RANDOM_DOT:
delay = 10 + ((1000 * (uint32_t)(SPEED_MAX - state->speed)) / SPEED_MAX);
break;
}
return delay;
}
/**
* run loop for the effects.
*/
static void ws2812_effects_loop(void *p)
{
if (state->effect_type == WS2812_EFFECT_BLINK)
{
ws2812_effects_mode_blink();
}
else if (state->effect_type == WS2812_EFFECT_RAINBOW)
{
ws2812_effects_mode_rainbow();
}
else if (state->effect_type == WS2812_EFFECT_RAINBOW_CYCLE)
{
// the rainbow cycle effect can be achieved by shifting the buffer
ws2812_buffer_shift(state->buffer, 1, SHIFT_CIRCULAR, 1, -1);
}
else if (state->effect_type == WS2812_EFFECT_FLICKER)
{
int flicker_value = state->effect_int_param1 != EFFECT_PARAM_INVALID ? state->effect_int_param1 : 100;
if (flicker_value == 0) {
flicker_value = 50;
}
ws2812_effects_mode_flicker_int(flicker_value);
state->counter_mode_step = (state->counter_mode_step + 1) % 256;
}
else if (state->effect_type == WS2812_EFFECT_FIRE_FLICKER)
{
ws2812_effects_mode_flicker_int(110);
state->counter_mode_step = (state->counter_mode_step + 1) % 256;
}
else if (state->effect_type == WS2812_EFFECT_FIRE_FLICKER_SOFT)
{
ws2812_effects_mode_flicker_int(70);
state->counter_mode_step = (state->counter_mode_step + 1) % 256;
}
else if (state->effect_type == WS2812_EFFECT_FIRE_FLICKER_INTENSE)
{
ws2812_effects_mode_flicker_int(170);
state->counter_mode_step = (state->counter_mode_step + 1) % 256;
}
else if (state->effect_type == WS2812_EFFECT_RANDOM_COLOR)
{
ws2812_effects_mode_random_color();
}
else if (state->effect_type == WS2812_EFFECT_HALLOWEEN)
{
ws2812_buffer_shift(state->buffer, 1, SHIFT_CIRCULAR, 1, -1);
}
else if (state->effect_type == WS2812_EFFECT_CIRCUS_COMBUSTUS)
{
ws2812_buffer_shift(state->buffer, 1, SHIFT_CIRCULAR, 1, -1);
}
else if (state->effect_type == WS2812_EFFECT_LARSON_SCANNER)
{
ws2812_effects_mode_larson_scanner();
}
else if (state->effect_type == WS2812_EFFECT_CYCLE)
{
ws2812_buffer_shift(state->buffer, state->effect_int_param1, SHIFT_CIRCULAR, 1, -1);
}
else if (state->effect_type == WS2812_EFFECT_COLOR_WIPE)
{
ws2812_effects_mode_color_wipe();
}
else if (state->effect_type == WS2812_EFFECT_RANDOM_DOT)
{
uint8_t dots = state->effect_int_param1 != EFFECT_PARAM_INVALID ? state->effect_int_param1 : 1;
ws2812_effects_mode_random_dot(dots);
}
// set the new delay for this effect
state->mode_delay = ws2812_effects_mode_delay();
// call count
state->counter_mode_call = (state->counter_mode_call + 1) % UINT32_MAX;
// write the buffer
ws2812_write(state->buffer);
// set the timer
if (state->running == 1 && state->mode_delay >= 10)
{
os_timer_disarm(&(state->os_t));
os_timer_arm(&(state->os_t), state->mode_delay, FALSE);
}
}
/**
* Set the active effect mode
*/
static int ws2812_effects_set_mode(lua_State* L) {
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
// opts must be same order as effect type enum
static const char * const opts[] = {"static", "blink", "gradient", "gradient_rgb", "random_color", "rainbow",
"rainbow_cycle", "flicker", "fire", "fire_soft", "fire_intense", "halloween", "circus_combustus",
"larson_scanner", "cycle", "color_wipe", "random_dot", NULL};
int type = luaL_checkoption(L, 1, NULL, opts);
state->effect_type = type;
int effect_param = EFFECT_PARAM_INVALID;
// check additional int parameter
// First mandatory parameter
int arg_type = lua_type(L, 2);
if (arg_type == LUA_TNONE || arg_type == LUA_TNIL)
{
// we don't have a second parameter
}
else if(arg_type == LUA_TNUMBER)
{
effect_param = luaL_optinteger( L, 2, EFFECT_PARAM_INVALID );
}
// initialize the effect
state->counter_mode_step = 0;
switch (state->effect_type) {
case WS2812_EFFECT_STATIC:
// fill with currently set color
ws2812_effects_fill_color();
state->mode_delay = 250;
break;
case WS2812_EFFECT_BLINK:
ws2812_effects_mode_blink();
break;
case WS2812_EFFECT_GRADIENT:
if(arg_type == LUA_TSTRING)
{
size_t length1;
const char *buffer1 = lua_tolstring(L, 2, &length1);
if ((length1 / state->buffer->colorsPerLed < 2) || (length1 % state->buffer->colorsPerLed != 0))
{
luaL_argerror(L, 2, "must be at least two colors and same size as buffer colors");
}
ws2812_effects_gradient(buffer1, length1);
ws2812_write(state->buffer);
}
else
{
luaL_argerror(L, 2, "string expected");
}
break;
case WS2812_EFFECT_GRADIENT_RGB:
if(arg_type == LUA_TSTRING)
{
size_t length1;
const char *buffer1 = lua_tolstring(L, 2, &length1);
if ((length1 / state->buffer->colorsPerLed < 2) || (length1 % state->buffer->colorsPerLed != 0))
{
luaL_argerror(L, 2, "must be at least two colors and same size as buffer colors");
}
ws2812_effects_gradient_rgb(buffer1, length1);
ws2812_write(state->buffer);
}
else
{
luaL_argerror(L, 2, "string expected");
}
break;
case WS2812_EFFECT_RANDOM_COLOR:
ws2812_effects_mode_random_color();
break;
case WS2812_EFFECT_RAINBOW:
ws2812_effects_mode_rainbow();
break;
case WS2812_EFFECT_RAINBOW_CYCLE:
ws2812_effects_mode_rainbow_cycle(effect_param != EFFECT_PARAM_INVALID ? effect_param : 1);
break;
// flicker
case WS2812_EFFECT_FLICKER:
state->effect_int_param1 = effect_param;
break;
case WS2812_EFFECT_FIRE_FLICKER:
case WS2812_EFFECT_FIRE_FLICKER_SOFT:
case WS2812_EFFECT_FIRE_FLICKER_INTENSE:
{
state->color[0] = 255-40;
state->color[1] = 255;
state->color[2] = 40;
state->color[3] = 0;
}
break;
case WS2812_EFFECT_HALLOWEEN:
ws2812_effects_mode_halloween();
break;
case WS2812_EFFECT_CIRCUS_COMBUSTUS:
ws2812_effects_mode_circus_combustus();
break;
case WS2812_EFFECT_LARSON_SCANNER:
ws2812_effects_mode_larson_scanner();
break;
case WS2812_EFFECT_CYCLE:
if (effect_param != EFFECT_PARAM_INVALID) {
state->effect_int_param1 = effect_param;
}
break;
case WS2812_EFFECT_COLOR_WIPE:
{
uint32_t black = 0;
ws2812_effects_fill_buffer(black);
ws2812_effects_mode_color_wipe();
break;
}
case WS2812_EFFECT_RANDOM_DOT:
{
// check if more than 1 dot shall be set
state->effect_int_param1 = effect_param;
uint32_t black = 0;
ws2812_effects_fill_buffer(black);
break;
}
}
}
/*
* Start the effect execution
*/
static int ws2812_effects_start(lua_State* L) {
//NODE_DBG("pin:%d, level:%d \n", pin, level);
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
if (state != NULL) {
os_timer_disarm(&(state->os_t));
state->running = 1;
state->counter_mode_call = 0;
state->counter_mode_step = 0;
// set the timer
os_timer_setfn(&(state->os_t), ws2812_effects_loop, NULL);
os_timer_arm(&(state->os_t), state->mode_delay, FALSE);
}
return 0;
}
/*
* Stop the effect execution
*/
static int ws2812_effects_stop(lua_State* L) {
luaL_argcheck(L, state != NULL, 1, LIBRARY_NOT_INITIALIZED_ERROR_MSG);
if (state != NULL) {
os_timer_disarm(&(state->os_t));
state->running = 0;
}
return 0;
}
static int ws2812_effects_tostring(lua_State* L) {
luaL_Buffer result;
luaL_buffinit(L, &result);
luaL_addchar(&result, '[');
luaL_addstring(&result, "effects");
luaL_addchar(&result, ']');
luaL_pushresult(&result);
return 1;
}
static const LUA_REG_TYPE ws2812_effects_map[] =
{
{ LSTRKEY( "init" ), LFUNCVAL( ws2812_effects_init )},
{ LSTRKEY( "set_brightness" ), LFUNCVAL( ws2812_effects_set_brightness )},
{ LSTRKEY( "set_color" ), LFUNCVAL( ws2812_effects_set_color )},
{ LSTRKEY( "set_speed" ), LFUNCVAL( ws2812_effects_set_speed )},
{ LSTRKEY( "set_delay" ), LFUNCVAL( ws2812_effects_set_delay )},
{ LSTRKEY( "set_mode" ), LFUNCVAL( ws2812_effects_set_mode )},
{ LSTRKEY( "start" ), LFUNCVAL( ws2812_effects_start )},
{ LSTRKEY( "stop" ), LFUNCVAL( ws2812_effects_stop )},
{ LSTRKEY( "get_delay" ), LFUNCVAL( ws2812_effects_get_delay )},
{ LSTRKEY( "get_speed" ), LFUNCVAL( ws2812_effects_get_speed )},
{ LSTRKEY( "__index" ), LROVAL( ws2812_effects_map )},
{ LSTRKEY( "__tostring" ), LFUNCVAL( ws2812_effects_tostring )},
{ LNILKEY, LNILVAL}
};
NODEMCU_MODULE(WS2812_EFFECTS, "ws2812_effects", ws2812_effects_map, NULL);
......@@ -33,7 +33,7 @@ static void ICACHE_RAM_ATTR drv_sd_timer_isr( os_param_t arg )
if (cfg->vu_samples_tmp >= cfg->vu_req_samples) {
cfg->vu_peak = cfg->vu_peak_tmp;
task_post_low( cfg->data_vu_task, (os_param_t)cfg );
task_post_low( pcm_data_vu_task, (os_param_t)cfg );
cfg->vu_samples_tmp = 0;
cfg->vu_peak_tmp = 0;
......@@ -44,7 +44,7 @@ static void ICACHE_RAM_ATTR drv_sd_timer_isr( os_param_t arg )
// buffer data consumed, request to re-fill it
buf->empty = TRUE;
cfg->fbuf_idx = cfg->rbuf_idx;
task_post_high( cfg->data_play_task, (os_param_t)cfg );
task_post_high( pcm_data_play_task, (os_param_t)cfg );
// switch to next buffer
cfg->rbuf_idx ^= 1;
dbg_platform_gpio_write( PLATFORM_GPIO_LOW );
......@@ -54,7 +54,7 @@ static void ICACHE_RAM_ATTR drv_sd_timer_isr( os_param_t arg )
cfg->isr_throttled = 1;
dbg_platform_gpio_write( PLATFORM_GPIO_LOW );
cfg->fbuf_idx = cfg->rbuf_idx;
task_post_high( cfg->data_play_task, (os_param_t)cfg );
task_post_high( pcm_data_play_task, (os_param_t)cfg );
}
}
......
......@@ -4,6 +4,7 @@
#define _PCM_H
#include "task/task.h"
#include "platform.h"
......@@ -63,8 +64,6 @@ typedef struct {
// buffer selectors
uint8_t rbuf_idx; // read by ISR
uint8_t fbuf_idx; // fill by data task
// task handles
task_handle_t data_vu_task, data_play_task, start_play_task;
// callback fn refs
int self_ref;
int cb_data_ref, cb_drained_ref, cb_paused_ref, cb_stopped_ref, cb_vu_ref;
......@@ -95,7 +94,10 @@ typedef struct {
} pud_t;
void pcm_data_vu_task( task_param_t param, uint8 prio );
void pcm_data_play_task( task_param_t param, uint8 prio );
void pcm_data_vu( task_param_t param, uint8 prio );
void pcm_data_play( task_param_t param, uint8 prio );
// task handles
extern task_handle_t pcm_data_vu_task, pcm_data_play_task, pcm_start_play_task;
#endif /* _PCM_H */
......@@ -27,7 +27,7 @@ static void dispatch_callback( lua_State *L, int self_ref, int cb_ref, int retur
}
}
void pcm_data_vu_task( task_param_t param, uint8 prio )
void pcm_data_vu( task_param_t param, uint8 prio )
{
cfg_t *cfg = (cfg_t *)param;
lua_State *L = lua_getstate();
......@@ -40,7 +40,7 @@ void pcm_data_vu_task( task_param_t param, uint8 prio )
}
}
void pcm_data_play_task( task_param_t param, uint8 prio )
void pcm_data_play( task_param_t param, uint8 prio )
{
cfg_t *cfg = (cfg_t *)param;
pcm_buf_t *buf = &(cfg->bufs[cfg->fbuf_idx]);
......@@ -85,7 +85,7 @@ void pcm_data_play_task( task_param_t param, uint8 prio )
// rerun data callback to get next buffer chunk
dbg_platform_gpio_write( PLATFORM_GPIO_LOW );
cfg->fbuf_idx = other_buf;
pcm_data_play_task( param, 0 );
pcm_data_play( param, 0 );
}
// unthrottle ISR
cfg->isr_throttled = 0;
......
......@@ -9,6 +9,7 @@ uint8_t pin_func[GPIO_PIN_NUM];
#ifdef GPIO_INTERRUPT_ENABLE
uint8_t pin_num_inv[GPIO_PIN_NUM_INV];
uint8_t pin_int_type[GPIO_PIN_NUM];
GPIO_INT_COUNTER pin_counter[GPIO_PIN_NUM];
#endif
typedef struct {
......
......@@ -15,6 +15,13 @@ extern uint8_t pin_func[GPIO_PIN_NUM];
#ifdef GPIO_INTERRUPT_ENABLE
extern uint8_t pin_num_inv[GPIO_PIN_NUM_INV];
extern uint8_t pin_int_type[GPIO_PIN_NUM];
typedef struct {
// These values have 15 bits of count, and the top bit
// in 'seen' is set if we are missing a task post
volatile uint16_t seen;
volatile uint16_t reported;
} GPIO_INT_COUNTER;
extern GPIO_INT_COUNTER pin_counter[GPIO_PIN_NUM];
#endif
void get_pin_map(void);
......
......@@ -14,6 +14,8 @@
#include "driver/uart.h"
#include "driver/sigma_delta.h"
#define INTERRUPT_TYPE_IS_LEVEL(x) ((x) >= GPIO_PIN_INTR_LOLEVEL)
#ifdef GPIO_INTERRUPT_ENABLE
static task_handle_t gpio_task_handle;
......@@ -103,6 +105,7 @@ static void NO_INTR_CODE set_gpio_no_interrupt(uint8 pin, uint8_t push_pull) {
GPIO_REG_READ(GPIO_PIN_ADDR(GPIO_ID_PIN(pnum))) |
GPIO_PIN_PAD_DRIVER_SET(GPIO_PAD_DRIVER_ENABLE)); //enable open drain;
}
ETS_GPIO_INTR_ENABLE();
}
......@@ -151,12 +154,15 @@ int platform_gpio_mode( unsigned pin, unsigned mode, unsigned pull )
case PLATFORM_GPIO_INPUT:
GPIO_DIS_OUTPUT(pin_num[pin]);
/* run on */
set_gpio_no_interrupt(pin, TRUE);
break;
case PLATFORM_GPIO_OUTPUT:
set_gpio_no_interrupt(pin, TRUE);
GPIO_REG_WRITE(GPIO_ENABLE_W1TS_ADDRESS, BIT(pin_num[pin]));
break;
case PLATFORM_GPIO_OPENDRAIN:
set_gpio_no_interrupt(pin, FALSE);
GPIO_REG_WRITE(GPIO_ENABLE_W1TS_ADDRESS, BIT(pin_num[pin]));
break;
#ifdef GPIO_INTERRUPT_ENABLE
......@@ -226,13 +232,29 @@ static void ICACHE_RAM_ATTR platform_gpio_intr_dispatcher (void *dummy){
if (gpio_status&1) {
int i = pin_num_inv[j];
if (pin_int_type[i]) {
//disable interrupt
gpio_pin_intr_state_set(GPIO_ID_PIN(j), GPIO_PIN_INTR_DISABLE);
uint16_t diff = pin_counter[i].seen ^ pin_counter[i].reported;
pin_counter[i].seen = 0x7fff & (pin_counter[i].seen + 1);
if (INTERRUPT_TYPE_IS_LEVEL(pin_int_type[i])) {
//disable interrupt
gpio_pin_intr_state_set(GPIO_ID_PIN(j), GPIO_PIN_INTR_DISABLE);
}
//clear interrupt status
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(j));
uint32 level = 0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(j));
task_post_high (gpio_task_handle, (now << 8) + (i<<1) + level);
// We re-enable the interrupt when we execute the callback
if (diff == 0 || diff & 0x8000) {
uint32 level = 0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(j));
if (!task_post_high (gpio_task_handle, (now << 8) + (i<<1) + level)) {
// If we fail to post, then try on the next interrupt
pin_counter[i].seen |= 0x8000;
}
// We re-enable the interrupt when we execute the callback (if level)
}
} else {
// this is an unexpected interrupt so shut it off for now
gpio_pin_intr_state_set(GPIO_ID_PIN(j), GPIO_PIN_INTR_DISABLE);
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(j));
}
}
}
......
......@@ -15,7 +15,7 @@
// vfs_close - close file descriptor and free memory
// fd: file descriptor
// Returns: VFS_RES_OK or negative value in case of error
inline sint32_t vfs_close( int fd ) {
static sint32_t vfs_close( int fd ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->close( f ) : VFS_RES_ERR;
}
......@@ -25,7 +25,7 @@ inline sint32_t vfs_close( int fd ) {
// ptr: destination data buffer
// len: requested length
// Returns: Number of bytes read, or VFS_RES_ERR in case of error
inline sint32_t vfs_read( int fd, void *ptr, size_t len ) {
static sint32_t vfs_read( int fd, void *ptr, size_t len ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->read( f, ptr, len ) : VFS_RES_ERR;
}
......@@ -35,7 +35,7 @@ inline sint32_t vfs_read( int fd, void *ptr, size_t len ) {
// ptr: source data buffer
// len: requested length
// Returns: Number of bytes written, or VFS_RES_ERR in case of error
inline sint32_t vfs_write( int fd, const void *ptr, size_t len ) {
static sint32_t vfs_write( int fd, const void *ptr, size_t len ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->write( f, ptr, len ) : VFS_RES_ERR;
}
......@@ -51,7 +51,7 @@ int vfs_ungetc( int c, int fd );
// VFS_SEEK_CUR - set pointer to current position + off
// VFS_SEEK_END - set pointer to end of file + off
// Returns: New position, or VFS_RES_ERR in case of error
inline sint32_t vfs_lseek( int fd, sint32_t off, int whence ) {
static sint32_t vfs_lseek( int fd, sint32_t off, int whence ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->lseek( f, off, whence ) : VFS_RES_ERR;
}
......@@ -59,7 +59,7 @@ inline sint32_t vfs_lseek( int fd, sint32_t off, int whence ) {
// vfs_eof - test for end-of-file
// fd: file descriptor
// Returns: 0 if not at end, != 0 if end of file
inline sint32_t vfs_eof( int fd ) {
static sint32_t vfs_eof( int fd ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->eof( f ) : VFS_RES_ERR;
}
......@@ -67,7 +67,7 @@ inline sint32_t vfs_eof( int fd ) {
// vfs_tell - get read/write position
// fd: file descriptor
// Returns: Current position
inline sint32_t vfs_tell( int fd ) {
static sint32_t vfs_tell( int fd ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->tell( f ) : VFS_RES_ERR;
}
......@@ -75,7 +75,7 @@ inline sint32_t vfs_tell( int fd ) {
// vfs_flush - flush write cache to file
// fd: file descriptor
// Returns: VFS_RES_OK, or VFS_RES_ERR in case of error
inline sint32_t vfs_flush( int fd ) {
static sint32_t vfs_flush( int fd ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->flush( f ) : VFS_RES_ERR;
}
......@@ -83,7 +83,7 @@ inline sint32_t vfs_flush( int fd ) {
// vfs_size - get current file size
// fd: file descriptor
// Returns: File size
inline uint32_t vfs_size( int fd ) {
static uint32_t vfs_size( int fd ) {
vfs_file *f = (vfs_file *)fd;
return f ? f->fns->size( f ) : 0;
}
......@@ -100,13 +100,13 @@ sint32_t vfs_ferrno( int fd );
// vfs_closedir - close directory descriptor and free memory
// dd: dir descriptor
// Returns: VFS_RES_OK, or VFS_RES_ERR in case of error
inline sint32_t vfs_closedir( vfs_dir *dd ) { return dd->fns->close( dd ); }
static sint32_t vfs_closedir( vfs_dir *dd ) { return dd->fns->close( dd ); }
// vfs_readdir - read next directory item
// dd: dir descriptor
// buf: pre-allocated stat structure to be filled in
// Returns: VFS_RES_OK if next item found, otherwise VFS_RES_ERR
inline sint32_t vfs_readdir( vfs_dir *dd, struct vfs_stat *buf ) { return dd->fns->readdir( dd, buf ); }
static sint32_t vfs_readdir( vfs_dir *dd, struct vfs_stat *buf ) { return dd->fns->readdir( dd, buf ); }
// ---------------------------------------------------------------------------
// volume functions
......@@ -115,7 +115,7 @@ inline sint32_t vfs_readdir( vfs_dir *dd, struct vfs_stat *buf ) { return dd->fn
// vfs_umount - unmount logical drive and free memory
// vol: volume object
// Returns: VFS_RES_OK, or VFS_RES_ERR in case of error
inline sint32_t vfs_umount( vfs_vol *vol ) { return vol->fns->umount( vol ); }
static sint32_t vfs_umount( vfs_vol *vol ) { return vol->fns->umount( vol ); }
// ---------------------------------------------------------------------------
// file system functions
......
The MIT License (MIT)
Copyright (c) 2013-2016 Peter Andersson (pelleplutt1976<at>gmail.com)
Copyright (c) 2013-2017 Peter Andersson (pelleplutt1976<at>gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
......
......@@ -14,7 +14,7 @@ spiffs fs;
static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];
static u8_t spiffs_fds[sizeof(spiffs_fd) * SPIFFS_MAX_OPEN_FILES];
#if SPIFFS_CACHE
static u8_t myspiffs_cache[(LOG_PAGE_SIZE+32)*2];
static u8_t myspiffs_cache[20 + (LOG_PAGE_SIZE+20)*4];
#endif
static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) {
......
......@@ -60,6 +60,9 @@ extern "C" {
#define SPIFFS_ERR_IX_MAP_MAPPED -10038
#define SPIFFS_ERR_IX_MAP_BAD_RANGE -10039
#define SPIFFS_ERR_SEEK_BOUNDS -10040
#define SPIFFS_ERR_INTERNAL -10050
#define SPIFFS_ERR_TEST -10100
......
......@@ -20,7 +20,7 @@ static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix)
if ((cache->cpage_use_map & (1<<i)) &&
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
cp->pix == pix ) {
SPIFFS_CACHE_DBG("CACHE_GET: have cache page "_SPIPRIi" for "_SPIPRIpg"\n", i, pix);
//SPIFFS_CACHE_DBG("CACHE_GET: have cache page "_SPIPRIi" for "_SPIPRIpg"\n", i, pix);
cp->last_access = cache->last_access;
return cp;
}
......@@ -39,17 +39,20 @@ static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) {
(cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 &&
(cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) {
u8_t *mem = spiffs_get_cache_page(fs, cache, ix);
SPIFFS_CACHE_DBG("CACHE_FREE: write cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix);
res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem);
}
cp->flags = 0;
cache->cpage_use_map &= ~(1 << ix);
#if SPIFFS_CACHE_WR
if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) {
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" objid "_SPIPRIid"\n", ix, cp->obj_id);
} else {
} else
#endif
{
SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->pix);
}
cache->cpage_use_map &= ~(1 << ix);
cp->flags = 0;
}
return res;
......@@ -98,7 +101,7 @@ static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) {
spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i);
cache->cpage_use_map |= (1<<i);
cp->last_access = cache->last_access;
SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi"\n", i);
//SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi"\n", i);
return cp;
}
}
......@@ -136,7 +139,7 @@ s32_t spiffs_phys_rd(
#endif
cp->last_access = cache->last_access;
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
_SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
} else {
if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) {
// for second layer lookup functions, we do not cache in order to prevent shredding
......@@ -153,6 +156,7 @@ s32_t spiffs_phys_rd(
if (cp) {
cp->flags = SPIFFS_CACHE_FLAG_WRTHRU;
cp->pix = SPIFFS_PADDR_TO_PAGE(fs, addr);
SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for pix "_SPIPRIpg "\n", cp->ix, cp->pix);
s32_t res2 = SPIFFS_HAL_READ(fs,
addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr),
......@@ -163,7 +167,7 @@ s32_t spiffs_phys_rd(
res = res2;
}
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
memcpy(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
_SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len);
} else {
// this will never happen, last resort for sake of symmetry
s32_t res2 = SPIFFS_HAL_READ(fs, addr, len, dst);
......@@ -201,7 +205,7 @@ s32_t spiffs_phys_wr(
}
u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix);
memcpy(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len);
_SPIFFS_MEMCPY(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len);
cache->last_access++;
cp->last_access = cache->last_access;
......@@ -256,6 +260,7 @@ spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) {
cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR;
cp->obj_id = fd->obj_id;
fd->cache_page = cp;
SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for fd "_SPIPRIfd ":"_SPIPRIid "\n", cp->ix, fd->file_nbr, fd->obj_id);
return cp;
}
......@@ -299,7 +304,7 @@ void spiffs_cache_init(spiffs *fs) {
cache.cpage_use_map = 0xffffffff;
cache.cpage_use_mask = cache_mask;
memcpy(fs->cache, &cache, sizeof(spiffs_cache));
_SPIFFS_MEMCPY(fs->cache, &cache, sizeof(spiffs_cache));
spiffs_cache *c = spiffs_get_cache(fs);
......
......@@ -161,11 +161,17 @@ static s32_t spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) {
return SPIFFS_OK;
}
SPIFFS_CHECK_RES(res);
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_IXDELE;
u8_t flags = 0xff;
#if SPIFFS_NO_BLIND_WRITES
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags),
sizeof(flags), &flags);
SPIFFS_CHECK_RES(res);
#endif
flags &= ~SPIFFS_PH_FLAG_IXDELE;
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT,
0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags),
sizeof(u8_t),
(u8_t *)&flags);
sizeof(flags), &flags);
return res;
}
......@@ -423,10 +429,17 @@ static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, s
// just finalize
SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n");
CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix);
u8_t flags = 0xff & ~SPIFFS_PH_FLAG_FINAL;
u8_t flags = 0xff;
#if SPIFFS_NO_BLIND_WRITES
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ,
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags),
sizeof(flags), &flags);
SPIFFS_CHECK_RES(res);
#endif
flags &= ~SPIFFS_PH_FLAG_FINAL;
res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT,
0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags),
sizeof(u8_t), (u8_t*)&flags);
sizeof(flags), &flags);
}
}
}
......
......@@ -34,6 +34,10 @@
#ifndef SPIFFS_CHECK_DBG
#define SPIFFS_CHECK_DBG(...) //dbg_printf(__VA_ARGS__)
#endif
// Set spiffs debug output call for all api invocations.
#ifndef SPIFFS_API_DBG
#define SPIFFS_API_DBG(_f, ...) //printf(_f, ## __VA_ARGS__)
#endif
// Defines spiffs debug print formatters
// some general signed number
......@@ -303,6 +307,17 @@
#define SPIFFS_IX_MAP 0
#endif
// By default SPIFFS in some cases relies on the property of NOR flash that bits
// cannot be set from 0 to 1 by writing and that controllers will ignore such
// bit changes. This results in fewer reads as SPIFFS can in some cases perform
// blind writes, with all bits set to 1 and only those it needs reset set to 0.
// Most of the chips and controllers allow this behavior, so the default is to
// use this technique. If your controller is one of the rare ones that don't,
// turn this option on and SPIFFS will perform a read-modify-write instead.
#ifndef SPIFFS_NO_BLIND_WRITES
#define SPIFFS_NO_BLIND_WRITES 0
#endif
// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
// in the api. This function will visualize all filesystem using given printf
// function.
......
......@@ -294,7 +294,7 @@ s32_t spiffs_gc_find_candidate(
// calculate score and insert into candidate table
// stoneage sort, but probably not so many blocks
if (res == SPIFFS_OK && deleted_pages_in_block > 0) {
if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/) {
// read erase count
spiffs_obj_id erase_count;
res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0,
......
......@@ -8,14 +8,6 @@
#include "spiffs.h"
#include "spiffs_nucleus.h"
#if SPIFFS_FILEHDL_OFFSET
#define SPIFFS_FH_OFFS(fs, fh) ((fh) != 0 ? ((fh) + (fs)->cfg.fh_ix_offset) : 0)
#define SPIFFS_FH_UNOFFS(fs, fh) ((fh) != 0 ? ((fh) - (fs)->cfg.fh_ix_offset) : 0)
#else
#define SPIFFS_FH_OFFS(fs, fh) (fh)
#define SPIFFS_FH_UNOFFS(fs, fh) (fh)
#endif
#if SPIFFS_CACHE == 1
static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh);
#endif
......@@ -69,6 +61,7 @@ s32_t SPIFFS_format(spiffs *fs) {
#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0
s32_t SPIFFS_probe_fs(spiffs_config *config) {
SPIFFS_API_DBG("%s\n", __func__);
s32_t res = spiffs_probe(config);
return res;
}
......@@ -79,11 +72,23 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
u8_t *fd_space, u32_t fd_space_size,
void *cache, u32_t cache_size,
spiffs_check_callback check_cb_f) {
SPIFFS_API_DBG("%s "
" sz:"_SPIPRIi " logpgsz:"_SPIPRIi " logblksz:"_SPIPRIi " perasz:"_SPIPRIi
" addr:"_SPIPRIad
" fdsz:"_SPIPRIi " cachesz:"_SPIPRIi
"\n",
__func__,
SPIFFS_CFG_PHYS_SZ(fs),
SPIFFS_CFG_LOG_PAGE_SZ(fs),
SPIFFS_CFG_LOG_BLOCK_SZ(fs),
SPIFFS_CFG_PHYS_ERASE_SZ(fs),
SPIFFS_CFG_PHYS_ADDR(fs),
fd_space_size, cache_size);
void *user_data;
SPIFFS_LOCK(fs);
user_data = fs->user_data;
memset(fs, 0, sizeof(spiffs));
memcpy(&fs->cfg, config, sizeof(spiffs_config));
_SPIFFS_MEMCPY(&fs->cfg, config, sizeof(spiffs_config));
fs->user_data = user_data;
fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs);
fs->work = &work[0];
......@@ -148,6 +153,7 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
}
void SPIFFS_unmount(spiffs *fs) {
SPIFFS_API_DBG("%s\n", __func__);
if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) return;
SPIFFS_LOCK(fs);
u32_t i;
......@@ -171,10 +177,12 @@ s32_t SPIFFS_errno(spiffs *fs) {
}
void SPIFFS_clearerr(spiffs *fs) {
SPIFFS_API_DBG("%s\n", __func__);
fs->err_code = SPIFFS_OK;
}
s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) {
SPIFFS_API_DBG("%s '%s'\n", __func__, path);
#if SPIFFS_READ_ONLY
(void)fs; (void)path; (void)mode;
return SPIFFS_ERR_RO_NOT_IMPL;
......@@ -199,6 +207,7 @@ s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) {
}
spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode) {
SPIFFS_API_DBG("%s '%s' "_SPIPRIfl "\n", __func__, path, flags);
(void)mode;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
......@@ -279,6 +288,7 @@ spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs
}
spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) {
SPIFFS_API_DBG("%s '%s':"_SPIPRIid " "_SPIPRIfl "\n", __func__, e->name, e->obj_id, flags);
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs);
......@@ -311,6 +321,7 @@ spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_fl
}
spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode) {
SPIFFS_API_DBG("%s "_SPIPRIpg " "_SPIPRIfl "\n", __func__, page_ix, flags);
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs);
......@@ -411,6 +422,7 @@ static s32_t spiffs_hydro_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len)
}
s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len);
s32_t res = spiffs_hydro_read(fs, fh, buf, len);
if (res == SPIFFS_ERR_END_OF_OBJECT) {
res = 0;
......@@ -444,6 +456,7 @@ static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offs
#endif // !SPIFFS_READ_ONLY
s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len);
#if SPIFFS_READ_ONLY
(void)fs; (void)fh; (void)buf; (void)len;
return SPIFFS_ERR_RO_NOT_IMPL;
......@@ -468,7 +481,6 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
if ((fd->flags & SPIFFS_O_APPEND)) {
fd->fdoffset = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size;
}
offset = fd->fdoffset;
#if SPIFFS_CACHE_WR
......@@ -502,7 +514,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page
{
// boundary violation, write back cache first and allocate new
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", boundary viol, offs:"_SPIPRIi" size:"_SPIPRIi"\n",
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", boundary viol, offs:"_SPIPRIi" size:"_SPIPRIi"\n",
fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
res = spiffs_hydro_write(fs, fd,
spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
......@@ -520,19 +532,30 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
if (fd->cache_page) {
fd->cache_page->offset = offset;
fd->cache_page->size = 0;
SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid"\n",
SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid"\n",
fd->cache_page->ix, fd->file_nbr, fd->obj_id);
}
}
if (fd->cache_page) {
u32_t offset_in_cpage = offset - fd->cache_page->offset;
SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", offs "_SPIPRIi":"_SPIPRIi" len "_SPIPRIi"\n",
SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", offs "_SPIPRIi":"_SPIPRIi" len "_SPIPRIi"\n",
fd->cache_page->ix, fd->file_nbr, fd->obj_id,
offset, offset_in_cpage, len);
spiffs_cache *cache = spiffs_get_cache(fs);
u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix);
memcpy(&cpage_data[offset_in_cpage], buf, len);
#ifdef _SPIFFS_TEST
{
intptr_t __a1 = (u8_t*)&cpage_data[offset_in_cpage]-(u8_t*)cache;
intptr_t __a2 = (u8_t*)&cpage_data[offset_in_cpage]+len-(u8_t*)cache;
intptr_t __b = sizeof(spiffs_cache) + cache->cpage_count * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs));
if (__a1 > __b || __a2 > __b) {
printf("FATAL OOB: CACHE_WR: memcpy to cache buffer ixs:%4ld..%4ld of %4ld\n", __a1, __a2, __b);
ERREXIT();
}
}
#endif
_SPIFFS_MEMCPY(&cpage_data[offset_in_cpage], buf, len);
fd->cache_page->size = MAX(fd->cache_page->size, offset_in_cpage + len);
fd->fdoffset += len;
SPIFFS_UNLOCK(fs);
......@@ -548,7 +571,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
// big write, no need to cache it - but first check if there is a cached write already
if (fd->cache_page) {
// write back cache first
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", big write, offs:"_SPIPRIi" size:"_SPIPRIi"\n",
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", big write, offs:"_SPIPRIi" size:"_SPIPRIi"\n",
fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
res = spiffs_hydro_write(fs, fd,
spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
......@@ -572,6 +595,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
}
s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) {
SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " %s\n", __func__, fh, offs, (const char* []){"SET","CUR","END","???"}[MIN(whence,3)]);
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs);
......@@ -586,24 +610,26 @@ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) {
spiffs_fflush_cache(fs, fh);
#endif
s32_t fileSize = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size;
s32_t file_size = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size;
switch (whence) {
case SPIFFS_SEEK_CUR:
offs = fd->fdoffset+offs;
break;
case SPIFFS_SEEK_END:
offs = fileSize + offs;
offs = file_size + offs;
break;
}
if ((offs > fileSize)) {
fd->fdoffset = fileSize;
if (offs < 0) {
SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_SEEK_BOUNDS);
}
if (offs > file_size) {
fd->fdoffset = file_size;
res = SPIFFS_ERR_END_OF_OBJECT;
}
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
spiffs_span_ix data_spix = offs / SPIFFS_DATA_PAGE_SIZE(fs);
spiffs_span_ix data_spix = (offs > 0 ? (offs-1) : 0) / SPIFFS_DATA_PAGE_SIZE(fs);
spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix);
if (fd->cursor_objix_spix != objix_spix) {
spiffs_page_ix pix;
......@@ -621,6 +647,7 @@ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) {
}
s32_t SPIFFS_remove(spiffs *fs, const char *path) {
SPIFFS_API_DBG("%s '%s'\n", __func__, path);
#if SPIFFS_READ_ONLY
(void)fs; (void)path;
return SPIFFS_ERR_RO_NOT_IMPL;
......@@ -663,6 +690,7 @@ s32_t SPIFFS_remove(spiffs *fs, const char *path) {
}
s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) {
SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh);
#if SPIFFS_READ_ONLY
(void)fs; (void)fh;
return SPIFFS_ERR_RO_NOT_IMPL;
......@@ -716,13 +744,14 @@ static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spi
s->pix = pix;
strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN);
#if SPIFFS_OBJ_META_LEN
memcpy(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN);
_SPIFFS_MEMCPY(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN);
#endif
return res;
}
s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) {
SPIFFS_API_DBG("%s '%s'\n", __func__, path);
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) {
......@@ -744,6 +773,7 @@ s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) {
}
s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) {
SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh);
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs);
......@@ -785,7 +815,7 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) {
fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd);
}
if (fd->cache_page) {
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIpg" for fd "_SPIPRIfd":"_SPIPRIid", flush, offs:"_SPIPRIi" size:"_SPIPRIi"\n",
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", flush, offs:"_SPIPRIi" size:"_SPIPRIi"\n",
fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
res = spiffs_hydro_write(fs, fd,
spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
......@@ -803,6 +833,7 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) {
#endif
s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) {
SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh);
(void)fh;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
......@@ -819,6 +850,7 @@ s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) {
}
s32_t SPIFFS_close(spiffs *fs, spiffs_file fh) {
SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh);
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
......@@ -839,6 +871,7 @@ s32_t SPIFFS_close(spiffs *fs, spiffs_file fh) {
}
s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) {
SPIFFS_API_DBG("%s %s %s\n", __func__, old_path, new_path);
#if SPIFFS_READ_ONLY
(void)fs; (void)old_path; (void)new_path;
return SPIFFS_ERR_RO_NOT_IMPL;
......@@ -965,6 +998,7 @@ s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta) {
#endif // SPIFFS_OBJ_META_LEN
spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) {
SPIFFS_API_DBG("%s\n", __func__);
(void)name;
if (!SPIFFS_CHECK_CFG((fs))) {
......@@ -1013,7 +1047,7 @@ static s32_t spiffs_read_dir_v(
e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size;
e->pix = pix;
#if SPIFFS_OBJ_META_LEN
memcpy(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN);
_SPIFFS_MEMCPY(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN);
#endif
return SPIFFS_OK;
}
......@@ -1021,6 +1055,7 @@ static s32_t spiffs_read_dir_v(
}
struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) {
SPIFFS_API_DBG("%s\n", __func__);
if (!SPIFFS_CHECK_MOUNT(d->fs)) {
d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
return 0;
......@@ -1055,12 +1090,14 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) {
}
s32_t SPIFFS_closedir(spiffs_DIR *d) {
SPIFFS_API_DBG("%s\n", __func__);
SPIFFS_API_CHECK_CFG(d->fs);
SPIFFS_API_CHECK_MOUNT(d->fs);
return 0;
}
s32_t SPIFFS_check(spiffs *fs) {
SPIFFS_API_DBG("%s\n", __func__);
#if SPIFFS_READ_ONLY
(void)fs;
return SPIFFS_ERR_RO_NOT_IMPL;
......@@ -1084,6 +1121,7 @@ s32_t SPIFFS_check(spiffs *fs) {
}
s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) {
SPIFFS_API_DBG("%s\n", __func__);
s32_t res = SPIFFS_OK;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
......@@ -1108,6 +1146,7 @@ s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) {
}
s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) {
SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, max_free_pages);
#if SPIFFS_READ_ONLY
(void)fs; (void)max_free_pages;
return SPIFFS_ERR_RO_NOT_IMPL;
......@@ -1127,6 +1166,7 @@ s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) {
s32_t SPIFFS_gc(spiffs *fs, u32_t size) {
SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, size);
#if SPIFFS_READ_ONLY
(void)fs; (void)size;
return SPIFFS_ERR_RO_NOT_IMPL;
......@@ -1145,6 +1185,7 @@ s32_t SPIFFS_gc(spiffs *fs, u32_t size) {
}
s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) {
SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh);
s32_t res;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
......@@ -1168,6 +1209,7 @@ s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) {
}
s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) {
SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh);
s32_t res;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
......@@ -1191,6 +1233,7 @@ s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) {
}
s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func) {
SPIFFS_API_DBG("%s\n", __func__);
SPIFFS_LOCK(fs);
fs->file_cb_f = cb_func;
SPIFFS_UNLOCK(fs);
......@@ -1201,6 +1244,7 @@ s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func) {
s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map,
u32_t offset, u32_t len, spiffs_page_ix *map_buf) {
SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " "_SPIPRIi "\n", __func__, fh, offset, len);
s32_t res;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
......@@ -1233,6 +1277,7 @@ s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map,
}
s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh) {
SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh);
s32_t res;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
......@@ -1255,6 +1300,7 @@ s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh) {
}
s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offset) {
SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, offset);
s32_t res = SPIFFS_OK;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
......
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