diff --git a/py/Makefile b/py/Makefile new file mode 100644 index 0000000..9b3f92f --- /dev/null +++ b/py/Makefile @@ -0,0 +1,18 @@ +SRC = ../src + +SOURCE_FILES = $(SRC)/spiffs_cache.c \ + $(SRC)/spiffs_check.c \ + $(SRC)/spiffs_gc.c \ + $(SRC)/spiffs_hydrogen.c \ + $(SRC)/spiffs_nucleus.c \ + python_ops.c + +INCLUDES = -I . \ + -I $(SRC)/ \ + -I $(SRC)/default + +spiffs_.so: $(SOURCE_FILES) + $(CC) -g3 -fPIC -shared $(INCLUDES) -o $@ $(SOURCE_FILES) -lm + +clean: + rm -rf spiffs_.so *~ *.pyc diff --git a/py/params_test.h b/py/params_test.h new file mode 100644 index 0000000..e81ff38 --- /dev/null +++ b/py/params_test.h @@ -0,0 +1,8 @@ +#include + +typedef int32_t s32_t; +typedef uint32_t u32_t; +typedef int16_t s16_t; +typedef uint16_t u16_t; +typedef int8_t s8_t; +typedef uint8_t u8_t; diff --git a/py/python_ops.c b/py/python_ops.c new file mode 100644 index 0000000..057be82 --- /dev/null +++ b/py/python_ops.c @@ -0,0 +1,92 @@ +#include +#include + +#include "spiffs.h" + +static s32_t my_spiffs_read(u32_t addr, u32_t size, u8_t *dst) { + //my_spi_read(addr, size, dst); + printf("SPI read %d bytes at %x\n",size,addr); + + return SPIFFS_OK; +} + +static s32_t my_spiffs_write(u32_t addr, u32_t size, u8_t *src) { + //my_spi_write(addr, size, src); + printf("SPI write %d bytes at %x\n",size, addr); + return SPIFFS_OK; +} + +static s32_t my_spiffs_erase(u32_t addr, u32_t size) { + //my_spi_erase(addr, size); + printf("SPI erase %d blocks at %x\n",size, addr); + return SPIFFS_OK; +} + +#define LOG_PAGE_SIZE (256) + +void *my_spiffs_mount(int phys_size, + int phys_addr, + int phys_erase_block, + int log_page_size, + int log_block_size, + s32_t (read_cb)(u32_t addr, u32_t size, u8_t *dst), + s32_t (write_cb)(u32_t addr, u32_t size, u8_t *src), + s32_t (erase_cb)(u32_t addr, u32_t size) + ) { + + spiffs *fs = malloc(sizeof(spiffs)); + + uint8_t *spiffs_work_buf = malloc(log_page_size*2); +#define SPIFFS_FDS_SIZE (32*4) + uint8_t *spiffs_fds = malloc(SPIFFS_FDS_SIZE); +#define SPIFFS_CACHE_BUF_SIZE ((log_page_size+32)*4) + uint8_t *spiffs_cache_buf = malloc(SPIFFS_CACHE_BUF_SIZE); + + spiffs_config cfg; + cfg.phys_size = phys_size; // use all spi flash + cfg.phys_addr = phys_addr; // start spiffs at start of spi flash + cfg.phys_erase_block = phys_erase_block; // according to datasheet + cfg.log_block_size = log_block_size; // let us not complicate things + cfg.log_page_size = log_page_size; // as we said + + cfg.hal_read_f = read_cb; + cfg.hal_write_f = write_cb; + cfg.hal_erase_f = erase_cb; + + int res = SPIFFS_mount(fs, + &cfg, + spiffs_work_buf, + spiffs_fds, + SPIFFS_FDS_SIZE, + spiffs_cache_buf, + SPIFFS_CACHE_BUF_SIZE, + 0); + //printf("mount res: %i\n", res); + + return res?NULL:(void *)fs; +} + +int my_dir(spiffs *fs, + void (entry_cb)(char *name, int size, int id)) +{ + + spiffs_DIR d; + struct spiffs_dirent e; + struct spiffs_dirent *pe = &e; + + int ret; + + SPIFFS_clearerr(fs); + + if (NULL==SPIFFS_opendir(fs, "/", &d)) goto done; + + while ((pe = SPIFFS_readdir(&d, pe))) { + entry_cb(pe->name, pe->size, pe->obj_id); + } + + if (SPIFFS_closedir(&d)) goto done; + SPIFFS_clearerr(fs); + + done: + return SPIFFS_errno(fs); +} diff --git a/py/spiffs.py b/py/spiffs.py new file mode 100755 index 0000000..1a4d65b --- /dev/null +++ b/py/spiffs.py @@ -0,0 +1,283 @@ +#!/usr/bin/python + +import ctypes + +import os,sys,copy,math +this_path = os.path.dirname(os.path.realpath(__file__)) + +assert(0==os.system('cd "%s" && make spiffs_.so' % this_path)) + +spiffs_lib = ctypes.CDLL(os.path.join(this_path,'spiffs_.so')) + +SPIFFS_O_APPEND = SPIFFS_APPEND = (1<<0) +SPIFFS_O_TRUNC = SPIFFS_TRUNC = (1<<1) +SPIFFS_O_CREAT = SPIFFS_CREAT = (1<<2) +SPIFFS_O_RDONLY = SPIFFS_RDONLY = (1<<3) +SPIFFS_O_WRONLY = SPIFFS_WRONLY = (1<<4) +SPIFFS_O_RDWR = SPIFFS_RDWR = (SPIFFS_RDONLY | SPIFFS_WRONLY) +SPIFFS_O_DIRECT = SPIFFS_DIRECT = (1<<5) +SPIFFS_O_EXCL = SPIFFS_EXCL = (1<<6) + +SPIFFS_SEEK_SET = 0 +SPIFFS_SEEK_CUR = 1 +SPIFFS_SEEK_END = 2 + +class SpiffsException(Exception): pass + +class Spiffs(object): + def __init__(self, + phys_size, + phys_addr = 0, + phys_erase_block = 65536, + log_page_size = 256, + log_block_size = 65536): + + self.phys_size = phys_size + self.phys_addr = phys_addr + self.phys_erase_block = phys_erase_block + self.log_page_size = log_page_size + self.log_block_size = log_block_size + + self.mount() + + def mount(self): + + RWCallback = ctypes.CFUNCTYPE(ctypes.c_int32, + ctypes.c_uint32, #addr + ctypes.c_uint32, #size + ctypes.POINTER(ctypes.c_uint8), # data + ) + + EraseCallback = ctypes.CFUNCTYPE(ctypes.c_int32, + ctypes.c_uint32, #addr + ctypes.c_uint32, #size + ) + + # hold a reference, otherwise garbage-collection crash + self._rcb = RWCallback(self._on_read) + self._wcb = RWCallback(self._on_write) + self._ecb = EraseCallback(self._on_erase) + + self.fs = spiffs_lib.my_spiffs_mount(self.phys_size, + self.phys_addr, + self.phys_erase_block, + self.log_page_size, + self.log_block_size, + self._rcb, + self._wcb, + self._ecb) + + def _on_read(self, addr, size, dst): + "Extra layer of indirection to unwrap the ctypes stuff" + data = self.on_read(int(addr), int(size)) + for i in range(size): + dst[i] = ord(data[i]) + + return 0 + + def _on_write(self, addr, size, src): + data = [chr(src[i]) for i in range(size)] + self.on_write(addr, size, data) + return 0 + + def _on_erase(self, addr, size): + print "on_erase",args + self.on_erase(addr, size) + return 0 + + ############################################## + # Backend interface + + def on_read(self, addr, size): + "Subclass me!" + print "read",addr,size + return '\xff'*size + + def on_write(self, addr, size, data): + "Subclass me!" + print "on_write",addr,size + + def on_erase(self, addr, size): + "Subclass me!" + print "on_erase",addr,size + + ############################################## + # API wrap + + def fopen(self, filename, flags): + return spiffs_lib.SPIFFS_open(self.fs, filename, flags) + + def fwrite(self, fd, data): + res = spiffs_lib.SPIFFS_write(self.fs, fd, data, len(data)) + if res != len(data): raise SpiffsException(res) + + def fread(self, fd, count=1): + buf = (ctypes.c_uint8 * count)() + res = spiffs_lib.SPIFFS_read(self.fs, fd, buf, count) + if res<0: raise SpiffsException(res) + ret = [chr(c) for c in buf][:res] + return ret + + def fclose(self, fd): + res = spiffs_lib.SPIFFS_close(self.fs, fd) + if res<0: raise SpiffsException(res) + + def dir(self): + entries=[] + def collect_entry(name, size, dentry_id): + entries.append([str(name), int(size), int(dentry_id)]) + + EntryCallback = ctypes.CFUNCTYPE(None, + ctypes.c_char_p, + ctypes.c_uint32, #size + ctypes.c_uint32 #id) + ) + entry_cb = EntryCallback(collect_entry) + + res = spiffs_lib.my_dir(self.fs, entry_cb) + + if res: raise SpiffsException(res) + + return entries + + def fseek(self, fd, offset, whence = SPIFFS_SEEK_SET): + res = spiffs_lib.SPIFFS_lseek(self.fs, fd, offset, whence) + if res<0: raise SpiffsException(res) + return res + + def ftell(self, fd): + res = spiffs_lib.SPIFFS_tell(self.fs, fd) + if res<0: raise SpiffsException(res) + return res + + def remove(self, path): + res = spiffs_lib.SPIFFS_remove(self.fs, path) + if res<0: raise SpiffsException(res) + + ############################################## + # Pythonic API + + def open(self, filename, mode=None): + mode = mode or 'r' + + mode = ''.join(mode.split('b')) + + spiffs_mode = {'r':SPIFFS_RDONLY, + 'r+':SPIFFS_RDWR, + 'w':SPIFFS_WRONLY | SPIFFS_TRUNC | SPIFFS_CREAT, + 'w+':SPIFFS_RDWR | SPIFFS_TRUNC | SPIFFS_CREAT, + 'a':SPIFFS_WRONLY, + 'a+':SPIFFS_RDWR}[mode] + + fd = self.fopen(filename, spiffs_mode) + + if fd<0: raise SpiffsException(fd) + + if mode.startswith('a'): + self.fseek(fd, 0, SPIFFS_SEEK_END) + + return SpiffsFile(self, fd) + +class SpiffsFile(object): + "A Python file-like interface" + def __init__(self, parent, fd): + self.parent = parent + self.fd = fd + def __enter__(self): + return self + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + def read(self, count=-1): + if count < 1: + pos = self.tell() + size = self.seek(0, SPIFFS_SEEK_END) + self.seek(pos) + count = size-pos + return ''.join(self.parent.fread(self.fd, count)) + + def write(self, *args, **kwargs): + return self.parent.fwrite(self.fd, *args, **kwargs) + def close(self, *args, **kwargs): + return self.parent.fclose(self.fd, *args, **kwargs) + def seek(self, *args, **kwargs): + return self.parent.fseek(self.fd, *args, **kwargs) + def tell(self, *args, **kwargs): + return self.parent.ftell(self.fd, *args, **kwargs) + +class SpiffsCharsBack(Spiffs): + "list-of-chars as block device" + + def __init__(self, chars): + self.chars = chars + super(SpiffsFileBack, self).__init__(len(chars)) + + def on_read(self, addr, size): + return ''.join(self.chars[addr:size]) + + def on_write(self, addr, size, data): + was_data = self.chars[addr:size] + is_data = [] + + for was,new in zip(was_data,data): + now = ord(was) & ord(new) + is_data.append(chr(now)) + + self.chars[addr:addr+size] = is_data + + def on_erase(self, addr, size): + self.chars[addr:addr+size] = ['\xff'] * size + +class SpiffsFileBack(Spiffs): + def __init__(self, fd): + self.back_fd = fd + fd.seek(0,2) # to the end + size = fd.tell() + super(SpiffsFileBack, self).__init__(size) + + def on_read(self, addr, size): + self.back_fd.seek(addr) + return self.back_fd.read(size) + + def on_write(self, addr, size, data): + self.back_fd.seek(addr) + was_data = self.back_fd.read(size) + is_data = [] + + for was,new in zip(was_data,data): + now = ord(was) & ord(new) + is_data.append(chr(now)) + + self.back_fd.seek(addr) + self.back_fd.write(''.join(is_data)) + + def on_erase(self, addr, size): + self.back_fd.seek(addr) + self.back_fd.write('\xff'*size) + +if __name__=="__main__": + + file('/tmp/back.bin','w').write('\xff'*8*1024*1024) + + with file('/tmp/back.bin','r+wb') as bf: + s = SpiffsFileBack(bf) + print s.dir() + + fd = s.fopen("Hello", SPIFFS_CREAT | SPIFFS_WRONLY) + print fd + s.fwrite(fd, "Hello World") + s.fclose(fd) + + print s.dir() + + with s.open("Testfile","w") as tf: + for x in range(100): + print >> tf, "Test message",x, + + print s.dir() + print s.open("Testfile").read() + + print s.dir() + s.remove("Testfile") + + print s.dir()