From 967b5d787e894b5cba95aa13e3488277bd3a0c37 Mon Sep 17 00:00:00 2001 From: slaff Date: Sun, 4 Apr 2021 14:09:36 +0200 Subject: [PATCH 001/130] Use the release token from the release creator. (#2297) --- Tools/ci/secrets.sh.enc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Tools/ci/secrets.sh.enc b/Tools/ci/secrets.sh.enc index a2e569f02f..722208acd1 100644 --- a/Tools/ci/secrets.sh.enc +++ b/Tools/ci/secrets.sh.enc @@ -1,7 +1,7 @@ -U2FsdGVkX1/VhqomygkMFsRejvogd3R4GpBC1qAiF+f+drEyY81gn12gf1UnGiaa -ZbLMqFqaBXMUmDxE5Ix0MR8uwSa5Bz1EGFBnhSY17iuMbQVfcAZOXDVpOJFB368G -sctrjME3JSWuvUw66PjOyCdfkKoAlCpQsD1sNGR70nXEqK06IF0JDKPvyTrhJxCC -RTx+Oc83fiLECmUcFityDAAwXkPAvv7A2iRBkoqCUjPmShhZz5niVUi9HuSZwOH6 -EPo6NE2P7B5Emvqh5Mx9qniIvN74KmUFb6JPuUT+tKqNWIVfbmSy/PHRfyGNGJ8v -EH5eWX9SfmGBZwFS7EtV3v81GEWUO3dz8PJ/unzwUgvYOiHYgBIvUPgL9viDhhxF -YzbTuxNjlY2UFHz172ZjxRJv5la7MW7NMI4GrSBHyr4= +U2FsdGVkX1863QmyR/JzFa/MOe0pgdRBvWe0vyGYK6k/8RA8aTRsQR6fHai7JGGA +L2Xcxl05apX98EIKQ/Kvn+H3HLgaFJsIIzt3YPl3d/E/n04sJOm9kchvEG/R+b/L +KvRn1vMp4eiTswJhNiw7aoFIlMY3C1PpViee756HVkxmvkzFLG4zyAtgyZzi6MRI +pLc+UbOGJij/MCdUsS4UOUnsXJRbrDEfiPEfOGzC0GDnt7VKjQHYLc1dSMXEs3pZ +9yYjW/4D2c2p6mhDCfeugEBMOEbIPkPUvvrngKIeuREd817nRHY22Hp+VFUTg0WC +WuCYDBw8ffOLx21zQGIXhh2hPO1QHLAG/Bs8SQ83U9m3YQvCAhzqz5S4vHZkZs65 +OBlHEUzn9APjOObjQBKDSF60e5WMmcan0hYPkIIipUg= From c9c875288de1c18c091ce146963ef347c379cecb Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 9 Apr 2021 08:57:15 +0100 Subject: [PATCH 002/130] Storage, rBoot and profile editor updates (#2302) **Fixes** - Unexpectedly get 'no partitions have build targets' error when attempting to build partition targets - Fix `SpiFlash::getId()` declaration (override keyword missing) - Change partition sort order so devices are in same order as listed - Fix problems when partition table offset is changed. Must rebuild Storage library if partition table offset changes Add `PARTITION_TABLE_OFFSET` relink dependency to rboot Component - Change default partition alignment to 0x1000 (was 4) - Add config `bootloader_size` field As partition table no longer lives directly after bootloader, size must be specified explicitly. **Editor fixes** - Fix error in editor when file dialog cancelled - Fix mis-identification of core profiles in editor. Profiles within Library samples identified as core profile. Now only profiles in SMING_HOME or SMING_HOME/Arch/** are considered core. - Ensure option lists are presented in sorted order - Update view if 'add device' is cancelled **Editor improvements** - Allow floating point values with size multipliers (e.g. 1.5m), and allow addresses to be entered in this style - Ensure selected item is visible in tree view - Add `HWCONFIG_BUILDSPECS` Components (SPIFFS, IFS) define their own schema for the editor to use when completing 'build' fields. Add example to Basic_Storage sample for how to use custom project build specs. - Show selected field description in status bar - Add scale controls for address/size fields to reduce the need for manual calculations - Change address/size text color if range is 0 This emphasises that value can't be adjusted but still allows values to be overridden, for example to move partition to entirely different location. - Output partitions in address (map) order - Revise 'array' handling, keep 'enum' as per JsonSchema spec. which MUST be an array - Add browse button for string fields marked with 'filename' or 'dirname' format in schema - Move partitions.offset into config.partition_table_offset Matches defined location within JSON files --- .../Esp32/Components/sming-arch/component.mk | 7 +- Sming/Arch/Esp32/standard.hw | 1 + Sming/Arch/Esp8266/standard.hw | 1 + Sming/Arch/Host/standard.hw | 1 + Sming/Components/IFS | 2 +- Sming/Components/Storage/Tools/README.rst | 2 + .../Storage/Tools/hwconfig/common.py | 15 +- .../Storage/Tools/hwconfig/config.py | 70 +- .../Storage/Tools/hwconfig/editor.py | 724 +++++++++++++----- .../Storage/Tools/hwconfig/hwconfig.py | 7 +- .../Storage/Tools/hwconfig/partition.py | 74 +- Sming/Components/Storage/component.mk | 16 +- Sming/Components/Storage/schema.json | 71 +- .../Storage/src/include/Storage/SpiFlash.h | 2 +- Sming/Components/Storage/todo.rst | 25 + Sming/Components/rboot/component.mk | 2 + Sming/Components/spiffs/build.json | 17 + Sming/Components/spiffs/component.mk | 2 + samples/Basic_Storage/basic_storage.hw | 8 +- samples/Basic_Storage/build.jsonc | 28 + samples/Basic_Storage/component.mk | 11 + 21 files changed, 811 insertions(+), 275 deletions(-) create mode 100644 Sming/Components/Storage/todo.rst create mode 100644 Sming/Components/spiffs/build.json create mode 100644 samples/Basic_Storage/build.jsonc diff --git a/Sming/Arch/Esp32/Components/sming-arch/component.mk b/Sming/Arch/Esp32/Components/sming-arch/component.mk index d97a67fcac..b32d5fae47 100644 --- a/Sming/Arch/Esp32/Components/sming-arch/component.mk +++ b/Sming/Arch/Esp32/Components/sming-arch/component.mk @@ -36,6 +36,7 @@ COMPONENT_VARS := \ # ELF and BIN files -TARGET_OUT := $(BUILD_BASE)/$(APP_NAME).out -TARGET_BIN := $(FW_BASE)/$(APP_NAME).bin -TARGET_OUT_0 := $(TARGET_OUT) +DEBUG_VARS += TARGET_BIN +TARGET_OUT = $(BUILD_BASE)/$(APP_NAME).out +TARGET_BIN = $(FW_BASE)/$(APP_NAME).bin +TARGET_OUT_0 = $(TARGET_OUT) diff --git a/Sming/Arch/Esp32/standard.hw b/Sming/Arch/Esp32/standard.hw index 96fd083214..2083a769eb 100644 --- a/Sming/Arch/Esp32/standard.hw +++ b/Sming/Arch/Esp32/standard.hw @@ -2,6 +2,7 @@ "name": "Standard config with single ROM", "comment": "Should work with any Esp32 variant", "arch": "Esp32", + "bootloader_size": "0x8000", "partition_table_offset": "0x8000", "devices": { "spiFlash": { diff --git a/Sming/Arch/Esp8266/standard.hw b/Sming/Arch/Esp8266/standard.hw index 37d6bd7d68..88e49adf8f 100644 --- a/Sming/Arch/Esp8266/standard.hw +++ b/Sming/Arch/Esp8266/standard.hw @@ -2,6 +2,7 @@ "name": "Standard config with single ROM", "comment": "Should work with any Esp8266 variant", "arch": "Esp8266", + "bootloader_size": "0x2000", "partition_table_offset": "self.devices[0].size - 0x6000", "devices": { "spiFlash": { diff --git a/Sming/Arch/Host/standard.hw b/Sming/Arch/Host/standard.hw index 7572825a4c..287e348737 100644 --- a/Sming/Arch/Host/standard.hw +++ b/Sming/Arch/Host/standard.hw @@ -1,6 +1,7 @@ { "name": "Standard config with single ROM", "arch": "Host", + "bootloader_size": "0x2000", "partition_table_offset": "0x2000", "devices": { "spiFlash": { diff --git a/Sming/Components/IFS b/Sming/Components/IFS index b31da3aa3e..e5447fc2b2 160000 --- a/Sming/Components/IFS +++ b/Sming/Components/IFS @@ -1 +1 @@ -Subproject commit b31da3aa3ec5b786bbea1967ed31a8e6ff931676 +Subproject commit e5447fc2b226a557e9bf059b7eb39483c2706812 diff --git a/Sming/Components/Storage/Tools/README.rst b/Sming/Components/Storage/Tools/README.rst index 04d56eacff..56f6e4c901 100644 --- a/Sming/Components/Storage/Tools/README.rst +++ b/Sming/Components/Storage/Tools/README.rst @@ -4,3 +4,5 @@ Partition tools hwconfig.py Supports parsing and creation of hardware configuration files which include partition information. +editor.py + Graphical tool for managing devices and partitions diff --git a/Sming/Components/Storage/Tools/hwconfig/common.py b/Sming/Components/Storage/Tools/hwconfig/common.py index 89d09efa87..37ee625a89 100644 --- a/Sming/Components/Storage/Tools/hwconfig/common.py +++ b/Sming/Components/Storage/Tools/hwconfig/common.py @@ -3,6 +3,8 @@ # import sys, json, platform +from rjsmin import jsmin +from collections import OrderedDict quiet = False @@ -35,7 +37,7 @@ def parse_int(v, keywords=None): try: for letter, multiplier in [("k", 1024), ("m", 1024 * 1024), ("g", 1024 * 1024 * 1024)]: if v.lower().endswith(letter): - return parse_int(v[:-1], keywords) * multiplier + return round(float(v[:-1]) * multiplier) return int(v, 0) except ValueError: raise InputError("Invalid field value %s" % v) @@ -91,6 +93,17 @@ def contains_whitespace(s): return ''.join(s.split()) != s +def json_loads(s): + return json.loads(jsmin(s), object_pairs_hook=OrderedDict) + +def json_load(filename): + with open(filename) as f: + return json_loads(f.read()) + +def json_save(data, filename): + with open(filename, "w") as f: + json.dump(data, f, indent=4) + def to_json(obj): return json.dumps(obj, indent=4) diff --git a/Sming/Components/Storage/Tools/hwconfig/config.py b/Sming/Components/Storage/Tools/hwconfig/config.py index ff40d62ead..cb273d7eb3 100644 --- a/Sming/Components/Storage/Tools/hwconfig/config.py +++ b/Sming/Components/Storage/Tools/hwconfig/config.py @@ -3,7 +3,6 @@ # import os, partition, storage, copy -from rjsmin import jsmin from common import * from builtins import classmethod @@ -11,7 +10,7 @@ def get_config_dirs(): s = os.environ['HWCONFIG_DIRS'] - dirs = s.replace(' ', ' ').split(' ') + dirs = s.strip().replace(' ', ' ').split(' ') return dirs def load_option_library(): @@ -20,9 +19,18 @@ def load_option_library(): for d in dirs: filename = fixpath(d) + '/options.json' if os.path.exists(filename): - with open(filename) as f: - data = json.loads(jsmin(f.read())) - library.update(data) + data = json_load(filename) + library.update(data) + return library + +def load_build_library(): + library = {} + s = os.environ['HWCONFIG_BUILDSPECS'] + s = s.strip().replace(' ', ' ') + if len(s) != 0: + for f in s.split(' '): + data = json_load(fixpath(f)) + library.update(data) return library def get_config_list(): @@ -43,10 +51,38 @@ def find_config(name): return path raise InputError("Config '%s' not found" % name) + +class Schema(dict): + def __init__(self, filename): + self.schema = json_load(filename) + # Config + properties = self['Config']['properties'] + properties['base_config']['enum'] = list(get_config_list().keys()) + # Device + properties = self['Device']['properties'] + properties['type']['enum'] = list((storage.TYPES).keys()) + # Partition + properties = self['Partition']['properties'] + properties['type']['enum'] = list((partition.TYPES).keys() - ['storage', 'internal']) + properties['subtype']['enum'] = [] + # Add defined build targets and all available build fields + self.builders = load_build_library() + tgt = properties['build.target'] = self['Build']['properties']['target'] + tgt['enum'] = list(self.builders.keys()) + for builder in self.builders.values(): + for k, v in builder['properties'].items(): + properties['build.' + k] = v + + def __getitem__(self, name): + return self.schema['definitions'][name] + + +schema = Schema(os.environ['HWCONFIG_SCHEMA']) + class Config(object): def __init__(self): - self.partitions = partition.Table() self.devices = storage.List() + self.partitions = partition.Table(self.devices) self.depends = [] self.options = [] self.option_library = load_option_library() @@ -82,8 +118,7 @@ def load(self, name): """ filename = find_config(name) self.depends.append(filename) - with open(filename) as f: - data = json.loads(jsmin(f.read())) + data = json_load(filename) self.parse_dict(data) def parse_options(self, options): @@ -102,7 +137,7 @@ def parse_options(self, options): self.parse_dict(temp) def resolve_expressions(self): - self.partitions.offset = eval(str(self.partitions.offset)) + self.partition_table_offset = eval(str(self.partition_table_offset)) for p in self.partitions: p.resolve_expressions() @@ -121,8 +156,10 @@ def parse_dict(self, data): for k, v in data.items(): if k == 'arch': self.arch = v + elif k == 'bootloader_size': + self.bootloader_size = parse_int(v) elif k == 'partition_table_offset': - self.partitions.offset = v + self.partition_table_offset = v elif k == 'devices': self.devices.parse_dict(v) elif k != 'name' and k != 'comment': @@ -142,7 +179,8 @@ def dict(self): res['comment'] = self.comment res['arch'] = self.arch; res['options'] = self.options - res['partition_table_offset'] = self.partitions.offset_str() + res['bootloader_size'] = size_format(self.bootloader_size) + res['partition_table_offset'] = addr_format(self.partition_table_offset) res['devices'] = self.devices.dict() res['partitions'] = self.partitions.dict() return res @@ -154,7 +192,7 @@ def buildVars(self): dict = {} dict['SMING_ARCH_HW'] = self.arch - dict['PARTITION_TABLE_OFFSET'] = self.partitions.offset_str() + dict['PARTITION_TABLE_OFFSET'] = addr_format(self.partition_table_offset) dict['PARTITION_TABLE_LENGTH'] = "0x%04x" % partition.MAX_PARTITION_LENGTH dict['SPIFLASH_PARTITION_NAMES'] = " ".join(p.name for p in filter(lambda p: p.device == self.devices[0], self.partitions)) dict['HWCONFIG_DEPENDS'] = " ".join(self.depends) @@ -167,17 +205,19 @@ def buildVars(self): return res def verify(self, secure): + if self.partition_table_offset % partition.FLASH_SECTOR_SIZE != 0: + raise InputError("Partition table offset not aligned to flash sector") self.devices.verify() - self.partitions.verify(self.arch, self.devices[0], secure) + self.partitions.verify(self, secure) def map(self): - return partition.Map(self.partitions, self.devices) + return partition.Map(self) @classmethod def from_binary(cls, b): res = Config() res.name = 'from binary' res.arch = os.environ.get('SMING_ARCH', 'Unknown') - res.partitions.offset = 0 + res.partition_table_offset = 0 res.partitions.parse_binary(b, res.devices) return res diff --git a/Sming/Components/Storage/Tools/hwconfig/editor.py b/Sming/Components/Storage/Tools/hwconfig/editor.py index 3a66b3cfdf..c3de08203c 100644 --- a/Sming/Components/Storage/Tools/hwconfig/editor.py +++ b/Sming/Components/Storage/Tools/hwconfig/editor.py @@ -1,4 +1,4 @@ -import argparse, os, partition, configparser, string +import argparse, os, config, partition, configparser, string, threading from common import * from config import * import tkinter as tk @@ -7,6 +7,30 @@ app_name = 'Sming Hardware Profile Editor' +# Fields not listed in schema and therefore not part of JSON configuration, but present in GUI +virtual_fields = { + "name": { + "type": "string", + "title": "Name", + "description": "Name of object" + }, + "unused_before": { + "type": "string", + "title": "Space before", + "description": "Unused space before partition" + }, + "next": { + "type": "string", + "title": "Next", + "description": "Start of next partition" + }, + "unused_after": { + "type": "string", + "title": "Space after", + "description": "Unused space after partition" + } +} + def read_property(obj, name): """Read an object property, preferring string representation """ @@ -34,6 +58,47 @@ def load_config_vars(filename): parser.read_string(data) return parser['config'] +class ConfigVars(dict): + def __init__(self): + self.update(os.environ) + self.update(load_config_vars('config.mk')) + self.update(load_config_vars('debug.mk')) + # Can take a short while to resolve path variables, so use a background thread + # Don't need this information until user requests it + threading.Thread(target=self._resolvePathVars).start() + + def getRelativePath(self, path): + try: + res = os.path.relpath(path) + return res.replace('\\', '/') + except Exception: + return path + + def resolve_path(self, path): + tmp = str(path) + while True: + tmp = tmp.replace('(', '{') + tmp = tmp.replace(')', '}') + try: + new_path = string.Template(tmp).substitute(self) + except Exception as err: + # Return the value obtained thus far + return tmp + if new_path == tmp: + return new_path + tmp = new_path + + def _resolvePathVars(self): + self.pathVars = {} + for k, v in self.items(): + if v == '': + continue + path = self.resolve_path(v) + path = os.path.abspath(path) + if os.path.exists(path): + self.pathVars[k] = path + +configVars = ConfigVars() def checkProfilePath(filename): filename = os.path.realpath(filename) @@ -64,42 +129,192 @@ def resolve_id(config, id): dev = config.devices.find_by_name(id) if dev is not None: return dev - part = config.map().find_by_name(id) - if part is not None: - return part + return config.map().find_by_name(id) + +def resolve_device(config, id): + obj = resolve_id(config, id) + if obj is None or isinstance(obj, storage.Device): + return obj + return obj.device + +def resolve_key(obj, key): + """Resolve dotted key, e.g. 'build.target' refers to 'target' property of 'build' object + """ + keys = key.split('.') + while len(keys) > 1: + k = keys.pop(0) + if hasattr(obj, k): + obj = getattr(obj, k) + else: + obj = get_dict_value(obj, k, {}) + return obj, keys[0] -def json_loads(s): - return json.loads(jsmin(s), object_pairs_hook=OrderedDict) class Field: - """Manages widget and associated variable + """Manages widget(s) and associated variable """ - def __init__(self, var, widget): + def __init__(self, name, schema, var, widget): + self.name = name + self.schema = schema self.var = var self.widget = widget + self.label = tk.Label(widget.master, text=schema.get('title', name)) + self.scale = None + self.browse = None + + def grid(self, row): + self.label.grid(row=row, column=0, sticky=tk.W) + cs = 3 + if self.scale is not None: + self.scale.grid(row=row, column=2, columnspan=2, sticky=tk.E) + cs = 1 + if self.browse is not None: + self.browse.grid(row=row, column=3, sticky=tk.E) + cs = 2 + self.widget.grid(row=row, column=1, columnspan=cs, sticky=tk.EW) + + def addBrowse(self, isFile): + self.browse = ttk.Button(self.widget.master, text='...', width=3, + style='Browse.TButton', + command=lambda *args: self.selectPath(isFile)) + self.setPath(self.get_value()) + def update(e): + self.setPath(self.var.get()) + self.widget.focus() + self.widget.event_generate('') + self.widget.bind('', update) + self.widget.bind('', update) + + def selectPath(self, isFile): + if isFile: + path = filedialog.asksaveasfilename( + title="Select '%s'" % self.schema['title'], + initialdir=os.getcwd(), + confirmoverwrite=False) + else: + path = filedialog.askdirectory( + title="Select '%s'" % self.schema['title'], + initialdir=os.getcwd()) + if len(path) != 0: + self.setPath(path) + self.var.set('') + self.widget.focus() + self.widget.event_generate('') + + def setPath(self, path): + resolved_path = configVars.resolve_path(path) + if path == '' or '${' in path: + values = [] + else: + path = os.path.abspath(resolved_path) + values = {path} + for k, p in configVars.pathVars.items(): + if path == p or path.startswith(p + os.path.sep): + v = '$(' + k + ')' + path[len(p):] + v = v.replace('\\', '/') + values.add(v) + try: + path = os.path.relpath(path).replace('\\', '/') + values.add(path) + except Exception: + None + values = list(values) + values.sort(key=lambda v: len(v)) + self.widget.configure(values=values) + return path + + def addScale(self, min, max, on_change): + """Add scale controls for address/size fields + """ + scale = self.scale = tk.Scale( + self.widget.master, + orient = tk.HORIZONTAL, + from_ = min // self.align, + to = max // self.align, + bigincrement=16, + showvalue = False, + takefocus = True) + value = parse_int(self.get_value()) + scale.set(value // self.align) + def change(event): + value = parse_int(self.get_value()) + self.scale.set(value // self.align) + self.widget.bind('', change) + self.widget.bind('', change) + self.update_scale_range() + scale.configure(command=lambda val: on_change(int(val) * self.align)) + return scale + + def update_scale_range(self): + min = self.scale.cget('from') * self.align + max = self.scale.cget('to') * self.align + self.widget.configure(foreground='black' if max > min else 'gray') def get_value(self): return str(self.var.get()) - def is_disabled(self): + def set_value(self, value): + self.var.set(value) + if self.browse is not None: + self.setPath(value) + + def get_scale(self): + return self.scale.get() * self.align + + def set_scale(self, value): + self.scale.set(value // self.align) + + def set_scale_min(self, value): + self.scale.configure(from_ = value // self.align) + self.update_scale_range() + + def set_scale_max(self, value): + self.scale.configure(to = value // self.align) + self.update_scale_range() + + def enable(self, state): + state_str = 'normal' if state else 'disabled' + self.widget.configure(state=state_str) + for c in [self.scale, self.browse]: + if c is not None: + c.configure(state=state_str) + + def is_enabled(self): try: - return str(self.widget.cget('state')) == 'disabled' + return str(self.widget.cget('state')) != 'disabled' except Exception: - return False + return True + + def show(self, state = True): + if state: + self.widget.grid() + for c in [self.label, self.scale, self.browse]: + if c is not None: + c.grid() + else: + self.hide() + + def hide(self): + self.widget.grid_remove() + for c in [self.label, self.scale, self.browse]: + if c is not None: + c.grid_remove() + + def is_visible(self): + return len(self.widget.grid_info()) != 0 class EditState(dict): """Manage details of Config/Device/Partition editing using dictionary of Field objects """ - def __init__(self, editor, objectType, dictName, obj, enumDict): + def __init__(self, editor, objectType, dictName, obj): super().__init__(self) self.editor = editor self.objectType = objectType self.dictName = dictName - self.enumDict = enumDict self.obj = obj self.name = obj.name - self.schema = editor.schema[objectType] + self.schema = config.schema[objectType] self.allow_delete = False baseConfig = self.editor.getBaseConfig() optionBaseConfig = self.editor.getOptionBaseConfig() @@ -140,6 +355,7 @@ def init(self): label.pack() f = self.controlFrame = ttk.Frame(self.editor.editFrame) + f.grid_columnconfigure(2, weight=1) # 'scale' widget - see Field f.pack(side=tk.TOP) self.array = {} # dictionary for array element variables self.row = 0 @@ -147,9 +363,10 @@ def init(self): if not 'name' in keys: self.addControl('name') for k in keys: - if k != 'devices' and k != 'partitions': - self.addControl(k) - f = ttk.Frame(self.editor.editFrame) + self.addControl(k) + if self.objectType == 'Partition': + self.updateBuildTargets() + f = ttk.Frame(self.editor.editFrame, padding=(0, 8)) f.pack(side=tk.BOTTOM) btn = ttk.Button(f, text='Apply', command=lambda *args: self.apply()) btn.grid(row=0, column=0) @@ -158,14 +375,25 @@ def init(self): if self.allow_delete: btn = ttk.Button(f, text='Delete', command=lambda *args: self.delete()) btn.grid(row=0, column=2) + self.editor.sizeEdit() + def addControl(self, fieldName): + if self.objectType == 'Partition' and fieldName == 'address': + self.addControl('unused_before') + schema = self.get_property(fieldName) fieldType = schema.get('type') + if fieldType == 'object': + return + fieldFormat = schema.get('format') frame = self.controlFrame disabled = False if fieldName == 'name': value = self.name + elif '.' in fieldName: + o, k = resolve_key(self.obj, fieldName) + value = '' if o is None else o.get(k) else: value = self.obj_dict.get(fieldName, self.obj.dict().get(fieldName)) if fieldName == 'device': @@ -179,58 +407,175 @@ def addControl(self, fieldName): critical(str(err)) var = tk.StringVar(value=value) + values = schema.get('enum') if fieldType == 'boolean': - c = ttk.Checkbutton(frame, text=fieldName, variable=var) - else: - l = tk.Label(frame, text=fieldName) - l.grid(row=self.row, column=0, sticky=tk.W) - values = self.enumDict.get(fieldName, schema.get('enum')) - if values is None: - c = tk.Entry(frame, width=64, textvariable=var) - elif fieldType == 'array': - c = ttk.Frame(frame) - def array_changed(fieldName, key, var): - values = set(json_loads(var.get())) - if self.array[fieldName][key].get(): - values.add(key) - else: - values.discard(key) - var.set(json.dumps(list(values))) - elements = self.array[fieldName] = {} - for k, v in values.items(): - elements[k] = tk.BooleanVar(value=k in getattr(self.obj, fieldName)) - btn = tk.Checkbutton(c, text = k + ': ' + v, - command=lambda *args, fieldName=fieldName, key=k, var=var: array_changed(fieldName, key, var), - variable=elements[k]) - btn.grid(sticky=tk.W) - base = getattr(self.base_obj, fieldName) - if base is not None and k in base: - btn.configure(state='disabled') + c = ttk.Checkbutton(frame, variable=var) + elif fieldType == 'array': + c = ttk.Frame(frame, takefocus=False) + def array_changed(fieldName, key, var): + values = set(json_loads(var.get())) + if self.array[fieldName][key].get(): + values.add(key) + else: + values.discard(key) + var.set(json.dumps(list(values))) + elements = self.array[fieldName] = {} + if self.objectType == 'Config' and fieldName == 'options': + optionlib = load_option_library() + values = optionlib.keys() + details = {k: v['description'] for k, v in optionlib.items()} else: - c = ttk.Combobox(frame, values=values, textvariable=var) + values = schema['items']['enum'] + details = {} + for k in values: + elements[k] = tk.BooleanVar(value=k in getattr(self.obj, fieldName)) + btn = tk.Checkbutton(c, text=k + ': ' + details.get(k, '?'), + command=lambda *args, fieldName=fieldName, key=k, var=var: array_changed(fieldName, key, var), + variable=elements[k]) + btn.grid(sticky=tk.W) + base = getattr(self.base_obj, fieldName) + if base is not None and k in base: + btn.configure(state='disabled') + elif values is None: + if fieldFormat == 'filename' or fieldFormat == 'dirname': + c = ttk.Combobox(frame, textvariable=var) + else: + c = tk.Entry(frame, width=32, textvariable=var) + else: + values.sort() + c = ttk.Combobox(frame, values=values, textvariable=var) + if self.objectType == 'Partition': if fieldName == 'subtype': def set_subtype_values(): t = self['type'].get_value() - t = partition.TYPES[t] - values = partition.SUBTYPES.get(t, []) - c.configure(values=list(values)) + t = partition.TYPES.get(t) + subtypes = list(partition.SUBTYPES.get(t, [])) + subtypes.sort() + c.configure(values=subtypes) c.configure(postcommand=set_subtype_values) - self[fieldName] = Field(var, c) - - if fieldName == 'name': - # Name is read-only for inherited devices/partitions - if self.is_inherited: - disabled = self.is_inherited + if fieldName == 'type' or fieldName == 'subtype' or fieldName == 'build.target': + def update(e): + self.updateBuildTargets() + c.bind('<>', update) + c.bind('', update) + c.bind('', update) + + field = self[fieldName] = Field(fieldName, schema, var, c) + + if fieldFormat == 'filename': + field.addBrowse(True) + elif fieldFormat == 'dirname': + field.addBrowse(False) + + # Help text in status bar + def setStatus(f): + title = f.schema.get('title', f.name) + desc = f.schema.get('description', None) + self.editor.status.set("%s: %s" % (title, desc)) + c.bind('', lambda event, f=field: setStatus(f)) + c.bind('', lambda event: self.editor.status.set('')) + + # Manage address/size fields with scale controls and two additional virtual fields + if self.objectType == 'Partition': + part = self.obj + field.align = part.alignment(self.editor.config.arch) + min_address = part.address - part.unused_before + next_address = part.address + part.size + part.unused_after # max address +1 + max_size = next_address - min_address + if fieldName == 'address': + def change(address): + field.set_value(addr_format(address)) + f_size = self['size'] + f_size.set_scale_max(next_address - address) + self['unused_before'].set_scale(address - min_address) + self['next'].set_scale(address + f_size.get_scale()) + field.addScale(part.address - part.unused_before, part.address + part.unused_after, change) + elif fieldName == 'size': + def change(size): + field.set_value(size_format(size)) + f_address = self['address'] + f_address.set_scale_max(next_address - size) + address = parse_int(f_address.get_value()) + f_unused_before = self['unused_before'] + f_unused_before.set_scale_max(max_size - size) + f_next = self['next'] + f_next.set_scale_min(address + size - f_unused_before.get_scale()) + f_next.set_scale(address + size) + f_unused_after = self['unused_after'] + f_unused_after.set_scale_max(max_size - size) + f_unused_after.set_scale(next_address - address - size) + field.addScale(field.align, part.size + part.unused_after, change) + elif fieldName == 'unused_before': + value = size_format(part.unused_before) + field.set_value(value) + def change(unused_before): + field.set_value(size_format(unused_before)) + self['unused_after'].set_scale(max_size - self['size'].get_scale() - unused_before) + field.addScale(0, part.unused_before + part.unused_after, change) + elif fieldName == 'next': + value = addr_format(part.address + part.size) + field.set_value(value) + def change(next): + field.set_value(addr_format(next)) + size = self['size'].get_scale() + self['unused_before'].set_scale(part.unused_before + next - size - part.address) + field.addScale(part.address + part.size - part.unused_before, next_address, change) + elif fieldName == 'unused_after': + value = size_format(part.unused_after) + field.set_value(value) + def change(unused_after): + field.set_value(size_format(unused_after)) + size = self['size'].get_scale() + address = next_address - unused_after - size + self['address'].set_scale(address) + self['unused_before'].set_scale(part.unused_before + address - part.address) + field.addScale(0, part.unused_before + part.unused_after, change) + + # Name is read-only for inherited devices/partitions + if fieldName == 'name' and self.is_inherited: + disabled = True # Internal 'partitions' are generally not editable, but make an exception to allow # creation of new partitions (on an 'unused' type) or changing the partition table offset - if self.objectType == 'Partition' and self.is_inherited and self.obj.is_internal() and not self.obj.is_unused(): + elif self.objectType == 'Partition' and self.is_inherited and self.obj.is_internal() and not self.obj.is_unused(): if not (self.obj.is_internal(partition.INTERNAL_PARTITION_TABLE) and fieldName == 'address'): disabled = True if disabled: - c.configure(state='disabled') - c.grid(row=self.row, column=1, sticky=tk.EW) + field.enable(False) + else: + c.bind('', lambda event: field.set_value(value)) + field.grid(self.row) self.row += 1 - return c + + if self.objectType == 'Partition' and fieldName == 'size': + self.addControl('next') + self.addControl('unused_after') + + return field + + + def updateBuildTargets(self): + t = self['type'].get_value() + subtype = self['subtype'].get_value() + builders = {} + for n, v in config.schema.builders.items(): + if t == v['partition']['type'] and subtype == v['partition']['subtype']: + builders[n] = v + self['build.target'].widget.configure(values=list(builders.keys())) + + for k, v in self.items(): + if k.startswith('build.'): + v.hide() + if len(builders) == 0: + return + target = self['build.target'] + target.show() + builder = builders.get(target.get_value()) + if builder is None: + target.set_value('') + return + for k in builder['properties']: + self['build.' + k].show() + self.editor.sizeEdit() def apply(self): # Fetch base JSON for comparison @@ -249,13 +594,17 @@ def apply(self): base = {} if base is None else base.dict() new_name = None try: - for k, f in self.items(): - if f.is_disabled(): + for fieldName, field in self.items(): + if not field.is_enabled(): continue - value = f.get_value() - schema = self.get_property(k) + value = field.get_value() + schema = self.get_property(fieldName) fieldType = schema.get('type') - if k == 'name' and json_dict is not None: + o, k = resolve_key(obj, fieldName) + if not field.is_visible(): + if k in o: + del o[k] + elif fieldName == 'name' and json_dict is not None: value = value.strip() if value != self.name: if value in self.editor.config.map(): @@ -265,23 +614,30 @@ def apply(self): new_name = value # If renaming a device, then all partitions must be updated if self.objectType == 'Device': - for n, p in json_config.get('partitions', {}).items(): + for p in json_config.get('partitions', {}).values(): if p.get('device') == self.name: p['device'] = new_name - elif k == 'address' and self.objectType == 'Partition' and self.obj.is_internal(partition.INTERNAL_PARTITION_TABLE): + elif 'virtual' in field.schema: + continue + elif fieldName == 'address' and self.objectType == 'Partition' and self.obj.is_internal(partition.INTERNAL_PARTITION_TABLE): json_config['partition_table_offset'] = value - elif value == '' and k != 'filename': # TODO mark 'allow empty' values in schema somehow - if k in obj: - del obj[k] - elif fieldType == 'object' or fieldType == 'array': - obj[k] = {} if value == '' else json_loads(value) + elif value == '' and fieldName != 'filename': # TODO mark 'allow empty' values in schema somehow + if k in o: + del o[k] + elif fieldType == 'array': + o[k] = {} if value == '' else json_loads(value) elif fieldType == 'boolean': - obj[k] = (value != '0') + o[k] = (value != '0') elif value.isdigit() and 'integer' in fieldType: - obj[k] = int(value) + o[k] = int(value) else: - obj[k] = value - if k in base and obj.get(k) == base[k]: + o[k] = value + obj_base, key_base = resolve_key(base, fieldName) + if key_base in obj_base and o.get(k) == obj_base[key_base]: + del o[k] + + for k in list(obj.keys()): + if obj[k] == {} or obj[k] == []: del obj[k] if len(obj) == 0: @@ -289,14 +645,12 @@ def apply(self): if len(json_dict) == 0: del json_config[self.dictName] - - if self.editor.verify_config(json_config): - self.editor.set_json(json_config) - if new_name is not None: - self.name = new_name - if self.objectType != 'Config': - self.editor.selected = self.name - self.editor.reload() + self.editor.json = self.editor.verify_config(json_config) + if new_name is not None: + self.name = new_name + if self.objectType != 'Config': + self.editor.selected = self.name + self.editor.reload() except Exception as err: self.editor.user_error(err) raise err @@ -321,10 +675,12 @@ def delete(self): self.editor.reload() def get_property(self, name): - if name == 'name': - return {'type': 'text'} - else: - return self.schema['properties'][name] + """Get field property from schema""" + prop = self.schema['properties'].get(name, None) + if prop is None: + prop = virtual_fields[name] + prop['virtual'] = True + return prop def nameChanged(self): return self.name != self['name'].get_value() @@ -384,8 +740,7 @@ def __init__(self, parent, editor): super().__init__(parent) self.pack(fill=tk.BOTH) self.editor = editor - self.device = None - self.selected_id = None + self.selected = '' self.bytesPerPixel = 32 self.maxPartitionDrawSize = 0x4000 canvas = self.canvas = tk.Canvas(self, height=150, xscrollincrement=1) @@ -422,12 +777,13 @@ def update(self): self.clear() - if self.device is None: + self.selected = self.editor.selected + device = self.device = resolve_device(self.editor.config, self.selected) + if device is None: return - device = self.device canvas = self.canvas - partitions = list(filter(lambda p: p.device == self.device, self.editor.config.map())) + partitions = list(filter(lambda p: p.device == device, self.editor.config.map())) # Minimum width (in pixels) to draw a partition minPartitionWidth = M @@ -563,7 +919,7 @@ def createText(r, anchor, text, font): canvas.config(scrollregion=(0, 0, self.scroll_width, 0)) # Highlight the selected item (if any) - item = self.items.get(self.selected_id) + item = self.items.get(self.selected) if item is not None: self.canvas.itemconfigure(item, width=3) @@ -601,23 +957,24 @@ def onMouseWheel(self, event): if not (shift or control): self.canvas.xview_scroll(delta, tk.UNITS) - def set_device(self, dev): - if self.device != dev: - self.device = dev - self.update() - def select(self): id = self.editor.selected - if id == self.selected_id: + if id == self.selected: return - item = self.items.get(self.selected_id) - if item is not None: - self.canvas.itemconfigure(item, width=1) + dev = resolve_device(self.editor.config, id) + if dev != self.device: + self.selected = id + self.device = dev + self.update() + else: + item = self.items.get(self.selected) + if item is not None: + self.canvas.itemconfigure(item, width=1) item = self.items.get(id) if item is None: return self.canvas.itemconfigure(item, width=3) - self.selected_id = id + self.selected = id # Ensure item is visible pos = self.canvas.coords(item) id_x1 = pos[0] / self.scroll_width @@ -750,15 +1107,7 @@ def select(self): if tree.exists(id) and tree.focus() != id: tree.focus(id) tree.selection_set(id) - - -class Schema(dict): - def __init__(self, filename): - with open(filename) as f: - self.schema = json.load(f, object_pairs_hook=OrderedDict) - - def __getitem__(self, name): - return self.schema['definitions'][name] + tree.see(id) class Editor: @@ -767,14 +1116,12 @@ def __init__(self, root): self.main = root self.edit = None self.selected = '' - self.config_vars = load_config_vars('config.mk') - self.config_vars.update(load_config_vars('debug.mk')) self.initialise() def initialise(self): self.main.option_add('*tearOff', False) - - self.schema = Schema(os.environ['HWCONFIG_SCHEMA']) + s = ttk.Style() + s.configure('Browse.TButton', padding=0) hwFilter = [('Hardware Profiles', '*' + HW_EXT)] @@ -789,7 +1136,7 @@ def fileOpen(): title='Select profile ' + HW_EXT + ' file', filetypes=hwFilter, initialdir=os.getcwd()) - if filename != '' and checkProfilePath(filename): + if len(filename) != 0 and checkProfilePath(filename): self.loadConfig(filename) def fileSave(): @@ -799,29 +1146,29 @@ def fileSave(): filetypes=hwFilter, initialfile=filename, initialdir=os.getcwd()) - if filename != '' and checkProfilePath(filename): + if len(filename) != 0 and checkProfilePath(filename): ext = os.path.splitext(filename)[1] if ext != HW_EXT: filename += HW_EXT - with open(filename, "w") as f: - json.dump(self.json, f, indent=4) + json_save(self.json, filename) # Toolbar toolbar = ttk.Frame(self.main) toolbar.pack(side=tk.TOP, fill=tk.X) - btn = ttk.Button(toolbar, text="New", command=fileNew) - btn.grid(row=0, column=1) - btn = ttk.Button(toolbar, text="Open...", command=fileOpen) - btn.grid(row=0, column=2) - btn = ttk.Button(toolbar, text="Save...", command=fileSave) - btn.grid(row=0, column=3) + col = 0 + def addButton(text, command): + btn = ttk.Button(toolbar, text=text, command=command) + nonlocal col + btn.grid(row=0, column=col) + col += 1 + addButton('New', fileNew) + addButton('Open...', fileOpen) + addButton('Save...', fileSave) sep = ttk.Separator(toolbar, orient=tk.VERTICAL) - sep.grid(row=0, column=4, sticky=tk.NS) - btn = ttk.Button(toolbar, text='Edit Config', command=self.editConfig) - btn.grid(row=0, column=5) - btn = ttk.Button(toolbar, text='Add Device', command=self.addDevice) - btn.grid(row=0, column=6) - + sep.grid(row=0, column=col, sticky=tk.NS) + col += 1 + addButton('Edit Config', self.editConfig) + addButton('Add Device', self.addDevice) # Group main controls into areas which can be re-sized by the user pwin = ttk.PanedWindow(self.main, orient=tk.VERTICAL) @@ -849,7 +1196,6 @@ def fileSave(): canvas['yscrollcommand'] = s.set def configure(event): canvas.config(scrollregion=(0, 0, 0, self.editFrame.winfo_height())) - canvas.config(width = self.editFrame.winfo_width()) self.editFrame.bind('', configure) @@ -859,21 +1205,20 @@ def configure(event): def apply(*args): try: json_config = json_loads(self.jsonEditor.get('1.0', 'end')) - if self.verify_config(json_config): - self.set_json(json_config) - self.updateWindowTitle() - self.reload() + self.json = self.verify_config(json_config) + self.updateWindowTitle() + self.reload() except Exception as err: self.user_error(err) def undo(*args): self.jsonEditor.replace('1.0', 'end', to_json(self.json)) - f = ttk.Frame(frame) + f = ttk.Frame(frame, padding=(0, 8)) f.pack(anchor=tk.S, side=tk.BOTTOM) - btn = ttk.Button(f, text="Apply", command=apply) + btn = ttk.Button(f, text='Apply', command=apply) btn.grid(row=0, column=0) - btn = ttk.Button(f, text="Undo", command=undo) + btn = ttk.Button(f, text='Undo', command=undo) btn.grid(row=0, column=1) - self.jsonEditor = tk.Text(frame, height=14) + self.jsonEditor = tk.Text(frame, width=10, height=14) self.jsonEditor.pack(anchor=tk.N, side=tk.LEFT, expand=True, fill=tk.BOTH) s = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=self.jsonEditor.yview) s.pack(anchor=tk.NE, side=tk.RIGHT, fill=tk.Y) @@ -886,6 +1231,11 @@ def undo(*args): self.reset() + def sizeEdit(self): + self.main.update_idletasks() + w = self.editFrame.winfo_width() + self.editCanvas.config(width=w+8) + def user_error(self, err): self.status.set(err) messagebox.showerror(type(err).__name__, err) @@ -903,20 +1253,19 @@ def getOptionBaseConfig(self): def loadConfig(self, filename): self.reset() # If this is a core profile, don't edit it but create a new profile based on it - if filename.startswith(os.environ['SMING_HOME']): + dirname = os.path.dirname(filename) + smingHome = configVars['SMING_HOME'] + if dirname == smingHome or dirname.startswith(smingHome + '/Arch/'): config_name = os.path.splitext(os.path.basename(filename))[0] self.json['base_config'] = config_name else: - with open(filename) as f: - json_config = json_loads(f.read()) + self.json = json_load(filename) options = get_dict_value(self.json, 'options', []) - for opt in os.environ.get('HWCONFIG_OPTS', '').replace(' ', '').split(): + for opt in configVars.get('HWCONFIG_OPTS', '').replace(' ', '').split(): if not opt in options: options.append(opt) - self.set_json(json_config) - self.reload() self.updateWindowTitle() self.editDevice(self.config.devices[0]) @@ -930,39 +1279,38 @@ def updateWindowTitle(self): self.main.title(self.config.arch + ' ' + name + ' - ' + app_name) def verify_config(self, json_config): - try: - Config.from_json(json_config).verify(False) - return True - except Exception as err: - self.user_error(err) - return False - - def set_json(self, json_config): - # Keep output order consistent - self.json = {} - for k in self.schema['Config']['properties'].keys(): - if k in json_config: - self.json[k] = json_config[k] + """Raises an exception if any problems are found in the configuration. + On success, returns a consistently-ordered JSON configuration. + """ + cfg = Config.from_json(json_config) + cfg.verify(False) + res = OrderedDict() + for key in config.schema['Config']['properties'].keys(): + if key in json_config: + value = json_config[key] + if key == 'devices': + names = list(dev.name for dev in cfg.devices) + elif key == 'partitions': + names = list(p.name for p in cfg.map()) + else: + res[key] = value + continue + output = res[key] = OrderedDict() + for n in names: + if n in value: + output[n] = value[n] + return res def reset(self): self.tree.clear() self.map.clear() self.status.set('') - self.json = {"name": "New Profile"} + self.json = OrderedDict() + self.json['name'] = 'New Profile' self.json['base_config'] = 'standard' self.reload() self.updateWindowTitle() - def resolve_path(self, path): - tmp = str(path) - while True: - tmp = tmp.replace('(', '{') - tmp = tmp.replace(')', '}') - new_path = string.Template(tmp).substitute(self.config_vars) - if new_path == tmp: - return new_path - tmp = new_path - class Used: def __init__(self): self.text = '' @@ -973,7 +1321,7 @@ def get_used(self, part): used = self.Used() if part.filename != '': try: - used.path = self.resolve_path(part.filename) + used.path = configVars.resolve_path(part.filename) if os.path.exists(used.path): used.size = os.path.getsize(used.path) used.text = size_frac_str(used.size) @@ -984,9 +1332,6 @@ def get_used(self, part): return used def reload(self): - with open(find_config(self.json['base_config'])) as f: - self.json_base_config = json_loads(f.read()) - self.jsonEditor.replace('1.0', 'end', to_json(self.json)) try: config = Config.from_json(self.json) @@ -996,16 +1341,15 @@ def reload(self): self.status.set('') self.config = config + self.json_base_config = json_load(find_config(self.json['base_config'])) + if self.selected == '': + self.selected = get_id(self.config.devices[0]) self.map.update() self.tree.update() def select(self, obj): self.selected = get_id(obj) - if isinstance(obj, partition.Entry): - self.map.set_device(obj.device) - else: - self.map.set_device(obj) self.map.select() self.tree.select() @@ -1015,18 +1359,17 @@ def is_editing(self, obj): def editConfig(self): if self.is_editing(self.config): return - enumDict = {} - enumDict['base_config'] = list(get_config_list().keys()) - optionlib = load_option_library() - options = {} - for k, v in optionlib.items(): - options[k] = v['description'] - enumDict['options'] = options - self.edit = EditState(self, 'Config', None, self.config, enumDict) + # If we're editing a new device, for example, cancel it + id = self.tree.tree.focus() + if id != self.selected: + obj = resolve_id(self.config, id) + if obj is not None: + self.select(obj) + self.edit = EditState(self, 'Config', None, self.config) def addDevice(self): dev = storage.Device('New device') - self.editDevice(dev) + self.edit = EditState(self, 'Device', 'devices', dev) def editDevice(self, dev): if isinstance(dev, str): @@ -1034,9 +1377,7 @@ def editDevice(self, dev): if self.is_editing(dev): return self.select(dev) - enumDict = {} - enumDict['type'] = list((storage.TYPES).keys()) - self.edit = EditState(self, 'Device', 'devices', dev, enumDict) + self.edit = EditState(self, 'Device', 'devices', dev) def editPartition(self, part): if isinstance(part, str): @@ -1044,10 +1385,7 @@ def editPartition(self, part): if self.is_editing(part): return self.select(part) - enumDict = {} - enumDict['type'] = list((partition.TYPES).keys() - ['storage', 'internal']) - enumDict['subtype'] = [] - self.edit = EditState(self, 'Partition', 'partitions', part, enumDict) + self.edit = EditState(self, 'Partition', 'partitions', part) def main(): diff --git a/Sming/Components/Storage/Tools/hwconfig/hwconfig.py b/Sming/Components/Storage/Tools/hwconfig/hwconfig.py index 1aff6773af..5f195737a4 100644 --- a/Sming/Components/Storage/Tools/hwconfig/hwconfig.py +++ b/Sming/Components/Storage/Tools/hwconfig/hwconfig.py @@ -6,6 +6,7 @@ import common, argparse, os, partition from common import * from config import Config +from config import schema as config_schema def openOutput(path): if path == '-': @@ -24,10 +25,8 @@ def handle_validate(args, config, part): # Validate resulting hardware configuration against schema try: from jsonschema import Draft7Validator - inst = json.loads(config.to_json()) - with open(os.environ['HWCONFIG_SCHEMA']) as f: - schema = json.load(f) - v = Draft7Validator(schema) + inst = json_loads(config.to_json()) + v = Draft7Validator(config_schema) errors = sorted(v.iter_errors(inst), key=lambda e: e.path) if errors != []: for e in errors: diff --git a/Sming/Components/Storage/Tools/hwconfig/partition.py b/Sming/Components/Storage/Tools/hwconfig/partition.py index 7551823109..4ef65e07de 100644 --- a/Sming/Components/Storage/Tools/hwconfig/partition.py +++ b/Sming/Components/Storage/Tools/hwconfig/partition.py @@ -127,8 +127,9 @@ def parse_subtype(ptype, value): class Table(list): - def __init__(self): + def __init__(self, devices): super().__init__(self) + self.devices = devices def parse_dict(self, data, devices): partnames = [] @@ -143,11 +144,9 @@ def parse_dict(self, data, devices): part.parse_dict(entry, devices) def sort(self): - # Ensure spiFlash partitions are first in the list def get_key(p): - key = p.device.name + p.address_str() - if p.device.name == 'spiFlash': - key = ' ' + key + idx = self.devices.index(p.device) + key = "%02x%08x" % (idx, p.address) return key super().sort(key=get_key) @@ -168,9 +167,6 @@ def to_csv(self): % (p.device.name, p.address_str(), p.end_str(), p.size_str(), p.type_str(), p.subtype_str(), p.name, p.filename) return res - def offset_str(self): - return addr_format(self.offset) - def buildVars(self): dict = {} dict['PARTITION_NAMES'] = " ".join(p.name for p in self) @@ -230,19 +226,17 @@ def find_by_address(self, device, addr): return p return None - def verify(self, arch, spiFlash, secure): + def verify(self, config, secure): """Verify partition layout """ # verify each partition individually for p in self: - p.verify(arch, secure) - - if self.offset % FLASH_SECTOR_SIZE != 0: - raise InputError("Partition table offset not aligned to flash sector") + p.verify(config.arch, secure) - p = self.find_by_address(spiFlash, self.offset) + spiFlash = config.devices[0] + p = self.find_by_address(spiFlash, config.partition_table_offset) if p is None: - p = self.find_by_address(spiFlash, self.offset + PARTITION_TABLE_SIZE - 1) + p = self.find_by_address(spiFlash, config.partition_table_offset + PARTITION_TABLE_SIZE - 1) if not p is None: raise InputError("Partition table conflict with '%s'" % p.name) @@ -259,10 +253,10 @@ def verify(self, arch, spiFlash, secure): raise InputError("Partition names must be unique") # check for overlaps - if arch == 'Esp32': - minPartitionAddress = self.offset + PARTITION_TABLE_SIZE + if config.arch == 'Esp32': + minPartitionAddress = config.partition_table_offset + PARTITION_TABLE_SIZE else: - minPartitionAddress = 0x00002000 + minPartitionAddress = config.bootloader_size dev = None last = None for p in self: @@ -365,7 +359,9 @@ def __init__(self, device=None, name="", address=None, size=None, ptype=None, su self.encrypted = False self.filename = '' self.build = None - + # Set during map construction + self.unused_before = 0 + self.unused_after = 0 def parse_dict(self, data, devices): """Construct a partition object from JSON definition @@ -399,7 +395,10 @@ def parse_dict(self, data, devices): raise InputError("Error in partition entry '%s': %s" % (self.name, e)) def resolve_expressions(self): - self.address = eval(str(self.address)) + try: + self.address = eval(str(self.address)) + except Exception: + self.address = parse_int(self.address) def dict(self): res = {} @@ -486,7 +485,7 @@ def __str__(self): return "Part '%s' %s/%s @ 0x%x size 0x%x" % (self.name, self.type_str(), self.subtype_str(), self.address or -1, self.size or -1) def alignment(self, arch): - return ALIGNMENT[arch].get(self.type, 4) + return ALIGNMENT[arch].get(self.type, 0x1000) def verify(self, arch, secure): if self.type is None: @@ -560,7 +559,7 @@ def to_binary(self): class Map(Table): """Contiguous map of flash memory """ - def __init__(self, table, devices): + def __init__(self, config): def add(table, device, name, address, size, subtype): entry = Entry(device, name, address, size, INTERNAL_TYPE, subtype) table.append(entry) @@ -568,34 +567,41 @@ def add(table, device, name, address, size, subtype): def add_unused(table, device, address, last_end): if address > last_end + 1: - add(table, device, '(unused)', last_end + 1, address - last_end - 1, INTERNAL_UNUSED) + return add(table, device, '(unused)', last_end + 1, address - last_end - 1, INTERNAL_UNUSED) + return None - device = devices[0] + device = config.devices[0] # Take copy of source partitions and add internal ones to appear in the map - partitions = copy.copy(table) - if table.offset != 0: - add(partitions, device, 'Boot Sector', 0, min(table.offset, partitions[0].address), INTERNAL_BOOT_SECTOR) - add(partitions, device, 'Partition Table', table.offset, PARTITION_TABLE_SIZE, INTERNAL_PARTITION_TABLE) + partitions = copy.copy(config.partitions) + if config.partition_table_offset != 0: + add(partitions, device, 'Boot Sector', 0, config.bootloader_size, INTERNAL_BOOT_SECTOR) + add(partitions, device, 'Partition Table', config.partition_table_offset, PARTITION_TABLE_SIZE, INTERNAL_PARTITION_TABLE) # Devices with no defined partitions pdevs = set(p.device for p in partitions) - for dev in devices: + for dev in config.devices: if not dev in pdevs: add_unused(partitions, dev, dev.size, -1) partitions.sort() + unused = None last = None for p in partitions: if last is not None: + start = p.address if p.device == last.device else last.device.size + unused = add_unused(self, device, start, last.end()) + if unused is not None: + last.unused_after = unused.size if p.device != last.device: - add_unused(self, device, last.device.size, last.end()) device = p.device - add_unused(self, device, p.address, -1) - elif p.address > last.end() + 1: - add_unused(self, device, p.address, last.end()) + unused = add_unused(self, device, p.address, -1) self.append(p) + if unused is not None: + p.unused_before = unused.size last = p - add_unused(self, device, device.size, last.end()) + unused = add_unused(self, device, device.size, last.end()) + if unused is not None: + p.unused_after = unused.size diff --git a/Sming/Components/Storage/component.mk b/Sming/Components/Storage/component.mk index dd7680fb48..cc10fb3a10 100644 --- a/Sming/Components/Storage/component.mk +++ b/Sming/Components/Storage/component.mk @@ -2,6 +2,8 @@ COMPONENT_INCDIRS := src/include COMPONENT_SRCDIRS := src COMPONENT_DOXYGEN_INPUT := src/include +COMPONENT_RELINK_VARS := PARTITION_TABLE_OFFSET + CONFIG_VARS += HWCONFIG HWCONFIG_OPTS ifndef HWCONFIG override HWCONFIG := standard @@ -27,6 +29,9 @@ $(error Hardware configuration '$(HWCONFIG)' not found) endif endif +ifndef HWCONFIG_BUILDSPECS +HWCONFIG_BUILDSPECS := +endif PARTITION_PATH := $(COMPONENT_PATH) PARTITION_TOOLS := $(PARTITION_PATH)/Tools HWCONFIG_SCHEMA := $(PARTITION_PATH)/schema.json @@ -35,12 +40,13 @@ HWCONFIG_VARS := \ OUT_BASE \ HWCONFIG_DIRS \ HWCONFIG_OPTS \ + HWCONFIG_BUILDSPECS \ HWCONFIG_SCHEMA -HWCONFIG_EXPORTS := $(foreach v,$(HWCONFIG_VARS),$v="$($v)") +HWCONFIG_EXPORTS = $(foreach v,$(HWCONFIG_VARS),$v="$($v)") HWCONFIG_CMDLINE := $(PYTHON) $(PARTITION_TOOLS)/hwconfig -HWCONFIG_TOOL := $(HWCONFIG_EXPORTS) $(HWCONFIG_CMDLINE)/hwconfig.py +HWCONFIG_TOOL = $(HWCONFIG_EXPORTS) $(HWCONFIG_CMDLINE)/hwconfig.py -HWCONFIG_EDITOR := $(HWCONFIG_EXPORTS) $(HWCONFIG_CMDLINE)/editor.py $(HWCONFIG) +HWCONFIG_EDITOR = $(HWCONFIG_EXPORTS) $(HWCONFIG_CMDLINE)/editor.py $(HWCONFIG) # When using WSL without an X server available, use native Windows python ifdef WSL_ROOT @@ -48,7 +54,7 @@ ifndef DISPLAY space := space += WSLENV := $(WSLENV)$(subst $(space),,$(foreach v,$(HWCONFIG_VARS),::$v)) -HWCONFIG_EDITOR := $(HWCONFIG_EXPORTS) powershell.exe -Command "$(HWCONFIG_CMDLINE)/editor.py $(HWCONFIG)" +HWCONFIG_EDITOR = $(HWCONFIG_EXPORTS) powershell.exe -Command "$(HWCONFIG_CMDLINE)/editor.py $(HWCONFIG)" endif endif @@ -145,7 +151,7 @@ endef .PHONY: buildpart buildpart: ##Rebuild all partition targets - $(Q) if [ "$(PARTITION_BUILD_TARGETS)" -eq "" ]; then \ + $(Q) if [ -z "$(PARTITION_BUILD_TARGETS)" ]; then \ echo "No partitions have build targets"; \ else \ rm -f $(PARTITION_BUILD_TARGETS); \ diff --git a/Sming/Components/Storage/schema.json b/Sming/Components/Storage/schema.json index 5e05c9ff10..2a51d4d4fc 100644 --- a/Sming/Components/Storage/schema.json +++ b/Sming/Components/Storage/schema.json @@ -9,15 +9,19 @@ "additionalProperties": false, "properties": { "name": { - "type": "string" + "type": "string", + "title": "Name", + "description": "Configuration name, can be different from the filename" }, "comment": { - "type": "string" + "type": "string", + "title": "Comment", + "description": "Additional details or notes for this configuration" }, "arch": { "type": "string", "title": "Target architecture", - "description": "Defined *only* in the base 'standard' spec" + "description": "Normally inherited from the base 'standard' spec, but can be overridden" }, "base_config": { "type": "string", @@ -26,14 +30,27 @@ }, "options": { "type": "array", + "items": { + "type": "string", + "title": "Option name" + }, "title": "List of option fragments", "description": "Import additional settings from option libraries" }, + "bootloader_size": { + "type": [ + "string", + "integer" + ], + "title": "Bootloader size", + "description": "Size to reserve for bootloader" + }, "partition_table_offset": { "type": [ "string", "integer" ], + "title": "Partition table offset", "description": "Location of partition table in spiFlash. Simple address or python expression to be evaluated." }, "devices": { @@ -48,14 +65,15 @@ "required": [ "name", "arch", + "bootloader_size", "partition_table_offset", "devices", "partitions" ] }, "Devices": { - "title": "Devices", "type": "object", + "title": "Devices", "additionalProperties": false, "properties": { "spiFlash": { @@ -74,15 +92,17 @@ ] }, "Device": { - "title": "Storage device definition", "type": "object", + "title": "Storage device definition", "additionalProperties": false, "properties": { "size": { - "type": "string" + "type": "string", + "description": "Size of device (decimal, hex or append K, M, G)" }, "type": { - "type": "string" + "type": "string", + "description": "Type of storage device" }, "mode": { "type": "string", @@ -91,10 +111,12 @@ "qout", "dio", "dout" - ] + ], + "description": "Flash access mode (spiFlash only)" }, "speed": { - "type": "integer" + "type": "integer", + "description": "Clock speed in MHz (spiFlash only)" } }, "required": [ @@ -103,8 +125,8 @@ ] }, "Partitions": { - "title": "Partitions", "type": "object", + "title": "Partitions", "additionalProperties": false, "patternProperties": { "^[A-Za-z_][A-Za-z0-9_]*$": { @@ -114,12 +136,13 @@ } }, "Partition": { - "title": "Partition definition", "type": "object", + "title": "Partition definition", "additionalProperties": false, "properties": { "device": { "type": "string", + "title": "Device Name", "description": "ID of device this partition relates to" }, "address": { @@ -127,34 +150,47 @@ "string", "integer" ], + "title": "Address", "description": "Starting address for partition. Simple address or python expression to be evaluated." }, "size": { "type": [ "string", "integer" - ] + ], + "title": "Size", + "description": "Size of partition (decimal, hex or append K, M, G)" }, "type": { "type": [ "string", "integer" - ] + ], + "title": "Type", + "description": "Partition type" }, "subtype": { "type": [ "string", "integer" - ] + ], + "title": "Sub-type", + "description": "Partition sub-type (interpretation depends on selected type)" }, "readonly": { - "type": "boolean" + "type": "boolean", + "title": "Read-only", + "description": "Whether partition is to be considered read-only in applications" }, "encrypted": { - "type": "boolean" + "type": "boolean", + "title": "Encrypted", + "description": "ESP32 partitions may be encrypted" }, "filename": { "type": "string", + "format": "filename", + "title": "Filename", "description": "Location of file to write to this partition" }, "build": { @@ -171,13 +207,14 @@ ] }, "Build": { + "type": "object", "title": "Build specification", "description": "Additional properties as required by build target", - "type": "object", "additionalProperties": true, "properties": { "target": { "type": "string", + "title": "Target", "description": "Makefile target for this build" } }, diff --git a/Sming/Components/Storage/src/include/Storage/SpiFlash.h b/Sming/Components/Storage/src/include/Storage/SpiFlash.h index c6ac39119a..bed0050cb4 100644 --- a/Sming/Components/Storage/src/include/Storage/SpiFlash.h +++ b/Sming/Components/Storage/src/include/Storage/SpiFlash.h @@ -30,7 +30,7 @@ class SpiFlash : public Device return Type::flash; } - uint32_t getId() const; + uint32_t getId() const override; bool read(uint32_t address, void* dst, size_t size) override; bool write(uint32_t address, const void* src, size_t size) override; diff --git a/Sming/Components/Storage/todo.rst b/Sming/Components/Storage/todo.rst new file mode 100644 index 0000000000..f9546195b4 --- /dev/null +++ b/Sming/Components/Storage/todo.rst @@ -0,0 +1,25 @@ +TODO +==== + +Access filesystem builders via plugins + e.g. add 'build' button to partition edit dialog + Instead of building via the makefile (from where we've been called), + this should be done via plugins. + Plugins are defined in schema (in the 'spiffs' and 'IFS' modules). + +Add flash support to editor + e.g. add 'read', 'write', 'erase' buttons to partition edit dialog. + Can also add these to device which will do the same for all contained partitions. + For example, select one or more partitions and click 'read', 'write', etc. + +Enable multi-select? + Allow selecting multiple partitions for read/write. + +Support reading/writing external storage devices + Add fields to .hw which define how this is done. + This could be done with build targets or shell commands. + +Filesystem introspection + For SPIFFS, partitions are typically shown as FULL rather than actual filesystem usage. + Plugins can be used to allow more useful information to be shown, + and allow direct browsing (and editing?) of filesystem content. diff --git a/Sming/Components/rboot/component.mk b/Sming/Components/rboot/component.mk index b5ab0f3771..1e67a632b4 100644 --- a/Sming/Components/rboot/component.mk +++ b/Sming/Components/rboot/component.mk @@ -4,12 +4,14 @@ ifeq ($(SMING_ARCH),Esp8266) COMPONENT_DEPENDS := esp8266 else RBOOT_EMULATION := 1 +COMPONENT_RELINK_VARS := PARTITION_TABLE_OFFSET endif COMPONENT_SUBMODULES := rboot COMPONENT_INCDIRS := rboot rboot/appcode include COMPONENT_SRCDIRS := src src/Arch/$(SMING_ARCH) +DEBUG_VARS += RBOOT_DIR RBOOT_DIR := $(COMPONENT_PATH) ifndef RBOOT_EMULATION diff --git a/Sming/Components/spiffs/build.json b/Sming/Components/spiffs/build.json new file mode 100644 index 0000000000..03cd16b6a0 --- /dev/null +++ b/Sming/Components/spiffs/build.json @@ -0,0 +1,17 @@ +{ + "spiffsgen": { + "title": "SPIFFS filesystem support", + "partition": { + "type": "data", + "subtype": "spiffs" + }, + "properties": { + "files": { + "type": "string", + "format": "dirname", + "title": "Path to files", + "description": "Source directory containing filesystem files" + } + } + } +} \ No newline at end of file diff --git a/Sming/Components/spiffs/component.mk b/Sming/Components/spiffs/component.mk index 68954b98b2..fb0ce44d3c 100644 --- a/Sming/Components/spiffs/component.mk +++ b/Sming/Components/spiffs/component.mk @@ -33,6 +33,8 @@ COMPONENT_CFLAGS += -DSPIFFS_OBJ_META_LEN=$(SPIFFS_OBJ_META_LEN) SPIFFSGEN := $(PYTHON) $(COMPONENT_PATH)/spiffsgen.py SPIFFSGEN_SMING = $(SPIFFSGEN) --meta-len=$(SPIFFS_OBJ_META_LEN) --block-size=8192 +HWCONFIG_BUILDSPECS += $(COMPONENT_PATH)/build.json + # Target invoked via partition table ifneq (,$(filter spiffsgen,$(MAKECMDGOALS))) PART_TARGET := $(PARTITION_$(PART)_FILENAME) diff --git a/samples/Basic_Storage/basic_storage.hw b/samples/Basic_Storage/basic_storage.hw index 44caa91626..4d28b7fa28 100644 --- a/samples/Basic_Storage/basic_storage.hw +++ b/samples/Basic_Storage/basic_storage.hw @@ -27,7 +27,13 @@ "address": "0x1F4000", "size": "16K", "type": "user", - "subtype": 1 + "subtype": 1, + "filename": "out/user1.bin", + "build": { + "target": "user-build", + "parameter1": "value1", + "parameter2": "value2" + } }, // Override default SPIFFS partition with new address and source content "spiffs0": { diff --git a/samples/Basic_Storage/build.jsonc b/samples/Basic_Storage/build.jsonc new file mode 100644 index 0000000000..e8fb337f61 --- /dev/null +++ b/samples/Basic_Storage/build.jsonc @@ -0,0 +1,28 @@ +{ + "user-build": { + "title": "SPIFFS filesystem support", + // Which partition type/subtype to match for this spec. + "partition": { + "type": "user", + "subtype": "1" + }, + // Schema properties to show in editor + "properties": { + "parameter1": { + "type": "string", + "title": "Parameter 1", + "description": "First parameter" + }, + "parameter2": { + "type": "string", + "title": "Parameter 2", + "description": "Second parameter, an enumerated type", + "enum": [ + "value1", + "value2", + "value3" + ] + } + } + } +} \ No newline at end of file diff --git a/samples/Basic_Storage/component.mk b/samples/Basic_Storage/component.mk index 9bb23018f7..cfe43013d0 100644 --- a/samples/Basic_Storage/component.mk +++ b/samples/Basic_Storage/component.mk @@ -2,3 +2,14 @@ HWCONFIG := basic_storage HOST_NETWORK_OPTIONS := --nonet + +# Use direct assignment like this if within project file as Storage component not yet parsed +# Within a Component, use += +HWCONFIG_BUILDSPECS := $(PROJECT_DIR)/build.jsonc + +# Target invoked via partition table +.PHONY: user-build +user-build: + @echo "This is a user build: $(call HwExpr,('X=%s, Y=%s' % (part.build['parameter1'], part.build['parameter2'])))" + @echo "Filename: $(PARTITION_$(PART)_FILENAME)" + @echo "$(call HwExpr,json_save(part.build, '$(PARTITION_$(PART)_FILENAME)'))" From f1836a4b22b7c44818dd9f0eb28fff09488cc974 Mon Sep 17 00:00:00 2001 From: slaff Date: Fri, 9 Apr 2021 09:58:26 +0200 Subject: [PATCH 003/130] Added the lines needed to have usable debugging session also when a UART is enabled. (#2301) See: https://github.com/SmingHub/Sming/issues/1970 for details. --- Sming/Arch/Host/Components/gdbstub/README.rst | 5 +++++ Sming/Arch/Host/Components/gdbstub/gdbinit | 1 + 2 files changed, 6 insertions(+) create mode 100644 Sming/Arch/Host/Components/gdbstub/gdbinit diff --git a/Sming/Arch/Host/Components/gdbstub/README.rst b/Sming/Arch/Host/Components/gdbstub/README.rst index 5728592b2d..e0414fc28c 100644 --- a/Sming/Arch/Host/Components/gdbstub/README.rst +++ b/Sming/Arch/Host/Components/gdbstub/README.rst @@ -3,3 +3,8 @@ GDB Stub for Host This defines the command line to use when ``make gdb`` is run. No additional code is required to debug for the Host. +If you want to debug your application while having a separate UART then make sure to send the following commands to your debugger:: + + handle SIGUSR1 nostop noprint + +This component provides also ``gdbinit`` file containing the optimal settings needed for debugging. diff --git a/Sming/Arch/Host/Components/gdbstub/gdbinit b/Sming/Arch/Host/Components/gdbstub/gdbinit new file mode 100644 index 0000000000..f74fcb3a81 --- /dev/null +++ b/Sming/Arch/Host/Components/gdbstub/gdbinit @@ -0,0 +1 @@ +handle SIGUSR1 nostop noprint From 5321a3ddc0e2eb032ee321578f4f00426871fda7 Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 11 Apr 2021 11:24:50 +0100 Subject: [PATCH 004/130] Add LittleFS support (#2304) This PR adds an IFS implementation for [LittleFS](https://github.com/littlefs-project/littlefs). The `Basic_IFS` sample has been updated to demonstrate use of littlefs volumes. Initial image files can be created using the `lfs-build` are defined using an FWFS build configuration file, which is then copied into the target littlefs image file using an `fscopy` tool. **TODO** These items are not critical to filesystem operation and can be added in future updates. The following two methods are not fully implemented - they require modifications to the littlefs library. ``` FileHandle IFileSystem::fopen(const Stat& stat, OpenFlags flags); int IFileSystem::fremove(FileHandle file); ``` Note: The `fopen()` call is an optimisation to avoid the need to re-parse the filesystem for a file which has already been discovered during a directory enumeration. The `Stat::id` field is intended to provide a way for the implementation to do this, but for littlefs it will probably need the filename as well. Also, there are a few internal optimisations which can be made to avoid the need to store filenames and paths separately. --- .gitmodules | 4 + .../Components/libc/src/oldlib/README.rst | 7 + .../Components/libc/src/oldlib/strcspn.c | 48 +++++++ .../Components/libc/src/oldlib/strspn.c | 52 ++++++++ Sming/Components/IFS | 2 +- .../Storage/Tools/hwconfig/partition.py | 1 + .../Components/Storage/src/include/Storage.h | 11 ++ .../Storage/src/include/Storage/Partition.h | 3 +- Sming/Core/FileSystem.cpp | 30 ++--- Sming/Core/FileSystem.h | 5 + Sming/Libraries/LittleFS | 1 + samples/Basic_IFS/app/application.cpp | 126 +++++++++++++++--- samples/Basic_IFS/basic_ifs.hw | 20 ++- samples/Basic_IFS/component.mk | 2 + samples/Basic_IFS/resource/listing.json | 1 + 15 files changed, 267 insertions(+), 46 deletions(-) create mode 100644 Sming/Arch/Esp8266/Components/libc/src/oldlib/README.rst create mode 100644 Sming/Arch/Esp8266/Components/libc/src/oldlib/strcspn.c create mode 100644 Sming/Arch/Esp8266/Components/libc/src/oldlib/strspn.c create mode 160000 Sming/Libraries/LittleFS diff --git a/.gitmodules b/.gitmodules index 85003018ee..b611b9d729 100644 --- a/.gitmodules +++ b/.gitmodules @@ -219,6 +219,10 @@ path = Sming/Libraries/libsodium/libsodium url = https://github.com/jedisct1/libsodium.git ignore = dirty +[submodule "Libraries.LittleFS"] + path = Sming/Libraries/LittleFS + url = https://github.com/mikee47/Sming-LittleFS + ignore = dirty [submodule "Libraries.MDNS"] path = Sming/Libraries/MDNS url = https://github.com/mikee47/Sming-MDNS diff --git a/Sming/Arch/Esp8266/Components/libc/src/oldlib/README.rst b/Sming/Arch/Esp8266/Components/libc/src/oldlib/README.rst new file mode 100644 index 0000000000..ee596e987e --- /dev/null +++ b/Sming/Arch/Esp8266/Components/libc/src/oldlib/README.rst @@ -0,0 +1,7 @@ +This directory contains the following source code from sourceware's newlib +at https://sourceware.org/git/?p=newlib-cygwin.git;a=tree;f=newlib/libc/string + +strcspn.c +strspn.c + +License LGPL3 applies. diff --git a/Sming/Arch/Esp8266/Components/libc/src/oldlib/strcspn.c b/Sming/Arch/Esp8266/Components/libc/src/oldlib/strcspn.c new file mode 100644 index 0000000000..abaa93ad67 --- /dev/null +++ b/Sming/Arch/Esp8266/Components/libc/src/oldlib/strcspn.c @@ -0,0 +1,48 @@ +/* +FUNCTION + <>---count characters not in string + +INDEX + strcspn + +SYNOPSIS + size_t strcspn(const char *<[s1]>, const char *<[s2]>); + +DESCRIPTION + This function computes the length of the initial part of + the string pointed to by <[s1]> which consists entirely of + characters <[NOT]> from the string pointed to by <[s2]> + (excluding the terminating null character). + +RETURNS + <> returns the length of the substring found. + +PORTABILITY +<> is ANSI C. + +<> requires no supporting OS subroutines. + */ + +#include + +size_t +strcspn (const char *s1, + const char *s2) +{ + const char *s = s1; + const char *c; + + while (*s1) + { + for (c = s2; *c; c++) + { + if (*s1 == *c) + break; + } + if (*c) + break; + s1++; + } + + return s1 - s; +} diff --git a/Sming/Arch/Esp8266/Components/libc/src/oldlib/strspn.c b/Sming/Arch/Esp8266/Components/libc/src/oldlib/strspn.c new file mode 100644 index 0000000000..baf2399478 --- /dev/null +++ b/Sming/Arch/Esp8266/Components/libc/src/oldlib/strspn.c @@ -0,0 +1,52 @@ +/* +FUNCTION + <>---find initial match + +INDEX + strspn + +SYNOPSIS + #include + size_t strspn(const char *<[s1]>, const char *<[s2]>); + +DESCRIPTION + This function computes the length of the initial segment of + the string pointed to by <[s1]> which consists entirely of + characters from the string pointed to by <[s2]> (excluding the + terminating null character). + +RETURNS + <> returns the length of the segment found. + +PORTABILITY +<> is ANSI C. + +<> requires no supporting OS subroutines. + +QUICKREF + strspn ansi pure +*/ + +#include + +size_t +strspn (const char *s1, + const char *s2) +{ + const char *s = s1; + const char *c; + + while (*s1) + { + for (c = s2; *c; c++) + { + if (*s1 == *c) + break; + } + if (*c == '\0') + break; + s1++; + } + + return s1 - s; +} diff --git a/Sming/Components/IFS b/Sming/Components/IFS index e5447fc2b2..1e9b06ea93 160000 --- a/Sming/Components/IFS +++ b/Sming/Components/IFS @@ -1 +1 @@ -Subproject commit e5447fc2b226a557e9bf059b7eb39483c2706812 +Subproject commit 1e9b06ea933b2d8d52626b1b024e599f036800a7 diff --git a/Sming/Components/Storage/Tools/hwconfig/partition.py b/Sming/Components/Storage/Tools/hwconfig/partition.py index 4ef65e07de..bd275e430a 100644 --- a/Sming/Components/Storage/Tools/hwconfig/partition.py +++ b/Sming/Components/Storage/Tools/hwconfig/partition.py @@ -99,6 +99,7 @@ "fat": 0x81, "spiffs": 0x82, "fwfs": 0xf1, + "littlefs": 0xf2, }, STORAGE_TYPE: storage.TYPES, INTERNAL_TYPE: { diff --git a/Sming/Components/Storage/src/include/Storage.h b/Sming/Components/Storage/src/include/Storage.h index 81977f4d85..a43fa8a24f 100644 --- a/Sming/Components/Storage/src/include/Storage.h +++ b/Sming/Components/Storage/src/include/Storage.h @@ -59,4 +59,15 @@ template Iterator findPartition(T subType) return Iterator(Partition::Type(T::partitionType), uint8_t(subType)); } +template Storage::Partition findDefaultPartition(T subType) +{ + auto part = *Storage::findPartition(subType); + if(part) { + debug_i("[%s] Found '%s'", part.typeString().c_str(), part.name().c_str()); + } else { + debug_e("[%s] No partition found", toString(subType).c_str()); + } + return part; +} + } // namespace Storage diff --git a/Sming/Components/Storage/src/include/Storage/Partition.h b/Sming/Components/Storage/src/include/Storage/Partition.h index 9b5badeee1..bc9ddc69bf 100644 --- a/Sming/Components/Storage/src/include/Storage/Partition.h +++ b/Sming/Components/Storage/src/include/Storage/Partition.h @@ -63,7 +63,8 @@ XX(espHttpd, 0x80, "ESPHTTPD") \ XX(fat, 0x81, "FAT") \ XX(spiffs, 0x82, "SPIFFS") \ - XX(fwfs, 0xF1, "FWFS") + XX(fwfs, 0xF1, "FWFS") \ + XX(littlefs, 0xF2, "LittleFS") namespace Storage { diff --git a/Sming/Core/FileSystem.cpp b/Sming/Core/FileSystem.cpp index 9d239cdaab..4a65bed66c 100644 --- a/Sming/Core/FileSystem.cpp +++ b/Sming/Core/FileSystem.cpp @@ -25,20 +25,7 @@ void fileSetFileSystem(IFS::IFileSystem* fileSystem) } } -namespace -{ -template Storage::Partition findDefaultPartition(T subType) -{ - auto part = *Storage::findPartition(subType); - if(part) { - debug_i("[%s] Found '%s'", part.typeString().c_str(), part.name().c_str()); - } else { - debug_e("[%s] No partition found", toString(subType).c_str()); - } - return part; -} - -bool mount(IFS::IFileSystem* fs) +bool fileMountFileSystem(IFS::IFileSystem* fs) { if(fs == nullptr) { debug_e("Failed to created filesystem object"); @@ -58,43 +45,42 @@ bool mount(IFS::IFileSystem* fs) debug_i("File system initialised"); return true; } -} // namespace bool spiffs_mount() { - auto part = findDefaultPartition(Storage::Partition::SubType::Data::spiffs); + auto part = Storage::findDefaultPartition(Storage::Partition::SubType::Data::spiffs); return part ? spiffs_mount(part) : false; } bool spiffs_mount(Storage::Partition partition) { auto fs = IFS::createSpiffsFilesystem(partition); - return mount(fs); + return fileMountFileSystem(fs); } bool fwfs_mount() { - auto part = findDefaultPartition(Storage::Partition::SubType::Data::fwfs); + auto part = Storage::findDefaultPartition(Storage::Partition::SubType::Data::fwfs); return part ? fwfs_mount(part) : false; } bool fwfs_mount(Storage::Partition partition) { auto fs = IFS::createFirmwareFilesystem(partition); - return mount(fs); + return fileMountFileSystem(fs); } bool hyfs_mount() { - auto fwfsPart = findDefaultPartition(Storage::Partition::SubType::Data::fwfs); - auto spiffsPart = findDefaultPartition(Storage::Partition::SubType::Data::spiffs); + auto fwfsPart = Storage::findDefaultPartition(Storage::Partition::SubType::Data::fwfs); + auto spiffsPart = Storage::findDefaultPartition(Storage::Partition::SubType::Data::spiffs); return (fwfsPart && spiffsPart) ? hyfs_mount(fwfsPart, spiffsPart) : false; } bool hyfs_mount(Storage::Partition fwfsPartition, Storage::Partition spiffsPartition) { auto fs = IFS::createHybridFilesystem(fwfsPartition, spiffsPartition); - return mount(fs); + return fileMountFileSystem(fs); } Vector fileList() diff --git a/Sming/Core/FileSystem.h b/Sming/Core/FileSystem.h index 750ea338e7..49638193ff 100644 --- a/Sming/Core/FileSystem.h +++ b/Sming/Core/FileSystem.h @@ -117,6 +117,11 @@ inline void fileFreeFileSystem() fileSetFileSystem(nullptr); } +/** + * @brief Mount a constructed filesystem with debug messages + */ +bool fileMountFileSystem(IFS::IFileSystem* fs); + /** * @brief Mount the first available SPIFFS volume * @retval bool true on success diff --git a/Sming/Libraries/LittleFS b/Sming/Libraries/LittleFS new file mode 160000 index 0000000000..f55d97190d --- /dev/null +++ b/Sming/Libraries/LittleFS @@ -0,0 +1 @@ +Subproject commit f55d97190da949617526b57fdd4dc369c4128217 diff --git a/samples/Basic_IFS/app/application.cpp b/samples/Basic_IFS/app/application.cpp index 346681e76c..d250b2eb25 100644 --- a/samples/Basic_IFS/app/application.cpp +++ b/samples/Basic_IFS/app/application.cpp @@ -9,6 +9,7 @@ #include #include #include +#include // If you want, you can define WiFi settings globally in Eclipse Environment Variables #ifndef WIFI_SSID @@ -79,6 +80,9 @@ void onFile(HttpRequest& request, HttpResponse& response) } else { // response.setCache(86400, true); // It's important to use cache for better performance. auto stream = new FileStream(stat); + if(!stream->isValid()) { + stream->open(file); + } if(stat.compression.type == IFS::Compression::Type::GZip) { response.headers[HTTP_HEADER_CONTENT_ENCODING] = F("gzip"); } else if(stat.compression.type != IFS::Compression::Type::None) { @@ -109,7 +113,7 @@ bool initFileSystem() { fileFreeFileSystem(); -#if DEBUG_VERBOSE_LEVEL >= INFO +#if DEBUG_BUILD auto freeheap = system_get_free_heap_size(); #endif debug_i("1: heap = %u", freeheap); @@ -118,23 +122,13 @@ bool initFileSystem() // Create a partition wrapping some flashstring data auto part = Storage::progMem.createPartition(F("fwfsMem"), fwfsImage, Storage::Partition::SubType::Data::fwfs); #else - auto part = *Storage::findPartition(Storage::Partition::SubType::Data::fwfs); - if(part) { - debug_i("Found '%s'", part.name().c_str()); - } else { - debug_e("No FWFS partition found"); - } + auto part = Storage::findDefaultPartition(Storage::Partition::SubType::Data::fwfs); #endif IFS::IFileSystem* fs; #ifdef FWFS_HYBRID // Create a read/write filesystem - auto spiffsPart = *Storage::findPartition(Storage::Partition::SubType::Data::spiffs); - if(spiffsPart) { - debug_i("Found '%s'", spiffsPart.name().c_str()); - } else { - debug_e("No SPIFFS partition found"); - } + auto spiffsPart = Storage::findDefaultPartition(Storage::Partition::SubType::Data::spiffs); fs = IFS::createHybridFilesystem(part, spiffsPart); #else // Read-only @@ -199,6 +193,101 @@ void printDirectory(const char* path) printStream(tmpl); } } + +void copySomeFiles() +{ + auto part = *Storage::findPartition(Storage::Partition::SubType::Data::fwfs); + if(!part) { + return; + } + auto fs = IFS::createFirmwareFilesystem(part); + if(fs == nullptr) { + return; + } + fs->mount(); + + IFS::Directory dir(fs); + if(!dir.open()) { + return; + } + + while(dir.next()) { + auto& stat = dir.stat(); + if(stat.isDir()) { + continue; + } + IFS::File src(fs); + auto filename = stat.name.c_str(); + if(src.open(filename)) { + File dst; + if(dst.open(filename, File::CreateNewAlways | File::WriteOnly)) { + auto len = + src.readContent([&dst](const char* buffer, size_t size) -> int { return dst.write(buffer, size); }); + (void)len; + debug_w("Wrote '%s', %d bytes", filename, len); + if(!dst.settime(stat.mtime)) { + Serial.print(F("settime() failed: ")); + Serial.println(dst.getLastErrorString()); + } + if(!dst.setcompression(stat.compression)) { + Serial.print(F("setcompression() failed: ")); + Serial.println(dst.getLastErrorString()); + } + if(!dst.setacl(stat.acl)) { + Serial.print(F("setacl() failed: ")); + Serial.println(dst.getLastErrorString()); + } + } else { + debug_w("%s", dst.getLastErrorString().c_str()); + } + } + } +} + +bool isVolumeEmpty() +{ + Directory dir; + dir.open(); + return !dir.next(); +} + +void fstest() +{ + // Various ways to initialise a filesystem + + /* + * Mount regular SPIFFS volume + */ + // spiffs_mount(); + + /* + * Mount LittleFS volume + */ + // lfs_mount(); + + /* + * Mount default Firmware Filesystem + */ + // fwfs_mount(); + + /* + * Mount default FWFS/SPIFFS as hybrid + */ + // hyfs_mount(); + + /* + * Explore some alternative methods of mounting filesystems + */ + initFileSystem(); + + if(isVolumeEmpty()) { + Serial.print(F("Volume appears to be empty, writing some files...\r\n")); + copySomeFiles(); + } + + printDirectory(nullptr); +} + } // namespace void init() @@ -211,13 +300,10 @@ void init() "Hello\n"); #endif - // Various ways to initialise a filesystem: we'll use a custom approach - // spiffs_mount(); - // fwfs_mount(); - // hyfs_mount(); - initFileSystem(); - - printDirectory(nullptr); + // Delay at startup so terminal gets time to start + auto timer = new AutoDeleteTimer; + timer->initializeMs<1000>(fstest); + timer->startOnce(); WifiStation.enable(true); WifiStation.config(WIFI_SSID, WIFI_PWD); diff --git a/samples/Basic_IFS/basic_ifs.hw b/samples/Basic_IFS/basic_ifs.hw index ee67b4c91f..8ffa9bbf94 100644 --- a/samples/Basic_IFS/basic_ifs.hw +++ b/samples/Basic_IFS/basic_ifs.hw @@ -2,9 +2,25 @@ "name": "Basic IFS sample", "base_config": "spiffs", "partitions": { + "lfs1": { + "address": "0x00100000", + "size": "2060K", + "type": "data", + "subtype": "littlefs", + "readonly": false, + "encrypted": false, + "filename": "$(FW_BASE)/lfs1.bin", + "build": { + "target": "lfs-build", + "config": "fsimage.fwfs" + } + }, + "spiffs0": { + "address": "0x00303000" + }, "fwfs1": { - "address": "0x280000", - "size": "0x60000", + "address": "0x00383000", + "size": "476K", "type": "data", "subtype": "fwfs", "filename": "out/fwfs1.bin", diff --git a/samples/Basic_IFS/component.mk b/samples/Basic_IFS/component.mk index 81f4a0ac1d..0b43df10ed 100644 --- a/samples/Basic_IFS/component.mk +++ b/samples/Basic_IFS/component.mk @@ -1,3 +1,5 @@ +COMPONENT_DEPENDS := LittleFS + # Empty SPIFFS partition please SPIFF_FILES := diff --git a/samples/Basic_IFS/resource/listing.json b/samples/Basic_IFS/resource/listing.json index 9c15c47968..262a56e2b8 100644 --- a/samples/Basic_IFS/resource/listing.json +++ b/samples/Basic_IFS/resource/listing.json @@ -7,6 +7,7 @@ {SECTION}{{!ifgt:$record:0}}, {{!endif}} { "record": {{!as_int:$record}}, + "id": "{{file_id}}", "name": "{{name}}", "modified": "{{modified}}", "size": {{!as_int:size}}, From 665542014f2c93a50e2d546f5f322aedc629e05f Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 18 Apr 2021 09:48:22 +0100 Subject: [PATCH 005/130] Fix windows path handling in editor (#2306) --- Sming/Components/Storage/Tools/hwconfig/editor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sming/Components/Storage/Tools/hwconfig/editor.py b/Sming/Components/Storage/Tools/hwconfig/editor.py index c3de08203c..76ccead9d5 100644 --- a/Sming/Components/Storage/Tools/hwconfig/editor.py +++ b/Sming/Components/Storage/Tools/hwconfig/editor.py @@ -1254,7 +1254,7 @@ def loadConfig(self, filename): self.reset() # If this is a core profile, don't edit it but create a new profile based on it dirname = os.path.dirname(filename) - smingHome = configVars['SMING_HOME'] + smingHome = fixpath(configVars['SMING_HOME']) if dirname == smingHome or dirname.startswith(smingHome + '/Arch/'): config_name = os.path.splitext(os.path.basename(filename))[0] self.json['base_config'] = config_name From fce0793353c2ad3c28ffa88299409149fcc5abb9 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 19 Apr 2021 08:15:17 +0100 Subject: [PATCH 006/130] Fix base64 encode length calculation to account for padding (#2307) * Fix base64 encode length calculation Must take padding into account * Make `base64_min_encode_len` and `base64_min_decode_len` functions available publicly. * Add tests to verify that expected output buffer sizes are not exceeded --- Sming/Core/Network/WebHelpers/base64.cpp | 21 ++++++++----- Sming/Core/Network/WebHelpers/base64.h | 19 ++++++++++++ tests/HostTests/modules/Base64.cpp | 38 ++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 7 deletions(-) diff --git a/Sming/Core/Network/WebHelpers/base64.cpp b/Sming/Core/Network/WebHelpers/base64.cpp index 2cf3ab9fdc..a64760f813 100644 --- a/Sming/Core/Network/WebHelpers/base64.cpp +++ b/Sming/Core/Network/WebHelpers/base64.cpp @@ -14,14 +14,21 @@ #include "libb64/cencode.h" #include "libb64/cdecode.h" -// Base-64 encodes 6 bits into one character (4 output chars for every 3 input bytes) -#define MIN_ENCODE_LEN(_in_len) (((_in_len)*4 + 2) / 3) -#define MIN_DECODE_LEN(_in_len) (((_in_len)*3 + 3) / 4) +size_t base64_min_encode_len(size_t in_len) +{ + int groups = 1 + ((int(in_len) - 1) / 3); + return groups * 4; +} + +size_t base64_min_decode_len(size_t in_len) +{ + return ((in_len * 3) + 3) / 4; +} int base64_encode(size_t in_len, const unsigned char* in, size_t out_len, char* out) { // Base-64 encoding produces 3 output bytes for every 2 input bytes - unsigned min_out_len = MIN_ENCODE_LEN(in_len); + unsigned min_out_len = base64_min_encode_len(in_len); if(out_len < min_out_len) { return -1; } @@ -36,7 +43,7 @@ int base64_encode(size_t in_len, const unsigned char* in, size_t out_len, char* String base64_encode(const unsigned char* in, size_t in_len) { String s; - if(!s.setLength(MIN_ENCODE_LEN(in_len))) { + if(!s.setLength(base64_min_encode_len(in_len))) { return nullptr; } @@ -52,7 +59,7 @@ String base64_encode(const unsigned char* in, size_t in_len) /* decode a base64 string in one shot */ int base64_decode(size_t in_len, const char* in, size_t out_len, unsigned char* out) { - if(out_len < MIN_DECODE_LEN(in_len)) { + if(out_len < base64_min_decode_len(in_len)) { return -1; } @@ -64,7 +71,7 @@ int base64_decode(size_t in_len, const char* in, size_t out_len, unsigned char* String base64_decode(const char* in, size_t in_len) { String s; - if(!s.setLength(MIN_DECODE_LEN(in_len))) { + if(!s.setLength(base64_min_decode_len(in_len))) { return nullptr; } diff --git a/Sming/Core/Network/WebHelpers/base64.h b/Sming/Core/Network/WebHelpers/base64.h index a0a46568d0..9c43b3d6ec 100644 --- a/Sming/Core/Network/WebHelpers/base64.h +++ b/Sming/Core/Network/WebHelpers/base64.h @@ -15,6 +15,25 @@ #include "WString.h" +/** + * @brief Get minimum output buffer size required to encode message of given length. + * @param in_len Length of input message in bytes + * @retval size_t Number of bytes required in output buffer + * + * Base-64 encodes 6 bits into one character (4 output chars for every 3 input bytes). + * However, it also requires padding such that the output size is a multiple of 4 characters. + */ +size_t base64_min_encode_len(size_t in_len); + +/** + * @brief Get minimum output buffer size required to decode message of given length. + * @param in_len Length of base64-encoded input message in characters + * @retval size_t Number of characters required in output buffer + * + * For decoding, do not assume that input is padded. + */ +size_t base64_min_decode_len(size_t in_len); + /** @brief encode binary data into base64 digits with MIME style === pads * @param in_len quantity of characters to encode * @param in data to encode diff --git a/tests/HostTests/modules/Base64.cpp b/tests/HostTests/modules/Base64.cpp index fcb1cf629e..916899b38e 100644 --- a/tests/HostTests/modules/Base64.cpp +++ b/tests/HostTests/modules/Base64.cpp @@ -30,6 +30,44 @@ class Base64Test : public TestGroup debug_hex(INFO, "decode output", clear.c_str(), clear.length() + 1); REQUIRE(clear == token); } + + TEST_CASE("Encode lengths") + { + // Verify that actual encoded size is no larger than estimated size + constexpr size_t inputSize{1024}; + constexpr size_t outputSize{2048}; + auto inbuf = new uint8_t[inputSize]; + auto outbuf = new char[outputSize]; + os_get_random(inbuf, inputSize); + for(unsigned i = 0; i < inputSize; ++i) { + auto minEncodeLen = base64_min_encode_len(i); + int len = base64_encode(i, inbuf, outputSize, outbuf); + CHECK(len >= int(i)); + CHECK(size_t(len) <= minEncodeLen); + } + delete[] outbuf; + delete[] inbuf; + } + + TEST_CASE("Decode lengths") + { + // Verify that actual decoded size is no larger than estimated size + constexpr size_t inputSize{2048}; + constexpr size_t outputSize{1020}; + auto inbuf = new char[inputSize]; + auto outbuf = new uint8_t[outputSize]; + os_get_random(reinterpret_cast(outbuf), outputSize); + size_t maxLen = base64_encode(outputSize, outbuf, inputSize, inbuf); + CHECK(maxLen < inputSize); + for(unsigned i = 0; i < maxLen; ++i) { + auto minDecodeLen = base64_min_decode_len(i); + int len = base64_decode(i, inbuf, outputSize, outbuf); + CHECK(len <= int(i)); + CHECK(size_t(len) <= minDecodeLen); + } + delete[] outbuf; + delete[] inbuf; + } } }; From eaafc21b1c8e248d7b10cf8e489a892bf72b64df Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 20 Apr 2021 07:09:51 +0100 Subject: [PATCH 007/130] Little FS improvements and IFS revisions to support user metadata (#2308) **Main changes** This PR adds the following methods to `IFileSystem`: * setProfiler This allows applications to hook into the low-level read/write/erase calls for debugging and profiling. * fsetxattr * fgetxattr * setxattr * getxattr The `setacl`, `settime`, `setattr` and `setcompression` methods have been moved to `FileSystem` and call the above new methods to do their work. Additional overloads have been provided in FileSystem (and via methods of File objects): * setAttribute * getAttribute These work with file handles or paths and have various convenient overloads. * setUserAttribute, getUserAttribute These are to access user-defined attributes. For LFS volumes, each item is limited to a maximum size of 1023 bytes. SPIFFS is less flexible (by design). Metadata is stored in the object headers (after filename, etc.) and is restricted by the setting of `SPIFFS_OBJ_META_LEN`. The first 16 bytes are used for system attributes (e.g. modified time), with user attributes occupying any remaining space. The current default setting (16) is unchanged, so if user metadata for SPIFFS volumes is required then this must be increased accordingly. Each user attribute requires a 2-byte header (tag + size). * fopen * fopendir Removed - see below. **Fixes** - Revise HYFS to track file path SPIFFS getFilePath() call sometimes returns weird results. - Fix SPIFFS FileSystem::getFilePath return code - Fix FileDir weirdness The generic `DirHandle` type points to an `IFS::FileDir` struct, which each filesystem defines internally as required. It looks like the compiler sometimes mixes these up and causes problems, though this has only been seen when compiling in debug mode for Host. So instead, redefine `DirHandle` as an abstract type, move the `FileDir` definition inside the appropriate filesystem namespace and add the `GET_FILEDIR()` macro to deal with checking and casting. - Fix memory leak in test module (thanks valgrind) - Set lasterror in `File::readContent()` - Fix `FileSystem::makedirs()` - Fix LFS reported volumesize **Improvements** - Increase LFS_CACHE_SIZE from 16 to 32 bytes Results in signficant reduction in read counts - Switch to littlefs fork, manage file attributes manually Only changed attributes (and those with non-default values) get written out File time only gets updated if file is changed, opening in write mode is insufficient - LFS mkdir sets mtime on success - LFS `mkdir` succeeds if directory already exists - Add LFS inspect sample - Implement Host `flush`, `rename` and `remove` methods - Helpers return FileSystem* instead of IFileSystem* for easier use - Add optional `IFileSystem::setProfiler` method to enable flexible debugging and performance evaluation - Revise hybrid filesystem to allow use of alternative writeable filesystem (e.g. Little FS) - Remove `IFileSystem.fopendir(Stat&)` and `IFileSystem.fopen(Stat&)` The goal of these methods was to provide more efficient means to open files/directories discovered during enumeration. However, they are difficult to implement effectively and the additional complexity is hard to justify. POSIX defines a whole raft of such methods such as `openat`, `mkdirat`, etc. which add an additional `dirfd` parameter to specific the working directory. See https://man7.org/linux/man-pages/man3/open.3p.html for some background. This may be re-visited in the future if the need arises. - Add generic get/set attribute methods These support LFS file/directory attributes as well as standard attribute types via `AttributeTag`. There is no POSIX specification for this but the linux convention (setxattr, etc.) are appropriate. --- Sming/Components/IFS | 2 +- Sming/Components/spiffs/spiffs.patch | 13 ++- Sming/Core/Data/Stream/FileStream.h | 4 +- Sming/Core/Data/Stream/IFS/FileStream.cpp | 49 ++------- Sming/Core/Data/Stream/IFS/FileStream.h | 20 +++- Sming/Core/FileSystem.cpp | 7 +- Sming/Core/FileSystem.h | 116 ++++++++++------------ Sming/Core/Network/Ftp/FtpDataRetrieve.h | 2 +- Sming/Core/Network/Ftp/FtpDataStore.h | 2 +- Sming/Core/Network/FtpServer.h | 6 +- Sming/Core/Network/Http/HttpResponse.cpp | 36 +++---- Sming/Core/Network/Http/HttpResponse.h | 6 -- Sming/Libraries/LittleFS | 2 +- samples/Basic_IFS/app/application.cpp | 30 ++---- samples/Basic_rBoot/app/application.cpp | 5 +- tests/HostTests/modules/Files.cpp | 2 +- 16 files changed, 131 insertions(+), 171 deletions(-) diff --git a/Sming/Components/IFS b/Sming/Components/IFS index 1e9b06ea93..0886783d41 160000 --- a/Sming/Components/IFS +++ b/Sming/Components/IFS @@ -1 +1 @@ -Subproject commit 1e9b06ea933b2d8d52626b1b024e599f036800a7 +Subproject commit 0886783d41b38e716948a10bcd13a4f2069a9c31 diff --git a/Sming/Components/spiffs/spiffs.patch b/Sming/Components/spiffs/spiffs.patch index c1b882a600..a1f816129c 100644 --- a/Sming/Components/spiffs/spiffs.patch +++ b/Sming/Components/spiffs/spiffs.patch @@ -71,10 +71,17 @@ index 235aaaa..4df4b4e 100644 spiffs_page_object_ix_header objix_hdr; diff --git a/src/spiffs_nucleus.c b/src/spiffs_nucleus.c -index f811d93..ff9db29 100644 +index f811d93..781c52f 100644 --- a/src/spiffs_nucleus.c +++ b/src/spiffs_nucleus.c -@@ -945,6 +945,7 @@ s32_t spiffs_object_create( +@@ -939,12 +939,14 @@ s32_t spiffs_object_create( + fs->stats_p_allocated++; + + // write empty object index page ++ memset(&oix_hdr, 0xff, sizeof(oix_hdr)); + oix_hdr.p_hdr.obj_id = obj_id; + oix_hdr.p_hdr.span_ix = 0; + oix_hdr.p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED); oix_hdr.type = type; oix_hdr.size = SPIFFS_UNDEFINED_LEN; // keep ones so we can update later without wasting this page strncpy((char*)oix_hdr.name, (const char*)name, SPIFFS_OBJ_NAME_LEN); @@ -82,7 +89,7 @@ index f811d93..ff9db29 100644 #if SPIFFS_OBJ_META_LEN if (meta) { _SPIFFS_MEMCPY(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN); -@@ -1008,6 +1009,7 @@ s32_t spiffs_object_update_index_hdr( +@@ -1008,6 +1010,7 @@ s32_t spiffs_object_update_index_hdr( // change name if (name) { strncpy((char*)objix_hdr->name, (const char*)name, SPIFFS_OBJ_NAME_LEN); diff --git a/Sming/Core/Data/Stream/FileStream.h b/Sming/Core/Data/Stream/FileStream.h index d391a2aa42..f42aaaac6d 100644 --- a/Sming/Core/Data/Stream/FileStream.h +++ b/Sming/Core/Data/Stream/FileStream.h @@ -33,9 +33,9 @@ class FileStream : public IFS::FileStream open(fileName, openFlags); } - FileStream(const FileStat& stat, FileOpenFlags openFlags = File::ReadOnly) : FileStream() + FileStream(DirHandle dir, const String& name, FileOpenFlags openFlags = File::ReadOnly) : FileStream() { - open(stat, openFlags); + open(dir, name, openFlags); } using IFS::FileStream::attach; diff --git a/Sming/Core/Data/Stream/IFS/FileStream.cpp b/Sming/Core/Data/Stream/IFS/FileStream.cpp index e491353c86..66eac3245e 100644 --- a/Sming/Core/Data/Stream/IFS/FileStream.cpp +++ b/Sming/Core/Data/Stream/IFS/FileStream.cpp @@ -21,10 +21,8 @@ void FileStream::attach(FileHandle file, size_t size) return; } - auto fs = getFileSystem(); - if(fs == nullptr) { - return; - } + GET_FS() + handle = file; this->size = size; fs->lseek(handle, 0, SeekOrigin::Start); @@ -33,30 +31,9 @@ void FileStream::attach(FileHandle file, size_t size) debug_d("attached file: '%s' (%u bytes) #0x%08X", fileName().c_str(), size, this); } -bool FileStream::open(const Stat& stat, OpenFlags openFlags) -{ - auto fs = getFileSystem(); - if(fs == nullptr) { - return false; - } - - lastError = FS_OK; - - FileHandle file = fs->fopen(stat, openFlags); - if(!check(file)) { - return false; - } - - attach(file, stat.size); - return true; -} - bool FileStream::open(const String& fileName, OpenFlags openFlags) { - auto fs = getFileSystem(); - if(fs == nullptr) { - return false; - } + GET_FS(false) lastError = FS_OK; @@ -96,10 +73,7 @@ size_t FileStream::readBytes(char* buffer, size_t length) return 0; } - auto fs = getFileSystem(); - if(fs == nullptr) { - return 0; - } + GET_FS(0) int available = fs->read(handle, buffer, std::min(size - pos, length)); if(!check(available)) { @@ -113,10 +87,7 @@ size_t FileStream::readBytes(char* buffer, size_t length) uint16_t FileStream::readMemoryBlock(char* data, int bufSize) { - auto fs = getFileSystem(); - if(fs == nullptr) { - return 0; - } + GET_FS(0) assert(bufSize >= 0); size_t startPos = pos; @@ -131,10 +102,7 @@ uint16_t FileStream::readMemoryBlock(char* data, int bufSize) size_t FileStream::write(const uint8_t* buffer, size_t size) { - auto fs = getFileSystem(); - if(fs == nullptr) { - return 0; - } + GET_FS(0) if(pos != this->size) { int writePos = fs->lseek(handle, 0, SeekOrigin::End); @@ -156,10 +124,7 @@ size_t FileStream::write(const uint8_t* buffer, size_t size) int FileStream::seekFrom(int offset, SeekOrigin origin) { - auto fs = getFileSystem(); - if(fs == nullptr) { - return 0; - } + GET_FS(lastError) // Cannot rely on return value from fileSeek - failure does not mean position hasn't changed fs->lseek(handle, offset, origin); diff --git a/Sming/Core/Data/Stream/IFS/FileStream.h b/Sming/Core/Data/Stream/IFS/FileStream.h index d9147d015f..e6669a63cd 100644 --- a/Sming/Core/Data/Stream/IFS/FileStream.h +++ b/Sming/Core/Data/Stream/IFS/FileStream.h @@ -35,15 +35,22 @@ class FileStream : public FsBase, public ReadWriteStream */ void attach(FileHandle file, size_t size); - bool open(const Stat& stat, OpenFlags openFlags = OpenFlag::Read); + /** @brief Open a file by path, and attach this stream object to it + * @param fileName Full path to file + * @param openFlags + * @retval bool true on success, false on error + * @note call getLastError() to determine cause of failure + */ + bool open(const String& fileName, IFS::OpenFlags openFlags = OpenFlag::Read); /** @brief Open a file and attach this stream object to it - * @param fileName + * @param dir Location of file + * @param fileName Name of file * @param openFlags * @retval bool true on success, false on error * @note call getLastError() to determine cause of failure */ - bool open(const String& fileName, IFS::OpenFlags openFlags = OpenFlag::Read); + bool open(DirHandle dir, const String& name, OpenFlags openFlags = OpenFlag::Read); /** @brief Close file */ @@ -139,6 +146,13 @@ class FileStream : public FsBase, public ReadWriteStream return truncate(pos); } + bool stat(Stat& s) + { + GET_FS(false) + + return check(fs->fstat(handle, &s)); + } + private: FileHandle handle{-1}; size_t pos{0}; diff --git a/Sming/Core/FileSystem.cpp b/Sming/Core/FileSystem.cpp index 4a65bed66c..89764a1fcf 100644 --- a/Sming/Core/FileSystem.cpp +++ b/Sming/Core/FileSystem.cpp @@ -14,14 +14,14 @@ namespace SmingInternal { -IFS::IFileSystem* activeFileSystem; +IFS::FileSystem* activeFileSystem; } void fileSetFileSystem(IFS::IFileSystem* fileSystem) { if(SmingInternal::activeFileSystem != fileSystem) { delete SmingInternal::activeFileSystem; - SmingInternal::activeFileSystem = fileSystem; + SmingInternal::activeFileSystem = IFS::FileSystem::cast(fileSystem); } } @@ -79,7 +79,8 @@ bool hyfs_mount() bool hyfs_mount(Storage::Partition fwfsPartition, Storage::Partition spiffsPartition) { - auto fs = IFS::createHybridFilesystem(fwfsPartition, spiffsPartition); + auto ffs = IFS::createSpiffsFilesystem(spiffsPartition); + auto fs = IFS::createHybridFilesystem(fwfsPartition, ffs); return fileMountFileSystem(fs); } diff --git a/Sming/Core/FileSystem.h b/Sming/Core/FileSystem.h index 49638193ff..78b9f3b820 100644 --- a/Sming/Core/FileSystem.h +++ b/Sming/Core/FileSystem.h @@ -18,7 +18,6 @@ #include #include #include -#include #include "WVector.h" ///< @deprecated see fileList() using file_t = IFS::FileHandle; @@ -47,7 +46,7 @@ namespace SmingInternal * first. * */ -extern IFS::IFileSystem* activeFileSystem; +extern IFS::FileSystem* activeFileSystem; } // namespace SmingInternal @@ -87,19 +86,19 @@ constexpr FileOpenFlags eFO_CreateNewAlways{File::CreateNewAlways}; ///< @deprec auto fileSystem = static_cast(SmingInternal::activeFileSystem); \ if(fileSystem == nullptr) { \ debug_e("ERROR in %s(): No active file system", __FUNCTION__); \ - return file_t(IFS::Error::NoFileSystem); \ + return FileHandle(IFS::Error::NoFileSystem); \ } /** * @brief Get the currently active file system, if any - * @retval IFS::IFileSystem* + * @retval IFS::FileSystem* */ inline IFS::FileSystem* getFileSystem() { if(SmingInternal::activeFileSystem == nullptr) { debug_e("ERROR: No active file system"); } - return static_cast(SmingInternal::activeFileSystem); + return SmingInternal::activeFileSystem; } /** @brief Sets the currently active file system @@ -157,108 +156,97 @@ bool hyfs_mount(); */ bool hyfs_mount(Storage::Partition fwfsPartition, Storage::Partition spiffsPartition); -/** @brief Open file - * @param name File name +/** @brief Open file by path + * @param path Full path to file * @param flags Mode to open file - * @retval file File ID or negative error code + * @retval file File handle or negative error code */ -inline file_t fileOpen(const char* name, FileOpenFlags flags = File::ReadOnly) +template inline FileHandle fileOpen(const T& path, FileOpenFlags flags = File::ReadOnly) { CHECK_FS(open) - return fileSystem->open(name, flags); -} - -inline file_t fileOpen(const String& name, FileOpenFlags flags = File::ReadOnly) -{ - return fileOpen(name.c_str(), flags); -} - -inline file_t fileOpen(const FileStat& stat, FileOpenFlags flags = File::ReadOnly) -{ - CHECK_FS(fopen) - return fileSystem->fopen(stat, flags); + return fileSystem->open(path, flags); } /** @brief Clode file - * @param file ID of file to open - * @note File ID is returned from fileOpen function + * @param file Handle of file to close + * @note File Handle is returned from fileOpen function */ -inline int fileClose(file_t file) +inline int fileClose(FileHandle file) { CHECK_FS(close) return fileSystem->close(file); } /** @brief Write to file - * @param file File ID + * @param file File handle * @param data Pointer to data to write to file * @param size Quantity of data elements to write to file * @retval int Quantity of data elements actually written to file or negative error code */ -inline int fileWrite(file_t file, const void* data, size_t size) +inline int fileWrite(FileHandle file, const void* data, size_t size) { CHECK_FS(write); return fileSystem->write(file, data, size); } /** @brief Update file modification time - * @param file File ID + * @param file File handle * @retval int Error code */ -inline int fileTouch(file_t file) +inline int fileTouch(FileHandle file) { return fileWrite(file, nullptr, 0); } /** @brief Read from file - * @param file File ID + * @param file File handle * @param data Pointer to data buffer in to which to read data * @param size Quantity of data elements to read from file * @retval int Quantity of data elements actually read from file or negative error code */ -inline int fileRead(file_t file, void* data, size_t size) +inline int fileRead(FileHandle file, void* data, size_t size) { CHECK_FS(read) return fileSystem->read(file, data, size); } /** @brief Position file cursor - * @param file File ID + * @param file File handle * @param offset Quantity of bytes to move cursor * @param origin Position from where to move cursor * @retval int Offset within file or negative error code */ -inline int fileSeek(file_t file, int offset, SeekOrigin origin) +inline int fileSeek(FileHandle file, int offset, SeekOrigin origin) { CHECK_FS(seek) return fileSystem->lseek(file, offset, origin); } /** @brief Check if at end of file - * @param file File ID + * @param file File handle * @retval bool true if at end of file */ -inline bool fileIsEOF(file_t file) +inline bool fileIsEOF(FileHandle file) { auto fileSystem = getFileSystem(); return fileSystem ? (fileSystem->eof(file) != 0) : true; } /** @brief Get position in file - * @param file File ID + * @param file File handle * @retval int32_t Read / write cursor position or error code */ -inline int fileTell(file_t file) +inline int fileTell(FileHandle file) { CHECK_FS(tell) return fileSystem->tell(file); } /** @brief Flush pending writes - * @param file File ID + * @param file File handle * @retval int Size of last file written or error code */ -inline int fileFlush(file_t file) +inline int fileFlush(FileHandle file) { CHECK_FS(flush) return fileSystem->flush(file); @@ -314,7 +302,7 @@ template inline uint32_t fileGetSize(const TFileName& fileN * @note In POSIX `ftruncate()` can also make the file bigger, however SPIFFS can only * reduce the file size and will return an error if newSize > fileSize */ -inline int fileTruncate(file_t file, size_t newSize) +inline int fileTruncate(FileHandle file, size_t newSize) { CHECK_FS(truncate); return fileSystem->ftruncate(file, newSize); @@ -324,7 +312,7 @@ inline int fileTruncate(file_t file, size_t newSize) * @param file Open file handle, must have Write access * @retval int Error code */ -inline int fileTruncate(file_t file) +inline int fileTruncate(FileHandle file) { CHECK_FS(truncate); return fileSystem->ftruncate(file); @@ -402,7 +390,7 @@ template inline size_t fileGetContent(const TFileName& file return fileSystem ? fileSystem->getContent(fileName, buffer) : 0; } -/** @brief Get file statistics +/** @brief Get file statistics * @param name File name * @param stat Pointer to SPIFFS statistic structure to populate * @retval int Error code @@ -419,11 +407,11 @@ inline int fileStats(const String& fileName, FileStat& stat) } /** brief Get file statistics - * @param file File ID + * @param file File handle * @param stat Pointer to SPIFFS statistic structure to populate * @retval int Error code */ -inline int fileStats(file_t file, FileStat& stat) +inline int fileStats(FileHandle file, FileStat& stat) { CHECK_FS(fstat) return fileSystem->fstat(file, &stat); @@ -448,20 +436,21 @@ inline int fileDelete(const String& fileName) * @param file handle of file to delete * @retval int Error code */ -inline int fileDelete(file_t file) +inline int fileDelete(FileHandle file) { CHECK_FS(fremove) return fileSystem->fremove(file); } /** @brief Check if a file exists on file system - * @param name Name of file to check for + * @param fileName Full path to file to check for * @retval bool true if file exists */ inline bool fileExist(const char* fileName) { CHECK_FS(stat) - return fileSystem->stat(fileName, nullptr) >= 0; + FileStat stat; + return fileSystem->stat(fileName, &stat) >= 0 && !stat.attr[FileAttribute::Directory]; } inline bool fileExist(const String& fileName) @@ -469,6 +458,22 @@ inline bool fileExist(const String& fileName) return fileExist(fileName.c_str()); } +/** @brief Check if a directory exists on file system + * @param dirName Full path to directory to check for + * @retval bool true if directory exists + */ +inline bool dirExist(const char* dirName) +{ + CHECK_FS(stat) + FileStat stat; + return fileSystem->stat(dirName, &stat) >= 0 && stat.attr[FileAttribute::Directory]; +} + +inline bool dirExist(const String& dirName) +{ + return dirExist(dirName.c_str()); +} + /** @brief Open a named directory for reading * @param name Name of directory to open, empty or "/" for root * @param dir Directory object @@ -491,17 +496,6 @@ inline int fileOpenRootDir(DirHandle& dir) return fileOpenDir(nullptr, dir); } -/** @brief Open a sub-directory for reading - * @param stat Details of directory to open, nullptr for root directory - * @param dir Directory object - * @retrval 0 on success or negative error - */ -inline int fileOpenDir(const FileStat& stat, DirHandle& dir) -{ - CHECK_FS(opendir) - return fileSystem->fopendir(&stat, dir); -} - /** @brief close a directory object * @param dir directory to close * @retval int Error code @@ -536,7 +530,7 @@ inline int fileRewindDir(DirHandle dir) /** @brief Get basic file system information * @retval int Error code */ -inline int fileGetSystemInfo(IFS::IFileSystem::Info& info) +inline int fileGetSystemInfo(IFS::FileSystem::Info& info) { CHECK_FS(getinfo) return fileSystem->getinfo(info); @@ -545,7 +539,7 @@ inline int fileGetSystemInfo(IFS::IFileSystem::Info& info) /** @brief Get the type of file system currently mounted (if any) * @retval FileSystemType the file system type */ -IFS::IFileSystem::Type fileSystemType(); +IFS::FileSystem::Type fileSystemType(); /** @brief Format the active file system * @retval int Error code @@ -570,7 +564,7 @@ inline int fileSystemCheck() * @param acl * @retval int Error code */ -inline int fileSetACL(file_t file, const IFS::ACL& acl) +inline int fileSetACL(FileHandle file, const IFS::ACL& acl) { CHECK_FS(setacl) return fileSystem->setacl(file, acl); @@ -599,7 +593,7 @@ inline int fileSetAttr(const String& filename, FileAttributes attr) * @retval int Error code * @note any writes to file will reset this to current time */ -inline int fileSetTime(file_t file, time_t mtime) +inline int fileSetTime(FileHandle file, time_t mtime) { CHECK_FS(settime) return fileSystem->settime(file, mtime); diff --git a/Sming/Core/Network/Ftp/FtpDataRetrieve.h b/Sming/Core/Network/Ftp/FtpDataRetrieve.h index 70cc360bc3..032526b52a 100644 --- a/Sming/Core/Network/Ftp/FtpDataRetrieve.h +++ b/Sming/Core/Network/Ftp/FtpDataRetrieve.h @@ -41,5 +41,5 @@ class FtpDataRetrieve : public FtpDataStream } private: - file_t file; + FileHandle file; }; diff --git a/Sming/Core/Network/Ftp/FtpDataStore.h b/Sming/Core/Network/Ftp/FtpDataStore.h index a713b97af0..4942cd7bd4 100644 --- a/Sming/Core/Network/Ftp/FtpDataStore.h +++ b/Sming/Core/Network/Ftp/FtpDataStore.h @@ -48,5 +48,5 @@ class FtpDataStore : public FtpDataStream } private: - file_t file; + FileHandle file; }; diff --git a/Sming/Core/Network/FtpServer.h b/Sming/Core/Network/FtpServer.h index 15be35353a..78f947b938 100644 --- a/Sming/Core/Network/FtpServer.h +++ b/Sming/Core/Network/FtpServer.h @@ -25,7 +25,7 @@ class CustomFtpServer : public TcpServer friend class FtpServerConnection; public: - CustomFtpServer(IFS::IFileSystem* fileSystem = nullptr) : fileSystem(fileSystem) + CustomFtpServer(IFS::FileSystem* fileSystem = nullptr) : fileSystem(fileSystem) { setTimeOut(900); } @@ -55,11 +55,11 @@ class CustomFtpServer : public TcpServer IFS::FileSystem* getFileSystem() const { - return fileSystem ? IFS::FileSystem::cast(fileSystem) : ::getFileSystem(); + return fileSystem ?: ::getFileSystem(); } private: - IFS::IFileSystem* fileSystem; + IFS::FileSystem* fileSystem; }; /** @defgroup ftpserver FTP server diff --git a/Sming/Core/Network/Http/HttpResponse.cpp b/Sming/Core/Network/Http/HttpResponse.cpp index 0330e875bc..ac7fbe8eb4 100644 --- a/Sming/Core/Network/Http/HttpResponse.cpp +++ b/Sming/Core/Network/Http/HttpResponse.cpp @@ -61,38 +61,32 @@ bool HttpResponse::sendString(String&& text) noexcept return true; } -bool HttpResponse::sendFile(const FileStat& stat) -{ - auto file = new FileStream(stat); - if(stat.compression.type == IFS::Compression::Type::GZip) { - headers[HTTP_HEADER_CONTENT_ENCODING] = F("gzip"); - } else if(stat.compression.type != IFS::Compression::Type::None) { - debug_e("Unsupported compression type: %u", stat.compression); - } - - return sendDataStream(file, ContentType::fromFullFileName(stat.name)); -} - bool HttpResponse::sendFile(const String& fileName, bool allowGzipFileCheck) { - FileStat stat; + auto fs = new FileStream; if(allowGzipFileCheck) { String fnCompressed = fileName + _F(".gz"); - if(fileStats(fnCompressed, stat) >= 0) { - debug_d("found %s", stat.name); - stat.compression.type = IFS::Compression::Type::GZip; - stat.name = IFS::NameBuffer(const_cast(fileName)); - return sendFile(stat); + if(fs->open(fnCompressed)) { + debug_d("found %s", fnCompressed.c_str()); + headers[HTTP_HEADER_CONTENT_ENCODING] = F("gzip"); + return sendDataStream(fs, ContentType::fromFullFileName(fileName)); } } - if(fileStats(fileName, stat) >= 0) { + if(fs->open(fileName)) { debug_d("found %s", fileName.c_str()); - stat.name = IFS::NameBuffer(const_cast(fileName)); - return sendFile(stat); + FileStat stat; + fs->stat(stat); + if(stat.compression.type == IFS::Compression::Type::GZip) { + headers[HTTP_HEADER_CONTENT_ENCODING] = F("gzip"); + } else if(stat.compression.type != IFS::Compression::Type::None) { + debug_e("Unsupported compression type: %u", stat.compression); + } + return sendDataStream(fs, ContentType::fromFullFileName(fileName)); } + delete fs; code = HTTP_STATUS_NOT_FOUND; return false; } diff --git a/Sming/Core/Network/Http/HttpResponse.h b/Sming/Core/Network/Http/HttpResponse.h index d12d7f4725..b4579b810f 100644 --- a/Sming/Core/Network/Http/HttpResponse.h +++ b/Sming/Core/Network/Http/HttpResponse.h @@ -94,12 +94,6 @@ class HttpResponse return this; } - /* - * Send file by stat, indicates whether file is compressed - * A name is required in stat to get the appropriate content type - */ - bool sendFile(const FileStat& stat); - /** * @brief Send file by name * @param fileName diff --git a/Sming/Libraries/LittleFS b/Sming/Libraries/LittleFS index f55d97190d..4b3ecd8ba8 160000 --- a/Sming/Libraries/LittleFS +++ b/Sming/Libraries/LittleFS @@ -1 +1 @@ -Subproject commit f55d97190da949617526b57fdd4dc369c4128217 +Subproject commit 4b3ecd8ba82bb9121ac4faa64f3bf4845b5d9d8c diff --git a/samples/Basic_IFS/app/application.cpp b/samples/Basic_IFS/app/application.cpp index d250b2eb25..a721a7907f 100644 --- a/samples/Basic_IFS/app/application.cpp +++ b/samples/Basic_IFS/app/application.cpp @@ -53,13 +53,7 @@ void onFile(HttpRequest& request, HttpResponse& response) String file = request.uri.getRelativePath(); - FileStat stat; - if(fileStats(file, stat) < 0) { - response.code = HTTP_STATUS_INTERNAL_SERVER_ERROR; - return; - } - - if(stat.isDir()) { + if(dirExist(file)) { auto dir = new Directory; IFS::DirectoryTemplate* tmpl; String fmt = request.uri.Query["format"]; @@ -79,17 +73,20 @@ void onFile(HttpRequest& request, HttpResponse& response) response.sendDataStream(tmpl, tmpl->getMimeType()); } else { // response.setCache(86400, true); // It's important to use cache for better performance. - auto stream = new FileStream(stat); - if(!stream->isValid()) { - stream->open(file); + auto stream = new FileStream; + if(!stream->open(file)) { + response.code = HTTP_STATUS_INTERNAL_SERVER_ERROR; + delete stream; + return; } + FileStat stat; + stream->stat(stat); if(stat.compression.type == IFS::Compression::Type::GZip) { response.headers[HTTP_HEADER_CONTENT_ENCODING] = F("gzip"); } else if(stat.compression.type != IFS::Compression::Type::None) { debug_e("Unsupported compression type: %u", stat.compression.type); } - - auto mimeType = ContentType::fromFullFileName(file.c_str(), MIME_TEXT); + auto mimeType = ContentType::fromFullFileName(file, MIME_TEXT); response.sendDataStream(stream, mimeType); } } @@ -125,15 +122,8 @@ bool initFileSystem() auto part = Storage::findDefaultPartition(Storage::Partition::SubType::Data::fwfs); #endif - IFS::IFileSystem* fs; -#ifdef FWFS_HYBRID - // Create a read/write filesystem - auto spiffsPart = Storage::findDefaultPartition(Storage::Partition::SubType::Data::spiffs); - fs = IFS::createHybridFilesystem(part, spiffsPart); -#else // Read-only - fs = IFS::createFirmwareFilesystem(part); -#endif + auto fs = IFS::createFirmwareFilesystem(part); debug_i("2: heap = -%u", freeheap - system_get_free_heap_size()); if(fs == nullptr) { diff --git a/samples/Basic_rBoot/app/application.cpp b/samples/Basic_rBoot/app/application.cpp index 11a6d7c9d5..e53a3a84fc 100644 --- a/samples/Basic_rBoot/app/application.cpp +++ b/samples/Basic_rBoot/app/application.cpp @@ -167,10 +167,11 @@ void serialCallBack(Stream& stream, char arrivedChar, unsigned short availableCh } else if(!strcmp(str, "cat")) { Directory dir; if(dir.open() && dir.next()) { - Serial.printf("dumping file %s:\r\n", dir.stat().name.c_str()); + auto filename = dir.stat().name.c_str(); + Serial.printf("dumping file %s:\r\n", filename); // We don't know how big the is, so streaming it is safest FileStream fs; - fs.open(dir.stat()); + fs.open(filename); Serial.copyFrom(&fs); Serial.println(); } else { diff --git a/tests/HostTests/modules/Files.cpp b/tests/HostTests/modules/Files.cpp index 6a401b46c6..05eef51a4b 100644 --- a/tests/HostTests/modules/Files.cpp +++ b/tests/HostTests/modules/Files.cpp @@ -16,7 +16,7 @@ class FilesTest : public TestGroup DEFINE_FSTR_LOCAL(testFileName, "test.txt"); int res, pos, size; - file_t file = -1; + FileHandle file{-1}; TEST_CASE("Initial position and size") { From 07ecd3bdf33f5b56ec1c32c9587df456ffaa4c07 Mon Sep 17 00:00:00 2001 From: slaff Date: Tue, 20 Apr 2021 08:11:57 +0200 Subject: [PATCH 008/130] [WIP] Feature: component hosted (#2305) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hosted component allows Sming's host emulator to run parts of the commands on an actual microcontroller. The communication is done via [simplePRC](https://simplerpc.readthedocs.io) and the microcontroller has to be flashed with a special application. Overview -------- Sming's host emulator allows easier debugging and development of embedded applications. This component named "Hosted" extends the host emulator and facilitates testing functionality that only a real microcontroller can provide as for example digital I/O operations or SPI operations. For example in order to run the Basic_Blink application under the host emulator and run the actual blinking of a LED on a microcontroller we can compile the application using the following directives:: ``` make SMING_ARCH=Host ENABLE_HOSTED=tcp HOSTED_SERVER_IP=192.168.4.1 ``` `SMING_ARCH=Host` instructs the build system to build the application for the Host architecture. `ENABLE_HOSTED=tcp` instructs the host emulator to communication with the real microcontroller using TCP `HOSTED_SERVER_IP=192.168.4.1` instructs the host emulator to connect to IP `192.168.4.1`. In the sub-directory ``samples`` inside this component you will find the sample applications that will turn your microcontroller into a remote RCP server. The compilation and flashing for ESP32, for example, can be done using the following commands: ``` cd samples/tcp make SMING_ARCH=Esp32 WIFI_SSID=YourSSID WIFI_PWD=YourPassword make flash ``` If you replace ``SMING_ARCH=Esp32`` with ``SMING_ARCH=Esp8266`` then the hosted application will be compiled and flashed on a ESP8266 microcontroller. Make sure to replace the values of  WIFI_SSID and WIFI_PWD with the actual name and password for the Access Point (AP). --- .gitmodules | 6 +- .travis.yml | 12 +- .../Components/hostlib/include/hostlib/emu.h | 24 +++ .../Components/hostlib/include/hostlib/init.h | 30 +++ Sming/Arch/Host/Components/hostlib/init.cpp | 27 +++ .../Arch/Host/Components/hostlib/startup.cpp | 30 ++- .../Host/Components/sming-arch/component.mk | 5 + Sming/Arch/Host/Core/SPI.cpp | 14 ++ Sming/Arch/Host/Core/SPI.h | 14 +- Sming/Arch/Host/Tools/travis/install.sh | 6 +- Sming/Arch/Host/app.mk | 5 + Sming/Components/Hosted-Lib/.cs | 0 Sming/Components/Hosted-Lib/component.mk | 2 + Sming/Components/Hosted-Lib/src/Digital.cpp | 33 +++ Sming/Components/Hosted/.cs | 0 Sming/Components/Hosted/README.rst | 52 +++++ Sming/Components/Hosted/component.mk | 21 ++ .../Components/Hosted/include/Hosted/Client.h | 174 ++++++++++++++++ .../include/Hosted/Transport/BaseTransport.h | 43 ++++ .../Hosted/Transport/SerialTransport.h | 40 ++++ .../Hosted/Transport/TcpClientStream.h | 93 +++++++++ .../Hosted/Transport/TcpClientTransport.h | 54 +++++ .../Hosted/Transport/TcpServerTransport.h | 60 ++++++ .../include/Hosted/Transport/TcpTransport.h | 31 +++ .../Hosted/init/serial/InitClient.cpp | 32 +++ .../Components/Hosted/init/tcp/InitClient.cpp | 73 +++++++ .../Components/Hosted/samples/serial/Makefile | 9 + .../Hosted/samples/serial/README.rst | 9 + .../Hosted/samples/serial/app/application.cpp | 36 ++++ .../Hosted/samples/serial/component.mk | 9 + Sming/Components/Hosted/samples/tcp/Makefile | 9 + .../Components/Hosted/samples/tcp/README.rst | 32 +++ .../Hosted/samples/tcp/app/application.cpp | 79 +++++++ .../Hosted/samples/tcp/component.mk | 10 + Sming/Core/Network/MqttClient.cpp | 4 +- Sming/Core/Network/TcpClient.h | 10 + Sming/Core/Network/TcpServer.h | 5 + Sming/Libraries/simpleRPC/component.mk | 6 + .../Libraries/simpleRPC/include/simpleRPC/.cs | 0 .../simpleRPC/include/simpleRPC/parser.h | 63 ++++++ Sming/Libraries/simpleRPC/simpleRPC | 1 + Sming/Libraries/simpleRPC/simpleRPC.patch | 65 ++++++ Sming/Libraries/simpleRPC/src/.cs | 0 Sming/Libraries/simpleRPC/src/parser.cpp | 195 ++++++++++++++++++ tests/HostTests/component.mk | 3 +- tests/HostTests/include/modules.h | 3 +- tests/HostTests/modules/Hosted.cpp | 155 ++++++++++++++ 47 files changed, 1547 insertions(+), 37 deletions(-) create mode 100644 Sming/Arch/Host/Components/hostlib/include/hostlib/emu.h create mode 100644 Sming/Arch/Host/Components/hostlib/include/hostlib/init.h create mode 100644 Sming/Arch/Host/Components/hostlib/init.cpp create mode 100644 Sming/Arch/Host/Core/SPI.cpp create mode 100644 Sming/Components/Hosted-Lib/.cs create mode 100644 Sming/Components/Hosted-Lib/component.mk create mode 100644 Sming/Components/Hosted-Lib/src/Digital.cpp create mode 100644 Sming/Components/Hosted/.cs create mode 100644 Sming/Components/Hosted/README.rst create mode 100644 Sming/Components/Hosted/component.mk create mode 100644 Sming/Components/Hosted/include/Hosted/Client.h create mode 100644 Sming/Components/Hosted/include/Hosted/Transport/BaseTransport.h create mode 100644 Sming/Components/Hosted/include/Hosted/Transport/SerialTransport.h create mode 100644 Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h create mode 100644 Sming/Components/Hosted/include/Hosted/Transport/TcpClientTransport.h create mode 100644 Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h create mode 100644 Sming/Components/Hosted/include/Hosted/Transport/TcpTransport.h create mode 100644 Sming/Components/Hosted/init/serial/InitClient.cpp create mode 100644 Sming/Components/Hosted/init/tcp/InitClient.cpp create mode 100644 Sming/Components/Hosted/samples/serial/Makefile create mode 100644 Sming/Components/Hosted/samples/serial/README.rst create mode 100644 Sming/Components/Hosted/samples/serial/app/application.cpp create mode 100644 Sming/Components/Hosted/samples/serial/component.mk create mode 100644 Sming/Components/Hosted/samples/tcp/Makefile create mode 100644 Sming/Components/Hosted/samples/tcp/README.rst create mode 100644 Sming/Components/Hosted/samples/tcp/app/application.cpp create mode 100644 Sming/Components/Hosted/samples/tcp/component.mk create mode 100644 Sming/Libraries/simpleRPC/component.mk create mode 100644 Sming/Libraries/simpleRPC/include/simpleRPC/.cs create mode 100644 Sming/Libraries/simpleRPC/include/simpleRPC/parser.h create mode 160000 Sming/Libraries/simpleRPC/simpleRPC create mode 100644 Sming/Libraries/simpleRPC/simpleRPC.patch create mode 100644 Sming/Libraries/simpleRPC/src/.cs create mode 100644 Sming/Libraries/simpleRPC/src/parser.cpp create mode 100644 tests/HostTests/modules/Hosted.cpp diff --git a/.gitmodules b/.gitmodules index b611b9d729..eb585627f1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -194,7 +194,7 @@ [submodule "Libraries.flatbuffers"] path = Sming/Libraries/flatbuffers/src url = https://github.com/google/flatbuffers.git - ignore = dirty + ignore = dirty [submodule "Libraries.GoogleCast"] path = Sming/Libraries/GoogleCast url = https://github.com/slaff/Sming-GoogleCast.git @@ -263,6 +263,10 @@ path = Sming/Libraries/SignalGenerator url = https://github.com/mikee47/SignalGenerator ignore = dirty +[submodule "Libraries.simpleRPC"] + path = Sming/Libraries/simpleRPC/simpleRPC + url = https://github.com/jfjlaros/simpleRPC.git + ignore = dirty [submodule "Libraries.SmingTest"] path = Sming/Libraries/SmingTest url = https://github.com/mikee47/SmingTest diff --git a/.travis.yml b/.travis.yml index 97b2443e0f..7c56d90a9e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,9 +15,9 @@ jobs: addons: apt: packages: - - clang-format-6.0 + - clang-format-8 - gcc-multilib - - g++-multilib + - g++-9-multilib - doxygen - python3-sphinx - python3-pip @@ -28,14 +28,6 @@ jobs: - jq env: - SMING_ARCH=Host - - - stage: build - name: C++11 - env: - - SMING_ARCH=Esp8266 - - SDK_VERSION=3.0.1 - - ESP_HOME=$TRAVIS_BUILD_DIR/opt/esp-alt-sdk - - stage: build name: C++17 env: diff --git a/Sming/Arch/Host/Components/hostlib/include/hostlib/emu.h b/Sming/Arch/Host/Components/hostlib/include/hostlib/emu.h new file mode 100644 index 0000000000..5ebda3bce8 --- /dev/null +++ b/Sming/Arch/Host/Components/hostlib/include/hostlib/emu.h @@ -0,0 +1,24 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * emu.h + * + ****/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Executing this function will run once the main emulator loop. + */ +void host_main_loop(); + +#ifdef __cplusplus +} +#endif diff --git a/Sming/Arch/Host/Components/hostlib/include/hostlib/init.h b/Sming/Arch/Host/Components/hostlib/include/hostlib/init.h new file mode 100644 index 0000000000..76c1f9defe --- /dev/null +++ b/Sming/Arch/Host/Components/hostlib/include/hostlib/init.h @@ -0,0 +1,30 @@ +/** + * hostlib.h + * + * Copyright 2019 mikee47 + * + * This file is part of the Sming Framework Project + * + * This library is free software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, version 3 or later. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with SHEM. + * If not, see . + * + ****/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void host_init(); + +#ifdef __cplusplus +} +#endif diff --git a/Sming/Arch/Host/Components/hostlib/init.cpp b/Sming/Arch/Host/Components/hostlib/init.cpp new file mode 100644 index 0000000000..4246269810 --- /dev/null +++ b/Sming/Arch/Host/Components/hostlib/init.cpp @@ -0,0 +1,27 @@ +/** + * init.cpp - Sming Host Emulator startup code + * + * Copyright 2019 mikee47 + * + * This file is part of the Sming Framework Project + * + * This library is free software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, version 3 or later. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with SHEM. + * If not, see . + * + ****/ + +#include "include/hostlib/init.h" + +extern void init(); + +void host_init() +{ + init(); +} diff --git a/Sming/Arch/Host/Components/hostlib/startup.cpp b/Sming/Arch/Host/Components/hostlib/startup.cpp index f33bba2f5b..b59632a496 100644 --- a/Sming/Arch/Host/Components/hostlib/startup.cpp +++ b/Sming/Arch/Host/Components/hostlib/startup.cpp @@ -31,6 +31,9 @@ #include #include #include +#include "include/hostlib/init.h" +#include "include/hostlib/emu.h" +#include "include/hostlib/hostlib.h" #include "include/hostlib/CommandLine.h" #include @@ -39,8 +42,9 @@ static int exitCode = 0; static bool done = false; +static bool lwip_initialised = false; +static OneShotElapseTimer lwipServiceTimer; -extern void init(); extern void host_wifi_lwip_init_complete(); extern void host_init_bootloader(); @@ -104,6 +108,17 @@ static void pause(int secs) } } +void host_main_loop() +{ + host_service_tasks(); + host_service_timers(); + if (lwip_initialised && lwipServiceTimer.expired()) { + host_lwip_service(); + lwipServiceTimer.start(); + } + system_soft_wdt_feed(); +} + int main(int argc, char* argv[]) { trap_exceptions(); @@ -230,7 +245,7 @@ int main(int argc, char* argv[]) sockets_initialise(); CUartServer::startup(config.uart); - bool lwip_initialised = false; + if(config.enable_network) { lwip_initialised = host_lwip_init(&config.lwip); if(lwip_initialised) { @@ -247,18 +262,11 @@ int main(int argc, char* argv[]) System.initialize(); - init(); + host_init(); - OneShotElapseTimer lwipServiceTimer; lwipServiceTimer.reset(); while(!done) { - host_service_tasks(); - host_service_timers(); - if(lwip_initialised && lwipServiceTimer.expired()) { - host_lwip_service(); - lwipServiceTimer.start(); - } - system_soft_wdt_feed(); + host_main_loop(); } host_debug_i(">> Normal Exit <<\n"); diff --git a/Sming/Arch/Host/Components/sming-arch/component.mk b/Sming/Arch/Host/Components/sming-arch/component.mk index 2fe62df117..db03e70106 100644 --- a/Sming/Arch/Host/Components/sming-arch/component.mk +++ b/Sming/Arch/Host/Components/sming-arch/component.mk @@ -37,6 +37,11 @@ COMPONENT_DEPENDS := \ spi_flash \ vflash \ rboot + + +ifneq ($(ENABLE_HOSTED),) + COMPONENT_DEPENDS += Hosted-Lib +endif # => Platform WiFi COMPONENT_VARS := \ diff --git a/Sming/Arch/Host/Core/SPI.cpp b/Sming/Arch/Host/Core/SPI.cpp new file mode 100644 index 0000000000..22edc86773 --- /dev/null +++ b/Sming/Arch/Host/Core/SPI.cpp @@ -0,0 +1,14 @@ +#include "SPI.h" + +bool SPIClass::begin() +{ + return false; +} + +void SPIClass::transfer(uint8_t* buffer, size_t numberBytes) +{ +} + +void SPIClass::prepare(SPISettings& settings) +{ +} diff --git a/Sming/Arch/Host/Core/SPI.h b/Sming/Arch/Host/Core/SPI.h index edcc04bd16..f1c6287d9c 100644 --- a/Sming/Arch/Host/Core/SPI.h +++ b/Sming/Arch/Host/Core/SPI.h @@ -30,24 +30,18 @@ class SPIClass : public SPIBase { public: - bool begin() override - { - return false; - } + bool begin() override; void end() override { } + using SPIBase::beginTransaction; using SPIBase::transfer; - void transfer(uint8_t* buffer, size_t numberBytes) override - { - } + void transfer(uint8_t* buffer, size_t numberBytes) override; protected: - void prepare(SPISettings& settings) override - { - } + void prepare(SPISettings& settings) override; }; /** @brief Global instance of SPI class */ diff --git a/Sming/Arch/Host/Tools/travis/install.sh b/Sming/Arch/Host/Tools/travis/install.sh index fd27cb5ba9..1fcf81b168 100755 --- a/Sming/Arch/Host/Tools/travis/install.sh +++ b/Sming/Arch/Host/Tools/travis/install.sh @@ -1,7 +1,11 @@ #!/bin/bash set -ex # exit with nonzero exit code if anything fails -sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-6.0 100 +sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 60 --slave /usr/bin/g++ g++ /usr/bin/g++-9 + +sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-8 200 +sudo rm -f /usr/local/clang-7.0.0/bin/clang-format + python3 -m pip install -q --upgrade pip if [ "$BUILD_DOCS" == "1" ]; then diff --git a/Sming/Arch/Host/app.mk b/Sming/Arch/Host/app.mk index 3d895d08ff..0e5c61c07e 100644 --- a/Sming/Arch/Host/app.mk +++ b/Sming/Arch/Host/app.mk @@ -12,6 +12,11 @@ LDFLAGS += \ # Executable TARGET_OUT_0 := $(FW_BASE)/$(APP_NAME)$(TOOL_EXT) +# Hosted Settings +ifneq ($(ENABLE_HOSTED),) + COMPONENTS_AR := $(USER_LIBDIR)/$(CLIB_PREFIX)Hosted-Lib-$(CMP_Hosted-Lib_LIBHASH).a $(COMPONENTS_AR) +endif + # Target definitions .PHONY: application diff --git a/Sming/Components/Hosted-Lib/.cs b/Sming/Components/Hosted-Lib/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Components/Hosted-Lib/component.mk b/Sming/Components/Hosted-Lib/component.mk new file mode 100644 index 0000000000..f2466e7923 --- /dev/null +++ b/Sming/Components/Hosted-Lib/component.mk @@ -0,0 +1,2 @@ +COMPONENT_SRCDIRS := $(COMPONENT_PATH)/src +COMPONENT_DEPENDS := Hosted \ No newline at end of file diff --git a/Sming/Components/Hosted-Lib/src/Digital.cpp b/Sming/Components/Hosted-Lib/src/Digital.cpp new file mode 100644 index 0000000000..d99e748ded --- /dev/null +++ b/Sming/Components/Hosted-Lib/src/Digital.cpp @@ -0,0 +1,33 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Digital.cpp + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#include +#include + +extern Hosted::Client* hostedClient; + +void pinMode(uint16_t pin, uint8_t mode) +{ + hostedClient->send(__func__, pin, mode); +} + +void digitalWrite(uint16_t pin, uint8_t val) +{ + hostedClient->send(__func__, pin, val); +} + +uint8_t digitalRead(uint16_t pin) +{ + hostedClient->send(__func__, pin); + return hostedClient->wait(); +} diff --git a/Sming/Components/Hosted/.cs b/Sming/Components/Hosted/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Components/Hosted/README.rst b/Sming/Components/Hosted/README.rst new file mode 100644 index 0000000000..824f811d39 --- /dev/null +++ b/Sming/Components/Hosted/README.rst @@ -0,0 +1,52 @@ +HostEd +====== + +.. highlight:: bash + +The hosted component allows Sming's host emulator to run parts of the commands on an actual microcontroller. +The communication is done via `simplePRC `_ and the microcontroller has to be flashed with a special application. + +Overview +-------- +Sming's host emulator allows easier debugging and development of embedded applications. This component named "HostEd" extends the host emulator +and facilitates testing functionality that only a real microcontroller can provide as for example digital I/O operations or SPI operations. + +For example in order to run the Basic_Blink application under the host emulator and run the actual blinking of a LED on a microcontroller +we can compile the application using the following directives:: + + make SMING_ARCH=Host ENABLE_HOSTED=tcp HOSTED_SERVER_IP=192.168.4.1 + +`SMING_ARCH=Host` instructs the build system to build the application for the Host architecture. +`ENABLE_HOSTED=tcp` instructs the host emulator to communication with the real microcontroller using TCP +`HOSTED_SERVER_IP=192.168.4.1` instructs the host emulator to connect to IP `192.168.4.1`. + +We need to compile and flash also a special application on the desired microcontroller. +This application will act as an RPC Server and will execute the commands from the host emulator on the microcontroller. + +In the sub-directory ``samples`` inside this directory you will find the sample applications that will turn your microcontroller into +RCP server. + +The compilation and flashing for ESP32, for example, can be done using the following commands:: + + cd samples/tcp + make SMING_ARCH=Esp32 WIFI_SSID=YourSSID WIFI_PWD=YourPassword + make flash + +If you replace ``SMING_ARCH=Esp32`` with ``SMING_ARCH=Esp8266`` then the hosted application will be compiled and flashed on a ESP8266 microcontroller. +Make sure to replace the values of  WIFI_SSID and WIFI_PWD with the actual name and password for the Access Point (AP). + +Communication +------------- +At the moment the communication between an application running on the Host and the RCP server running on a microcontroller +can be done using TCP or serial interface. +The ``transport`` classes are located under ``include/Hosted/Transport``. + +Configuration +------------- +.. envvar:: ENABLE_HOSTED + + Default: empty (disabled) + + Enables the hosted component. Valid values for the moment are: + - tcp - for communication over TCP network. + - serial - for communication over serial interface \ No newline at end of file diff --git a/Sming/Components/Hosted/component.mk b/Sming/Components/Hosted/component.mk new file mode 100644 index 0000000000..11f6778e28 --- /dev/null +++ b/Sming/Components/Hosted/component.mk @@ -0,0 +1,21 @@ +COMPONENT_SRCDIRS := $(COMPONENT_PATH)/src +COMPONENT_INCDIRS := $(COMPONENT_SRCDIRS) $(COMPONENT_PATH)/include +COMPONENT_DEPENDS := simpleRPC + +# Architecture of the device where the hosted service will be flashed +HOSTED_ARCH ?= Esp8266 + +COMPONENT_RELINK_VARS += ENABLE_HOSTED +COMPONENT_VARS := ENABLE_HOSTED + +ENABLE_HOSTED ?= + +ifneq ($(ENABLE_HOSTED),) + COMPONENT_SRCDIRS += $(COMPONENT_PATH)/init/$(ENABLE_HOSTED) + EXTRA_LDFLAGS := -Wl,-wrap,host_init +endif + +COMPONENT_VARS += HOSTED_SERVER_IP + +COMPONENT_CFLAGS = -DHOSTED_SERVER_IP=$(HOSTED_SERVER_IP) +COMPONENT_CXXFLAGS := $(COMPONENT_CFLAGS) diff --git a/Sming/Components/Hosted/include/Hosted/Client.h b/Sming/Components/Hosted/include/Hosted/Client.h new file mode 100644 index 0000000000..d736e82c28 --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Client.h @@ -0,0 +1,174 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Client.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#pragma once + +#ifndef ARCH_HOST +#error "Hosted::Client can be used only on the Host architecture!" +#endif + +#include +#include +#include +#include +#include +#include +#include + +using namespace simpleRPC; + +namespace Hosted +{ +constexpr int COMMAND_NOT_FOUND = -1; + +class Client +{ +public: + using RemoteCommands = HashMap; + + Client(Stream& stream) : stream(stream) + { + } + + /** + * @brief Method to send commands to the remote server + * @param functionName + * @param variable arguments + * + * @retval true on success + */ + template bool send(const char* functionName, Args... args) + { + uint8_t functionId = getFunctionId(functionName); + + rpcPrint(stream, functionId, args...); + + return true; + } + + /** + * @brief This method will block the execution until a message is detected + * @retval HostedCommand + */ + template R wait() + { + size_t neededBytes = sizeof(R); + + while(stream.available() < int(neededBytes)) { + stream.flush(); + host_main_loop(); + } + + R result{}; + stream.readBytes(reinterpret_cast(&result), sizeof(result)); + return result; + } + + /** + * @brief Fetches a list of commands supported on the RPC server and gives back the id of the desired command + * @param name command name to query + * @retval -1 if not found. Otherwise the id of the function + */ + int getFunctionId(const char* name) + { + if(fetchCommands) { + getRemoteCommands(); + } + + int id = commands.indexOf(name); + if(id < 0) { + return COMMAND_NOT_FOUND; + } + + return commands[name]; + } + + /** + * @brief Gets list of remote command names and their ids + * @retval true on success, false otherwise + */ + bool getRemoteCommands() + { + host_debug_i("Getting remote RPC commands \033[5m...\033[0m"); + + uint8_t head = 0xff; + stream.write(&head, 1); + char buffer[512]; + ParserSettings settings; + settings.startMethods = ParserSettings::SimpleMethod(&Client::startMethods, this); + settings.startMethod = ParserSettings::SimpleMethod(&Client::startMethod, this); + settings.methodName = ParserSettings::CharMethod(&Client::methodName, this); + settings.endMethod = ParserSettings::SimpleMethod(&Client::endMethod, this); + settings.endMethods = ParserSettings::SimpleMethod(&Client::endMethods, this); + settings.state = ParserState::ready; + + do { + stream.flush(); + host_main_loop(); + + size_t length = stream.readBytes(buffer, 512); + if(!length) { + continue; + } + + ParserResult result = parse(settings, buffer, length); + if(result == ParserResult::finished) { + break; + } + + if(result == ParserResult::error) { + debug_e("Invalid header"); + return false; + } + } while(true); + + host_debug_i("Connected. Starting application"); + + return true; + } + +private: + Stream& stream; + bool fetchCommands{true}; + RemoteCommands commands; + uint8_t methodPosition = 0; + String parsedCommand; + + void startMethods() + { + methodPosition = 0; + commands.clear(); + } + + void startMethod() + { + parsedCommand = ""; + } + + void methodName(char ch) + { + parsedCommand += ch; + } + + void endMethod() + { + commands[parsedCommand] = methodPosition++; + } + + void endMethods() + { + fetchCommands = false; + } +}; + +} // namespace Hosted diff --git a/Sming/Components/Hosted/include/Hosted/Transport/BaseTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/BaseTransport.h new file mode 100644 index 0000000000..484a568c02 --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Transport/BaseTransport.h @@ -0,0 +1,43 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BaseTransport.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#pragma once + +#include +#include + +namespace Hosted +{ +namespace Transport +{ +class BaseTransport +{ +public: + using DataHandler = Delegate; + + virtual ~BaseTransport() + { + } + + void onData(DataHandler handler) + { + this->handler = handler; + } + +protected: + DataHandler handler; +}; + +} // namespace Transport + +} // namespace Hosted diff --git a/Sming/Components/Hosted/include/Hosted/Transport/SerialTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/SerialTransport.h new file mode 100644 index 0000000000..6dca2b1510 --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Transport/SerialTransport.h @@ -0,0 +1,40 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * SerialTransport.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#pragma once + +#include +#include "BaseTransport.h" + +namespace Hosted +{ +namespace Transport +{ +class SerialTransport : public BaseTransport +{ +public: + SerialTransport(HardwareSerial& stream) + { + stream.onDataReceived(StreamDataReceivedDelegate(&SerialTransport::process, this)); + } + +private: + void process(Stream& source, char arrivedChar, uint16_t availableCharsCount) + { + handler(source); + } +}; + +} // namespace Transport + +} // namespace Hosted diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h new file mode 100644 index 0000000000..9dcc79ece6 --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h @@ -0,0 +1,93 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * TcpClientStream.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#pragma once + +#include +#include + +namespace Hosted +{ +namespace Transport +{ +class TcpClientStream : public Stream +{ +public: + TcpClientStream(TcpClient& client, size_t cbufferSize = 1024) : cBuffer(cbufferSize), client(client) + { + client.setReceiveDelegate(TcpClientDataDelegate(&TcpClientStream::store, this)); + } + + void setClient(TcpClient& client) + { + this->client = client; + } + + bool push(const uint8_t* buffer, size_t size) + { + size_t written = cBuffer.write(buffer, size); + return (written == size); + } + + size_t readBytes(char* buffer, size_t length) override + { + return cBuffer.readBytes(buffer, length); + } + + size_t write(const uint8_t* buffer, size_t size) override + { + if(client.send(reinterpret_cast(buffer), size)) { + return size; + } + + return 0; + } + + size_t write(uint8_t c) override + { + return cBuffer.write(c); + } + + int available() override + { + return cBuffer.available(); + } + + int peek() override + { + return cBuffer.peek(); + } + + int read() override + { + return cBuffer.read(); + } + + void flush() override + { + client.commit(); + } + +private: + CircularBuffer cBuffer; + TcpClient& client; + + bool store(TcpClient& client, char* data, int size) + { + return push(reinterpret_cast(data), size); + } +}; + +} // namespace Transport + +} // namespace Hosted diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientTransport.h new file mode 100644 index 0000000000..d6cb1a5dca --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientTransport.h @@ -0,0 +1,54 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * TcpClientTransport.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#pragma once + +#include +#include "TcpTransport.h" +#include "TcpClientStream.h" + +namespace Hosted +{ +namespace Transport +{ +class TcpClientTransport : public TcpTransport +{ +public: + TcpClientTransport(TcpClient& client) + { + client.setReceiveDelegate(TcpClientDataDelegate(&TcpClientTransport::process, this)); + stream = new TcpClientStream(client); + } + + ~TcpClientTransport() + { + delete stream; + } + +protected: + bool process(TcpClient& client, char* data, int size) override + { + if(!stream.push(data, size)) { + return false; + } + + return handler(*stream); + } + +private: + TcpClientStream* stream = nullptr; +}; + +} // namespace Transport + +} // namespace Hosted diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h new file mode 100644 index 0000000000..6e85cafc43 --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h @@ -0,0 +1,60 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * TcpServerTransport.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#pragma once + +#include +#include +#include "TcpTransport.h" +#include "TcpClientStream.h" + +namespace Hosted +{ +namespace Transport +{ +class TcpServerTransport : public TcpTransport +{ +public: + using ClientMap = ObjectMap; + + TcpServerTransport(TcpServer& server) + { + server.setClientReceiveHandler(TcpClientDataDelegate(&TcpServerTransport::process, this)); + } + +protected: + bool process(TcpClient& client, char* data, int size) override + { + auto key = &client; + + TcpClientStream* stream = map.find(key); + if(stream == nullptr) { + map[key] = stream = new TcpClientStream(client); + client.setReceiveDelegate(TcpClientDataDelegate(&TcpServerTransport::process, this)); + stream = map[key]; + } + + if(!stream->push(reinterpret_cast(data), size)) { + return false; + } + + return handler(*stream); + } + +private: + ClientMap map; +}; + +} // namespace Transport + +} // namespace Hosted diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpTransport.h new file mode 100644 index 0000000000..9553430592 --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpTransport.h @@ -0,0 +1,31 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * TcpTransport.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#pragma once + +#include +#include "BaseTransport.h" + +namespace Hosted +{ +namespace Transport +{ +class TcpTransport : public BaseTransport +{ +protected: + virtual bool process(TcpClient& client, char* data, int size) = 0; +}; + +} // namespace Transport + +} // namespace Hosted diff --git a/Sming/Components/Hosted/init/serial/InitClient.cpp b/Sming/Components/Hosted/init/serial/InitClient.cpp new file mode 100644 index 0000000000..cb9d2b2745 --- /dev/null +++ b/Sming/Components/Hosted/init/serial/InitClient.cpp @@ -0,0 +1,32 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * InitClient.cpp + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#include +#include + +Hosted::Client* hostedClient{nullptr}; + +extern void init(); + +extern "C" { +void __real_host_init(); +void __wrap_host_init(); +} + +void __wrap_host_init() +{ + Serial.begin(115200); + hostedClient = new Hosted::Client(Serial); + hostedClient->getRemoteCommands(); + init(); +} diff --git a/Sming/Components/Hosted/init/tcp/InitClient.cpp b/Sming/Components/Hosted/init/tcp/InitClient.cpp new file mode 100644 index 0000000000..04a71dc3c1 --- /dev/null +++ b/Sming/Components/Hosted/init/tcp/InitClient.cpp @@ -0,0 +1,73 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * InitClient.cpp + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#include +#include +#include + +Hosted::Client* hostedClient{nullptr}; + +#ifndef WIFI_SSID +#define WIFI_SSID "PleaseEnterSSID" // Put your SSID and password here +#define WIFI_PWD "PleaseEnterPass" +#endif + +#ifndef HOSTED_SERVER_IP +#define REMOTE_IP IpAddress("192.168.13.1") +#else +#define STRINGIFY_HELPER(X) #X +#define STRINGIFY(X) STRINGIFY_HELPER(X) +#define REMOTE_IP STRINGIFY(HOSTED_SERVER_IP) +#endif + +extern "C" { +void __real_host_init(); +void __wrap_host_init(); +} + +extern void init(); + +namespace +{ +TcpClient* tcpClient = nullptr; +Hosted::Transport::TcpClientStream* stream = nullptr; + +static void ready(IpAddress ip, IpAddress mask, IpAddress gateway) +{ + if(hostedClient != nullptr) { + return; + } + + IpAddress remoteIp(REMOTE_IP); + if(gateway == IpAddress("192.168.4.1")) { + remoteIp = gateway; + } + + tcpClient = new TcpClient(false); + tcpClient->connect(remoteIp, 4031); + stream = new Hosted::Transport::TcpClientStream(*tcpClient); + + hostedClient = new Hosted::Client(*stream); + hostedClient->getRemoteCommands(); + init(); +} + +} // namespace + +void __wrap_host_init() +{ + WifiEvents.onStationGotIP(ready); + WifiStation.enable(true); + WifiStation.config(_F(WIFI_SSID), _F(WIFI_PWD)); + WifiStation.connect(); +} diff --git a/Sming/Components/Hosted/samples/serial/Makefile b/Sming/Components/Hosted/samples/serial/Makefile new file mode 100644 index 0000000000..ff51b6c3a7 --- /dev/null +++ b/Sming/Components/Hosted/samples/serial/Makefile @@ -0,0 +1,9 @@ +##################################################################### +#### Please don't change this file. Use component.mk instead #### +##################################################################### + +ifndef SMING_HOME +$(error SMING_HOME is not set: please configure it as an environment variable) +endif + +include $(SMING_HOME)/project.mk diff --git a/Sming/Components/Hosted/samples/serial/README.rst b/Sming/Components/Hosted/samples/serial/README.rst new file mode 100644 index 0000000000..d1635d59fb --- /dev/null +++ b/Sming/Components/Hosted/samples/serial/README.rst @@ -0,0 +1,9 @@ +Hosted RCP Server over Serial +============================= + +Overview +-------- +This application creates a RPC server that will communicate over serial interface. To compile it for an Esp8266 microcontroller you can +use the following command:: + + make SMING_ARCH=Esp8266 diff --git a/Sming/Components/Hosted/samples/serial/app/application.cpp b/Sming/Components/Hosted/samples/serial/app/application.cpp new file mode 100644 index 0000000000..ba70d7576f --- /dev/null +++ b/Sming/Components/Hosted/samples/serial/app/application.cpp @@ -0,0 +1,36 @@ +/* + * This sample application demostrates RPC communication via the serial interface + */ +#include +#include +#include + +using namespace Hosted::Transport; + +SerialTransport* transport = nullptr; + +void init() +{ + Serial.begin(SERIAL_BAUD_RATE); + + transport = new SerialTransport(Serial); + transport->onData([](Stream& stream) { + // clang-format off + interface(stream, + /* + * Below we are exporting the following remote commands: + * - pinMode + * - digitalRead + * - digitalWrite + * You can add more commands here. For every command you should specify command and text description in the format below. + * For more information read the SimpleRPC interface API: https://simplerpc.readthedocs.io/en/latest/api/interface.html + */ + pinMode, F("pinMode: Sets mode of digital pin. @pin: Pin number, @mode: Mode type."), + digitalRead, F("digitalRead: Read digital pin. @pin: Pin number. @return: Pin value."), + digitalWrite, F("digitalWrite: Write to a digital pin. @pin: Pin number. @value: Pin value.") + ); + // clang-format on + + return true; + }); +} diff --git a/Sming/Components/Hosted/samples/serial/component.mk b/Sming/Components/Hosted/samples/serial/component.mk new file mode 100644 index 0000000000..a2140118b1 --- /dev/null +++ b/Sming/Components/Hosted/samples/serial/component.mk @@ -0,0 +1,9 @@ +## This sample depends on the HostEd component +COMPONENT_DEPENDS := Hosted +ENABLE_HOSTED := + +## And does not require WIFI +DISABLE_WIFI := 1 + +ENABLE_HOST_UARTID := 0 +HOST_NETWORK_OPTIONS := --pause \ No newline at end of file diff --git a/Sming/Components/Hosted/samples/tcp/Makefile b/Sming/Components/Hosted/samples/tcp/Makefile new file mode 100644 index 0000000000..ff51b6c3a7 --- /dev/null +++ b/Sming/Components/Hosted/samples/tcp/Makefile @@ -0,0 +1,9 @@ +##################################################################### +#### Please don't change this file. Use component.mk instead #### +##################################################################### + +ifndef SMING_HOME +$(error SMING_HOME is not set: please configure it as an environment variable) +endif + +include $(SMING_HOME)/project.mk diff --git a/Sming/Components/Hosted/samples/tcp/README.rst b/Sming/Components/Hosted/samples/tcp/README.rst new file mode 100644 index 0000000000..308e5757fb --- /dev/null +++ b/Sming/Components/Hosted/samples/tcp/README.rst @@ -0,0 +1,32 @@ +Hosted RCP Server over TCP +========================== + +Overview +-------- +This application creates a RPC server that will communicate over TCP. You can either start an Access Point from the controller +or connect the application to an existing WIFI Access point. The latter can be compiled using the following command:: + + make SMING_ARCH=Esp8266 CONNECT_TO_WIFI=1 WIFI_SSID="MySSID" WIFI_PWD="Secr3tP4Ssw0rd" + +Configuration +------------- + +.. envvar:: CONNECT_TO_WIFI + + Default: 0 (disabled) + + If set to 1 the application will try to connect to a WIFI access point. Make sure to provide also the WIFI_SSID and WIFI_PWD values. + + If set to 0 the application will start an access point to which the Host application can connect. + +.. envvar:: WIFI_SSID + + Default: PleaseEnterSSID + + WIFI Access Point name. If you have enabled CONNECT_TO_WIFI then make sure to set also WIFI_SSID and WIFI_PWD values. + +.. envvar:: WIFI_PWD + + Default: PleaseEnterPass + + WIFI Access Point password. If you have enabled CONNECT_TO_WIFI then make sure to set also WIFI_SSID and WIFI_PWD values. diff --git a/Sming/Components/Hosted/samples/tcp/app/application.cpp b/Sming/Components/Hosted/samples/tcp/app/application.cpp new file mode 100644 index 0000000000..2e3a467e83 --- /dev/null +++ b/Sming/Components/Hosted/samples/tcp/app/application.cpp @@ -0,0 +1,79 @@ +/* + * This sample application demostrates RPC communication via TCP. + * It will try to connect to create an existing Access Point (AP) or create to a new AP and start a TCP server. + * The TCP server will listen on port 4031 for remote commands. + */ +#include +#include +#include + +#if CONNECT_TO_WIFI +// If you want, you can define WiFi settings globally in Eclipse Environment Variables +#ifndef WIFI_SSID +#define WIFI_SSID "PleaseEnterSSID" // Put your SSID and password here +#define WIFI_PWD "PleaseEnterPass" +#endif +#endif /* CONNECT_TO_WIFI */ + +namespace +{ +constexpr size_t port = 4031; + +using namespace Hosted::Transport; + +TcpServer* server; +TcpServerTransport* transport; + +// Will be called when WiFi station was connected to AP +void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) +{ + if(server != nullptr) { + return; + } + + server = new TcpServer(); + server->listen(port); + server->setTimeOut(USHRT_MAX); // disable connection timeout + + transport = new TcpServerTransport(*server); + transport->onData([](Stream& stream) { + // clang-format off + interface(stream, + /* + * Below we are exporting the following remote commands: + * - pinMode + * - digitalRead + * - digitalWrite + * You can add more commands here. For every command you should specify command and text description in the format below. + * For more information read the SimpleRPC interface API: https://simplerpc.readthedocs.io/en/latest/api/interface.html + */ + pinMode, F("pinMode: Sets mode of digital pin. @pin: Pin number, @mode: Mode type."), + digitalRead, F("digitalRead: Read digital pin. @pin: Pin number. @return: Pin value."), + digitalWrite, F("digitalWrite: Write to a digital pin. @pin: Pin number. @value: Pin value.") + ); + // clang-format on + + return true; + }); + + Serial.printf("Running RCP server on: %s:%u", ip.toString().c_str(), port); +} + +} // namespace + +void init() +{ + Serial.begin(SERIAL_BAUD_RATE); + +#if CONNECT_TO_WIFI + // Connect to same AP as the client application + WifiStation.enable(true); + WifiStation.config(_F(WIFI_SSID), _F(WIFI_PWD)); + // Set callback that should be triggered when we have assigned IP + WifiEvents.onStationGotIP(connectOk); +#else + WifiAccessPoint.enable(true); + WifiAccessPoint.config(_F("RCP Server"), nullptr, AUTH_OPEN); + connectOk(WifiAccessPoint.getIP(), WifiAccessPoint.getNetworkMask(), WifiAccessPoint.getNetworkGateway()); +#endif +} diff --git a/Sming/Components/Hosted/samples/tcp/component.mk b/Sming/Components/Hosted/samples/tcp/component.mk new file mode 100644 index 0000000000..a3b186f112 --- /dev/null +++ b/Sming/Components/Hosted/samples/tcp/component.mk @@ -0,0 +1,10 @@ +## This sample depends on the HostEd component +COMPONENT_DEPENDS := Hosted +ENABLE_HOSTED := + +# If set the application should connect to a WIFI access point +# otherwise it will set its own access point +COMPONENT_VARS := CONNECT_TO_WIFI +CONNECT_TO_WIFI ?= 0 + +APP_CFLAGS = -DCONNECT_TO_WIFI=$(CONNECT_TO_WIFI) diff --git a/Sming/Core/Network/MqttClient.cpp b/Sming/Core/Network/MqttClient.cpp index 1647272fce..349095d718 100644 --- a/Sming/Core/Network/MqttClient.cpp +++ b/Sming/Core/Network/MqttClient.cpp @@ -264,7 +264,7 @@ bool MqttClient::publish(const String& topic, const String& content, uint8_t fla if(success) { // Try to force-send message to decrease latency. // Should work for small size messages but there is no guarantee. - onReadyToSendData(TcpConnectionEvent::eTCE_Poll); + commit(); } return success; @@ -300,7 +300,7 @@ bool MqttClient::publish(const String& topic, IDataSourceStream* stream, uint8_t if(success) { // Try to force-send message to decrease latency. // Should work for small size messages but there is no guarantee. - onReadyToSendData(TcpConnectionEvent::eTCE_Poll); + commit(); } return success; diff --git a/Sming/Core/Network/TcpClient.h b/Sming/Core/Network/TcpClient.h index 71789f7b54..9be0e46cde 100644 --- a/Sming/Core/Network/TcpClient.h +++ b/Sming/Core/Network/TcpClient.h @@ -126,6 +126,16 @@ class TcpClient : public TcpConnection closeAfterSent = ignoreIncomingData ? eTCCASS_AfterSent_Ignore_Received : eTCCASS_AfterSent; } + /** + * @brief Tries to send the pending data immediately. + * @note Call this method to decrease latency. Use it carefully. + */ + void commit() + { + onReadyToSendData(TcpConnectionEvent::eTCE_Poll); + TcpConnection::flush(); + } + protected: err_t onConnected(err_t err) override; err_t onReceive(pbuf* buf) override; diff --git a/Sming/Core/Network/TcpServer.h b/Sming/Core/Network/TcpServer.h index 2d28d25e21..15b362d269 100644 --- a/Sming/Core/Network/TcpServer.h +++ b/Sming/Core/Network/TcpServer.h @@ -56,6 +56,11 @@ class TcpServer : public TcpConnection TcpConnection::timeOut = TCP_SERVER_TIMEOUT; } + void setClientReceiveHandler(TcpClientDataDelegate clientReceiveDataHandler) + { + clientReceiveDelegate = clientReceiveDataHandler; + } + ~TcpServer() { debug_i("TcpServer destroyed"); diff --git a/Sming/Libraries/simpleRPC/component.mk b/Sming/Libraries/simpleRPC/component.mk new file mode 100644 index 0000000000..4fd217ca80 --- /dev/null +++ b/Sming/Libraries/simpleRPC/component.mk @@ -0,0 +1,6 @@ +COMPONENT_SUBMODULES += simpleRPC + +SIMPLE_RPC_ROOT := $(COMPONENT_PATH)/simpleRPC + +COMPONENT_SRCDIRS := $(SIMPLE_RPC_ROOT)/src $(COMPONENT_PATH)/src +COMPONENT_INCDIRS := $(COMPONENT_SRCDIRS) $(COMPONENT_PATH)/include diff --git a/Sming/Libraries/simpleRPC/include/simpleRPC/.cs b/Sming/Libraries/simpleRPC/include/simpleRPC/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Libraries/simpleRPC/include/simpleRPC/parser.h b/Sming/Libraries/simpleRPC/include/simpleRPC/parser.h new file mode 100644 index 0000000000..a1ce2e336e --- /dev/null +++ b/Sming/Libraries/simpleRPC/include/simpleRPC/parser.h @@ -0,0 +1,63 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * parser.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#pragma once + +#include + +namespace simpleRPC +{ +enum class ParserResult { finished = 0, more, error }; + +enum class ParserState { + ready = 0, + header_s, + header_si, + header_sim, + header_simp, + header_simpl, + header_simple, + header_simpleR, + header_simpleRP, + header_simpleRPC, + header_end, + version_major, + version_minor, + version_patch, + ness, + type, + type_end, + start_methods, + extract_method_start, + extract_method_name, + extract_method, + extract_method_end, + end_methods, + finished +}; + +struct ParserSettings { + using SimpleMethod = Delegate; + using CharMethod = Delegate; + + SimpleMethod startMethods; + SimpleMethod startMethod; + CharMethod methodName; + SimpleMethod endMethod; + SimpleMethod endMethods; + ParserState state = ParserState::ready; +}; + +ParserResult parse(ParserSettings& settings, const char* buffer, size_t length); + +} // namespace simpleRPC diff --git a/Sming/Libraries/simpleRPC/simpleRPC b/Sming/Libraries/simpleRPC/simpleRPC new file mode 160000 index 0000000000..5696f34c42 --- /dev/null +++ b/Sming/Libraries/simpleRPC/simpleRPC @@ -0,0 +1 @@ +Subproject commit 5696f34c429c4e87721ebdb191b21c2ea0f23791 diff --git a/Sming/Libraries/simpleRPC/simpleRPC.patch b/Sming/Libraries/simpleRPC/simpleRPC.patch new file mode 100644 index 0000000000..ba6edc8500 --- /dev/null +++ b/Sming/Libraries/simpleRPC/simpleRPC.patch @@ -0,0 +1,65 @@ +diff --git a/src/read.tcc b/src/read.tcc +index 2e5810f..513d691 100644 +--- a/src/read.tcc ++++ b/src/read.tcc +@@ -3,7 +3,6 @@ + + #include "defs.h" + #include "tuple.tcc" +-#include "vector.tcc" + + //! \defgroup read + +diff --git a/src/signature.tcc b/src/signature.tcc +index 00f5eb1..f3a675d 100644 +--- a/src/signature.tcc ++++ b/src/signature.tcc +@@ -25,7 +25,7 @@ void _parameterTypes(Stream& io, void (*f_)(H, Tail...)) { + * `rpcTypeOf()` to encode its type. The first parameter type `H` is removed + * from function pointer `*f_` in the recursive call. + */ +- H data; ++ H data{}; + + rpcPrint(io, ' '); + rpcTypeOf(io, data); +@@ -49,13 +49,13 @@ void _parameterTypes(Stream& io, void (*f_)(H&, Tail...)) { + */ + template + void signature(Stream& io, R (*f)(FArgs...)) { +- /* ++ /* + * A dummy function pointer is prepared, referred to as `f_` in the template + * functions above, which will be used to isolate parameter types. The return + * type of this function pointer is removed to avoid unneeded template + * expansion. + */ +- R data; ++ R data{}; + + rpcTypeOf(io, data); + rpcPrint(io, ':'); +diff --git a/src/types.tcc b/src/types.tcc +index 651fb38..8a7cfa5 100644 +--- a/src/types.tcc ++++ b/src/types.tcc +@@ -3,7 +3,6 @@ + + #include "print.tcc" + #include "tuple.tcc" +-#include "vector.tcc" + + //! \defgroup types + +diff --git a/src/write.tcc b/src/write.tcc +index d51cdd1..abf1e7b 100644 +--- a/src/write.tcc ++++ b/src/write.tcc +@@ -3,7 +3,6 @@ + + #include "print.tcc" + #include "tuple.tcc" +-#include "vector.tcc" + + //! \defgroup write + diff --git a/Sming/Libraries/simpleRPC/src/.cs b/Sming/Libraries/simpleRPC/src/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Libraries/simpleRPC/src/parser.cpp b/Sming/Libraries/simpleRPC/src/parser.cpp new file mode 100644 index 0000000000..7be4f8183c --- /dev/null +++ b/Sming/Libraries/simpleRPC/src/parser.cpp @@ -0,0 +1,195 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * parser.cpp + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#include "../include/simpleRPC/parser.h" + +namespace simpleRPC +{ +#define EXPECT(X, NEW_STATE) \ + { \ + if(ch != X) { \ + hasError = true; \ + goto ERROR; \ + } \ + state = NEW_STATE; \ + break; \ + } + +#define SKIP_UNTIL(X, NEW_STATE) \ + { \ + if(ch != X) { \ + break; \ + } \ + state = NEW_STATE; \ + } + +ParserResult parse(ParserSettings& settings, const char* buffer, size_t length) +{ + auto& state = settings.state; + /* + * See: https://simplerpc.readthedocs.io/en/latest/protocol.html# +00000000 ff . + 00000000 73 s + 00000001 69 i + 00000002 6d m + 00000003 70 p + 00000004 6c l + 00000005 65 e + 00000006 52 R + 00000007 50 P + 00000008 43 C + 00000009 00 . + 0000000A 03 00 00 ... + 3c < + 49 I + 00 . + 3a 20 : + 3a 20 48 20 42 3b 70 69 6e 4d ... + +#include +#include +#include +#include +#include +#include + +using namespace simpleRPC; + +static uint32_t plusCommand(uint8_t a, uint16_t b) +{ + return a + b; +}; + +class HostedTest : public TestGroup +{ +public: + using RemoteCommands = HashMap; + + HostedTest() : TestGroup(_F("Hosted")) + { + } + + void execute() override + { + char packet[] = { + 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x50, 0x43, 0x00, 0x03, 0x00, 0x00, 0x3c, 0x49, 0x00, 0x3a, 0x20, + 0x48, 0x20, 0x42, 0x3b, 0x70, 0x69, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x53, 0x65, 0x74, 0x73, 0x20, + 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x64, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x70, 0x69, + 0x6e, 0x2e, 0x20, 0x40, 0x70, 0x69, 0x6e, 0x3a, 0x20, 0x50, 0x69, 0x6e, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x2c, 0x20, 0x40, 0x6d, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4d, 0x6f, 0x64, 0x65, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x2e, 0x00, 0x42, 0x3a, 0x20, 0x48, 0x3b, 0x64, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, + 0x64, 0x3a, 0x20, 0x52, 0x65, 0x61, 0x64, 0x20, 0x64, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x70, 0x69, + 0x6e, 0x2e, 0x20, 0x40, 0x70, 0x69, 0x6e, 0x3a, 0x20, 0x50, 0x69, 0x6e, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x2e, 0x20, 0x40, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3a, 0x20, 0x50, 0x69, 0x6e, 0x20, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x2e, 0x00, 0x3a, 0x20, 0x48, 0x20, 0x42, 0x3b, 0x64, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x3a, 0x20, 0x57, 0x72, 0x69, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, + 0x64, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x70, 0x69, 0x6e, 0x2e, 0x20, 0x40, 0x70, 0x69, 0x6e, 0x3a, + 0x20, 0x50, 0x69, 0x6e, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2e, 0x20, 0x40, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x20, 0x50, 0x69, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x00, 0x00}; + + ParserSettings settings; + settings.startMethods = ParserSettings::SimpleMethod(&HostedTest::startMethods, this); + settings.startMethod = ParserSettings::SimpleMethod(&HostedTest::startMethod, this); + settings.methodName = ParserSettings::CharMethod(&HostedTest::methodName, this); + settings.endMethod = ParserSettings::SimpleMethod(&HostedTest::endMethod, this); + settings.endMethods = ParserSettings::SimpleMethod(&HostedTest::endMethods, this); + settings.state = ParserState::ready; + + TEST_CASE("simpleRPC::parse()") + { + REQUIRE(parse(settings, packet, sizeof(packet)) == ParserResult::finished); + REQUIRE(commands.count() == 3); + REQUIRE(commands["digitalWrite"] == 2); + REQUIRE(commands["pinMode"] != 2); + REQUIRE(commands["pinMode"] == 0); + } + + if(!WifiStation.isConnected()) { + Serial.println("No network, skipping tests"); + return; + } + + // RPC Server + server = new TcpServer(); + server->listen(4031); + server->setTimeOut(USHRT_MAX); // disable connection timeout + server->setKeepAlive(USHRT_MAX); // disable connection timeout + + Hosted::Transport::TcpServerTransport transport(*server); + transport.onData([](Stream& stream) { + // clang-format off + interface(stream, + /* + * Below we are exporting the following remote commands: + * - pinMode + * - digitalRead + * - digitalWrite + * You can add more commands here. For every command you should specify command and text description in the format below. + * For more information read the SimpleRPC interface API: https://simplerpc.readthedocs.io/en/latest/api/interface.html + */ + pinMode, "pinMode: Sets mode of digital pin. @pin: Pin number, @mode: Mode type.", + digitalRead, "digitalRead: Read digital pin. @pin: Pin number. @return: Pin value.", + plusCommand, "plusCommand: Sum two numbers. @a: number one. @b: number two." + ); + // clang-format on + + return true; + }); + + // RCP Client + + client.connect(WifiStation.getIP(), 4031); + Hosted::Transport::TcpClientStream stream(client, 1024); + + Hosted::Client hostedClient(stream); + + TEST_CASE("Client::getRemoteCommands()") + { + REQUIRE(hostedClient.getRemoteCommands() == true); + REQUIRE(hostedClient.getFunctionId("plusCommand") == 2); + } + + TEST_CASE("Client::send and wait()") + { + ElapseTimer timer; + + REQUIRE(hostedClient.send("plusCommand", uint8_t(3), uint16_t(2)) == true); + REQUIRE(hostedClient.wait() == 5); + + debug_i("PlusCommand Roundtrip Time: %s", timer.elapsedTime().toString().c_str()); + } + } + +private: + RemoteCommands commands; + uint8_t methodPosition = 0; + String parsedCommand; + + TcpServer* server{nullptr}; + TcpClient client{false}; + Hosted::Transport::TcpClientStream* stream{nullptr}; + + void startMethods() + { + methodPosition = 0; + commands.clear(); + } + + void startMethod() + { + parsedCommand = ""; + } + + void methodName(char ch) + { + parsedCommand += ch; + } + + void endMethod() + { + commands[parsedCommand] = methodPosition++; + } + + void endMethods() + { + } +}; + +void REGISTER_TEST(Hosted) +{ + registerGroup(); +} From 7dd729f4b061e518db8d0fea33ef22586fe35516 Mon Sep 17 00:00:00 2001 From: slaff Date: Wed, 21 Apr 2021 08:45:11 +0200 Subject: [PATCH 009/130] Fix travis builds. (#2311) Co-authored-by: mikee47 --- .travis.yml | 7 ++++++- Sming/Arch/Host/Tools/travis/install.sh | 2 -- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7c56d90a9e..4b5249e996 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ jobs: apt: packages: - clang-format-8 - - gcc-multilib + - linux-libc-dev:i386 - g++-9-multilib - doxygen - python3-sphinx @@ -30,6 +30,11 @@ jobs: - SMING_ARCH=Host - stage: build name: C++17 + addons: + apt: + packages: + - linux-libc-dev:i386 + - g++-9-multilib env: - SMING_ARCH=Esp8266 - SDK_VERSION=3.0.1 diff --git a/Sming/Arch/Host/Tools/travis/install.sh b/Sming/Arch/Host/Tools/travis/install.sh index 1fcf81b168..70495ca431 100755 --- a/Sming/Arch/Host/Tools/travis/install.sh +++ b/Sming/Arch/Host/Tools/travis/install.sh @@ -1,8 +1,6 @@ #!/bin/bash set -ex # exit with nonzero exit code if anything fails -sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 60 --slave /usr/bin/g++ g++ /usr/bin/g++-9 - sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-8 200 sudo rm -f /usr/local/clang-7.0.0/bin/clang-format From 0664104c0a06928420df4acebcae347f6276ec02 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 22 Apr 2021 09:31:05 +0100 Subject: [PATCH 010/130] Move Spiffs into a separate library. (#2313) * Move IFS code into SPIFFS Component * Move spiffs into Libraries --- .gitmodules | 8 +- Sming/Components/IFS | 2 +- Sming/Components/spiffs/spiffs_sming.cpp | 46 -- Sming/Core/FileSystem.cpp | 26 - Sming/Core/FileSystem.h | 24 +- .../spiffs => Libraries/Spiffs}/README.rst | 11 +- .../spiffs => Libraries/Spiffs}/build.json | 0 .../spiffs => Libraries/Spiffs}/component.mk | 11 +- .../spiffs => Libraries/Spiffs}/spiffs | 0 .../spiffs => Libraries/Spiffs}/spiffs.patch | 0 .../spiffs => Libraries/Spiffs}/spiffsgen.py | 0 Sming/Libraries/Spiffs/src/Error.cpp | 107 +++ Sming/Libraries/Spiffs/src/FileSystem.cpp | 769 ++++++++++++++++++ Sming/Libraries/Spiffs/src/Spiffs.cpp | 93 +++ .../Spiffs/src/include/IFS/SPIFFS/Error.h | 38 + .../Spiffs/src/include/IFS/SPIFFS/FileMeta.h | 265 ++++++ .../src/include/IFS/SPIFFS/FileSystem.h | 149 ++++ .../Spiffs/src/include/Spiffs.h} | 39 +- .../Spiffs/src/include}/spiffs_config.h | 0 Sming/component.mk | 2 +- tests/HostTests/modules/Spiffs.cpp | 14 +- 21 files changed, 1485 insertions(+), 119 deletions(-) delete mode 100644 Sming/Components/spiffs/spiffs_sming.cpp rename Sming/{Components/spiffs => Libraries/Spiffs}/README.rst (80%) rename Sming/{Components/spiffs => Libraries/Spiffs}/build.json (100%) rename Sming/{Components/spiffs => Libraries/Spiffs}/component.mk (81%) rename Sming/{Components/spiffs => Libraries/Spiffs}/spiffs (100%) rename Sming/{Components/spiffs => Libraries/Spiffs}/spiffs.patch (100%) rename Sming/{Components/spiffs => Libraries/Spiffs}/spiffsgen.py (100%) create mode 100644 Sming/Libraries/Spiffs/src/Error.cpp create mode 100644 Sming/Libraries/Spiffs/src/FileSystem.cpp create mode 100644 Sming/Libraries/Spiffs/src/Spiffs.cpp create mode 100644 Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/Error.h create mode 100644 Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileMeta.h create mode 100644 Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileSystem.h rename Sming/{Components/spiffs/spiffs_sming.h => Libraries/Spiffs/src/include/Spiffs.h} (50%) rename Sming/{Components/spiffs => Libraries/Spiffs/src/include}/spiffs_config.h (100%) diff --git a/.gitmodules b/.gitmodules index eb585627f1..1f60c88332 100644 --- a/.gitmodules +++ b/.gitmodules @@ -65,10 +65,6 @@ path = Sming/Components/rboot/rboot url = https://github.com/mikee47/rboot ignore = dirty -[submodule "spiffs"] - path = Sming/Components/spiffs/spiffs - url = https://github.com/pellepl/spiffs.git - ignore = dirty [submodule "ws_parser"] path = Sming/Components/ws_parser url = https://github.com/charliesome/ws_parser.git @@ -275,6 +271,10 @@ path = Sming/Libraries/SolarCalculator url = https://github.com/mikee47/SolarCalculator ignore = dirty +[submodule "spiffs"] + path = Sming/Libraries/Spiffs/spiffs + url = https://github.com/pellepl/spiffs.git + ignore = dirty [submodule "Libraries.SSDP"] path = Sming/Libraries/SSDP url = https://github.com/mikee47/Sming-SSDP diff --git a/Sming/Components/IFS b/Sming/Components/IFS index 0886783d41..57f93bf87d 160000 --- a/Sming/Components/IFS +++ b/Sming/Components/IFS @@ -1 +1 @@ -Subproject commit 0886783d41b38e716948a10bcd13a4f2069a9c31 +Subproject commit 57f93bf87dc18e61733b0db1acb59da4ef709dcb diff --git a/Sming/Components/spiffs/spiffs_sming.cpp b/Sming/Components/spiffs/spiffs_sming.cpp deleted file mode 100644 index def31d36b6..0000000000 --- a/Sming/Components/spiffs/spiffs_sming.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * spiffs_sming.cpp - * - ****/ - -#include "spiffs_sming.h" -#include -#include -#include - -void spiffs_unmount() -{ - if(fileSystemType() == IFS::IFileSystem::Type::SPIFFS) { - fileFreeFileSystem(); - } -} - -bool spiffs_format() -{ - if(fileSystemType() != IFS::IFileSystem::Type::SPIFFS) { - return false; - } - return fileSystemFormat() == FS_OK; -} - -bool spiffs_format(Storage::Partition& partition) -{ - if(fileSystemType() == IFS::IFileSystem::Type::SPIFFS) { - fileFreeFileSystem(); - } - auto fs = new IFS::SPIFFS::FileSystem(partition); - int err = fs->format(); - if(err < 0) { - debug_e("SPIFFS format failed: %s", fs->getErrorString(err).c_str()); - delete fs; - return false; - } - - fileSetFileSystem(fs); - return true; -} diff --git a/Sming/Core/FileSystem.cpp b/Sming/Core/FileSystem.cpp index 89764a1fcf..7b10808d0e 100644 --- a/Sming/Core/FileSystem.cpp +++ b/Sming/Core/FileSystem.cpp @@ -46,18 +46,6 @@ bool fileMountFileSystem(IFS::IFileSystem* fs) return true; } -bool spiffs_mount() -{ - auto part = Storage::findDefaultPartition(Storage::Partition::SubType::Data::spiffs); - return part ? spiffs_mount(part) : false; -} - -bool spiffs_mount(Storage::Partition partition) -{ - auto fs = IFS::createSpiffsFilesystem(partition); - return fileMountFileSystem(fs); -} - bool fwfs_mount() { auto part = Storage::findDefaultPartition(Storage::Partition::SubType::Data::fwfs); @@ -70,20 +58,6 @@ bool fwfs_mount(Storage::Partition partition) return fileMountFileSystem(fs); } -bool hyfs_mount() -{ - auto fwfsPart = Storage::findDefaultPartition(Storage::Partition::SubType::Data::fwfs); - auto spiffsPart = Storage::findDefaultPartition(Storage::Partition::SubType::Data::spiffs); - return (fwfsPart && spiffsPart) ? hyfs_mount(fwfsPart, spiffsPart) : false; -} - -bool hyfs_mount(Storage::Partition fwfsPartition, Storage::Partition spiffsPartition) -{ - auto ffs = IFS::createSpiffsFilesystem(spiffsPartition); - auto fs = IFS::createHybridFilesystem(fwfsPartition, ffs); - return fileMountFileSystem(fs); -} - Vector fileList() { Vector result; diff --git a/Sming/Core/FileSystem.h b/Sming/Core/FileSystem.h index 78b9f3b820..ff4c72f4af 100644 --- a/Sming/Core/FileSystem.h +++ b/Sming/Core/FileSystem.h @@ -18,6 +18,7 @@ #include #include #include +#include #include "WVector.h" ///< @deprecated see fileList() using file_t = IFS::FileHandle; @@ -121,17 +122,6 @@ inline void fileFreeFileSystem() */ bool fileMountFileSystem(IFS::IFileSystem* fs); -/** - * @brief Mount the first available SPIFFS volume - * @retval bool true on success - */ -bool spiffs_mount(); - -/** - * @brief Mount SPIFFS volume from a specific partition - */ -bool spiffs_mount(Storage::Partition partition); - /** * @brief Mount the first available FWFS volume * @retval bool true on success @@ -144,18 +134,6 @@ bool fwfs_mount(); */ bool fwfs_mount(Storage::Partition partition); -/** - * @brief Mount the first available FWFS and SPIFFS partitions as a hybrid filesystem - * @retval bool true on success - */ -bool hyfs_mount(); - -/** - * @brief Mount the given FWFS and SPIFFS partitions as a hybrid filesystem - * @retval bool true on success - */ -bool hyfs_mount(Storage::Partition fwfsPartition, Storage::Partition spiffsPartition); - /** @brief Open file by path * @param path Full path to file * @param flags Mode to open file diff --git a/Sming/Components/spiffs/README.rst b/Sming/Libraries/Spiffs/README.rst similarity index 80% rename from Sming/Components/spiffs/README.rst rename to Sming/Libraries/Spiffs/README.rst index 3ac089846f..4e50a7c99d 100644 --- a/Sming/Components/spiffs/README.rst +++ b/Sming/Libraries/Spiffs/README.rst @@ -1,5 +1,5 @@ -SPIFFS for Sming -================ +SPIFFS IFS Library +================== This Component provides SPIFFS filesystem support for all architectures. @@ -59,3 +59,10 @@ custom :ref:`hardware_config`. If this value is changed, existing SPIFFS images will not be readable. The default value given here is provided to support :component:`IFS` extended file attribute information. + + The first 16 bytes are used for system attributes (e.g. modified time), so setting this to, say, 64 + leaves 48 bytes for user metadata. Each attribute has a 2-byte header (tag + size) so a single user + attribute can be stored of up to 46 bytes, or multiple tags up to this limit. + + Note: :library:`LittleFS` provides better support for user metadata. + diff --git a/Sming/Components/spiffs/build.json b/Sming/Libraries/Spiffs/build.json similarity index 100% rename from Sming/Components/spiffs/build.json rename to Sming/Libraries/Spiffs/build.json diff --git a/Sming/Components/spiffs/component.mk b/Sming/Libraries/Spiffs/component.mk similarity index 81% rename from Sming/Components/spiffs/component.mk rename to Sming/Libraries/Spiffs/component.mk index fb0ce44d3c..946794670c 100644 --- a/Sming/Components/spiffs/component.mk +++ b/Sming/Libraries/Spiffs/component.mk @@ -1,7 +1,7 @@ ## SPIFFS library COMPONENT_SUBMODULES := spiffs -COMPONENT_SRCDIRS := . spiffs/src -COMPONENT_INCDIRS := . spiffs/src +COMPONENT_SRCDIRS := src spiffs/src +COMPONENT_INCDIRS := src/include COMPONENT_DOXYGEN_INPUT := spiffs/src ## Application @@ -23,9 +23,10 @@ COMPONENT_CFLAGS += -DSPIFF_FILEDESC_COUNT=$(SPIFF_FILEDESC_COUNT) COMPONENT_CFLAGS += -Wno-tautological-compare -COMPONENT_RELINK_VARS += SPIFFS_OBJ_META_LEN -SPIFFS_OBJ_META_LEN ?= 16 -COMPONENT_CFLAGS += -DSPIFFS_OBJ_META_LEN=$(SPIFFS_OBJ_META_LEN) +COMPONENT_RELINK_VARS += SPIFFS_OBJ_META_LEN +SPIFFS_OBJ_META_LEN ?= 16 +COMPONENT_CFLAGS += -DSPIFFS_OBJ_META_LEN=$(SPIFFS_OBJ_META_LEN) +COMPONENT_CXXFLAGS += -DSPIFFS_OBJ_META_LEN=$(SPIFFS_OBJ_META_LEN) ##@Building diff --git a/Sming/Components/spiffs/spiffs b/Sming/Libraries/Spiffs/spiffs similarity index 100% rename from Sming/Components/spiffs/spiffs rename to Sming/Libraries/Spiffs/spiffs diff --git a/Sming/Components/spiffs/spiffs.patch b/Sming/Libraries/Spiffs/spiffs.patch similarity index 100% rename from Sming/Components/spiffs/spiffs.patch rename to Sming/Libraries/Spiffs/spiffs.patch diff --git a/Sming/Components/spiffs/spiffsgen.py b/Sming/Libraries/Spiffs/spiffsgen.py similarity index 100% rename from Sming/Components/spiffs/spiffsgen.py rename to Sming/Libraries/Spiffs/spiffsgen.py diff --git a/Sming/Libraries/Spiffs/src/Error.cpp b/Sming/Libraries/Spiffs/src/Error.cpp new file mode 100644 index 0000000000..395957b744 --- /dev/null +++ b/Sming/Libraries/Spiffs/src/Error.cpp @@ -0,0 +1,107 @@ +/** + * Error.cpp + * SPIFFS error codes + * + * Created on: 31 Aug 2018 + * + * Copyright 2019 mikee47 + * + * This file is part of the SPIFFS IFS Library + * + * This library is free software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, version 3 or later. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this library. + * If not, see . + * + ****/ + +#include "include/IFS/SPIFFS/Error.h" +#include + +namespace IFS +{ +namespace SPIFFS +{ +/* + * @todo Return generic FSERR codes whereever possible by mapping from SPIFFS codes + */ + +#define SPIFFS_ERROR_MAP(XX) \ + XX(OK, 0) \ + XX(NOT_MOUNTED, -10000) \ + XX(FULL, -10001) \ + XX(NOT_FOUND, -10002) \ + XX(END_OF_OBJECT, -10003) \ + XX(DELETED, -10004) \ + XX(NOT_FINALIZED, -10005) \ + XX(NOT_INDEX, -10006) \ + XX(OUT_OF_FILE_DESCS, -10007) \ + XX(FILE_CLOSED, -10008) \ + XX(FILE_DELETED, -10009) \ + XX(BAD_DESCRIPTOR, -10010) \ + XX(IS_INDEX, -10011) \ + XX(IS_FREE, -10012) \ + XX(INDEX_SPAN_MISMATCH, -10013) \ + XX(DATA_SPAN_MISMATCH, -10014) \ + XX(INDEX_REF_FREE, -10015) \ + XX(INDEX_REF_LU, -10016) \ + XX(INDEX_REF_INVALID, -10017) \ + XX(INDEX_FREE, -10018) \ + XX(INDEX_LU, -10019) \ + XX(INDEX_INVALID, -10020) \ + XX(NOT_WRITABLE, -10021) \ + XX(NOT_READABLE, -10022) \ + XX(CONFLICTING_NAME, -10023) \ + XX(NOT_CONFIGURED, -10024) \ + \ + XX(NOT_A_FS, -10025) \ + XX(MOUNTED, -10026) \ + XX(ERASE_FAIL, -10027) \ + XX(MAGIC_NOT_POSSIBLE, -10028) \ + \ + XX(NO_DELETED_BLOCKS, -10029) \ + \ + XX(FILE_EXISTS, -10030) \ + \ + XX(NOT_A_FILE, -10031) \ + XX(RO_NOT_IMPL, -10032) \ + XX(RO_ABORTED_OPERATION, -10033) \ + XX(PROBE_TOO_FEW_BLOCKS, -10034) \ + XX(PROBE_NOT_A_FS, -10035) \ + XX(NAME_TOO_LONG, -10036) \ + \ + XX(IX_MAP_UNMAPPED, -10037) \ + XX(IX_MAP_MAPPED, -10038) \ + XX(IX_MAP_BAD_RANGE, -10039) \ + \ + XX(SEEK_BOUNDS, -10040) \ + \ + XX(INTERNAL, -10050) \ + \ + XX(TEST, -10100) + +struct spiffs_error_t { + int32_t err; + PGM_P tag; +}; + +#define XX(tag, value) DEFINE_FSTR_LOCAL(str_##tag, #tag) +SPIFFS_ERROR_MAP(XX) +#undef XX + +#define XX(tag, value) {value, &str_##tag}, +DEFINE_FSTR_MAP_LOCAL(errorMap, int, FlashString, SPIFFS_ERROR_MAP(XX)) +#undef XX + +String spiffsErrorToStr(int err) +{ + return errorMap[std::min(err, 0)].content(); +} + +} // namespace SPIFFS +} // namespace IFS diff --git a/Sming/Libraries/Spiffs/src/FileSystem.cpp b/Sming/Libraries/Spiffs/src/FileSystem.cpp new file mode 100644 index 0000000000..d779f4f5c7 --- /dev/null +++ b/Sming/Libraries/Spiffs/src/FileSystem.cpp @@ -0,0 +1,769 @@ +/** + * FileSystem.cpp + * + * Created on: 21 Jul 2018 + * + * Copyright 2019 mikee47 + * + * This file is part of the SPIFFS IFS Library + * + * This library is free software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, version 3 or later. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this library. + * If not, see . + * + ****/ + +#include "include/IFS/SPIFFS/FileSystem.h" +#include "include/IFS/SPIFFS/Error.h" +#include + +namespace IFS +{ +namespace SPIFFS +{ +struct FileDir { + char path[SPIFFS_OBJ_NAME_LEN]; ///< Filter for readdir() + unsigned pathlen; + String directories; // Names of discovered directories + spiffs_DIR d; +}; + +#define GET_FILEDIR() \ + if(dir == nullptr) { \ + return Error::InvalidHandle; \ + } \ + auto d = reinterpret_cast(dir); + +constexpr uint32_t logicalBlockSize{4096 * 2}; + +namespace +{ +/** @brief map IFS OpenFlags to SPIFFS equivalents + * @param flags + * @param sflags OUT the SPIFFS file open flags + * @retval OpenFlags if non-zero then some flags weren't recognised + * @note OpenFlags were initially created the same as SPIFFS, but they + * may change to support other features so map them here + */ +OpenFlags mapFileOpenFlags(OpenFlags flags, spiffs_flags& sflags) +{ + sflags = 0; + + auto map = [&](OpenFlag flag, spiffs_flags sflag) { + if(flags[flag]) { + sflags |= sflag; + flags -= flag; + } + }; + + map(OpenFlag::Append, SPIFFS_O_APPEND); + map(OpenFlag::Truncate, SPIFFS_O_TRUNC); + map(OpenFlag::Create, SPIFFS_O_CREAT); + map(OpenFlag::Read, SPIFFS_O_RDONLY); + map(OpenFlag::Write, SPIFFS_O_WRONLY); + + if(flags.any()) { + debug_w("Unknown OpenFlags: 0x%02X", flags.value()); + } + + return flags; +} + +} // namespace + +s32_t FileSystem::f_read(struct spiffs_t* spiffs, u32_t addr, u32_t size, u8_t* dst) +{ + auto fs = static_cast(spiffs->user_data); + assert(fs != nullptr); + if(!fs->partition.read(addr, dst, size)) { + return SPIFFS_ERR_INTERNAL; + } + if(fs->profiler != nullptr) { + fs->profiler->write(addr, dst, size); + } + return SPIFFS_OK; +} + +s32_t FileSystem::f_write(struct spiffs_t* spiffs, u32_t addr, u32_t size, u8_t* src) +{ + auto fs = static_cast(spiffs->user_data); + assert(fs != nullptr); + if(fs->profiler != nullptr) { + fs->profiler->write(addr, src, size); + } + return fs->partition.write(addr, src, size) ? SPIFFS_OK : SPIFFS_ERR_INTERNAL; +} + +s32_t FileSystem::f_erase(struct spiffs_t* spiffs, u32_t addr, u32_t size) +{ + auto fs = static_cast(spiffs->user_data); + assert(fs != nullptr); + if(fs->profiler != nullptr) { + fs->profiler->erase(addr, size); + } + return fs->partition.erase_range(addr, size) ? SPIFFS_OK : SPIFFS_ERR_INTERNAL; +} + +FileSystem::~FileSystem() +{ + SPIFFS_unmount(handle()); +} + +int FileSystem::mount() +{ + if(!partition) { + return Error::NoPartition; + } + + if(!partition.verify(Storage::Partition::SubType::Data::spiffs)) { + return Error::BadPartition; + } + + fs.user_data = this; + spiffs_config cfg{ + .hal_read_f = f_read, + .hal_write_f = f_write, + .hal_erase_f = f_erase, + .phys_size = partition.size(), + .phys_addr = 0, + .phys_erase_block = partition.getBlockSize(), + .log_block_size = logicalBlockSize, + .log_page_size = LOG_PAGE_SIZE, + }; + + // debug_i("FFS offset: 0x%08x, size: %u Kb, \n", cfg.phys_addr, cfg.phys_size / 1024); + + auto res = tryMount(cfg); + if(res < 0) { + /* + * Mount failed, so we either try to repair the system or format it. + * For now, just format it. + */ + res = format(); + } + +#if SPIFFS_TEST_VISUALISATION + if(res == SPIFFS_OK) { + SPIFFS_vis(handle()); + } +#endif + + return res; +} + +int FileSystem::tryMount(spiffs_config& cfg) +{ + auto err = SPIFFS_mount(handle(), &cfg, reinterpret_cast(workBuffer), + reinterpret_cast(fileDescriptors), sizeof(fileDescriptors), cache, sizeof(cache), + nullptr); + if(err < 0) { + if(isSpiffsError(err)) { + err = Error::fromSystem(err); + } + debug_ifserr(err, "SPIFFS_mount()"); + } + + return err; +} + +/* + * Format the file system and leave it mounted in an accessible state. + */ +int FileSystem::format() +{ + spiffs_config cfg = fs.cfg; + // Must be unmounted before format is called - see API + SPIFFS_unmount(handle()); + int err = SPIFFS_format(handle()); + if(err < 0) { + err = Error::fromSystem(err); + debug_ifserr(err, "format()"); + return err; + } + + // Re-mount + return tryMount(cfg); +} + +int FileSystem::check() +{ + fs.check_cb_f = [](spiffs* fs, spiffs_check_type type, spiffs_check_report report, u32_t arg1, u32_t arg2) { + if(report > SPIFFS_CHECK_PROGRESS) { + debug_d("SPIFFS check %d, %d, %u, %u", type, report, arg1, arg2); + } + }; + + int err = SPIFFS_check(handle()); + return Error::fromSystem(err); +} + +int FileSystem::getinfo(Info& info) +{ + info.clear(); + info.partition = partition; + info.type = Type::SPIFFS; + info.maxNameLength = SPIFFS_OBJ_NAME_LEN - 1; + info.maxPathLength = info.maxNameLength; + if(SPIFFS_mounted(handle())) { + info.volumeID = fs.config_magic; + info.attr |= Attribute::Mounted; +#ifndef SPIFFS_STORE_META + info.attr |= Attribute::NoMeta; +#endif + u32_t total, used; + SPIFFS_info(handle(), &total, &used); + info.volumeSize = total; + // As per SPIFFS spec + if(used >= total) { + info.attr |= Attribute::Check; + } else { + info.freeSpace = total - used; + } + } + + return FS_OK; +} + +int FileSystem::setProfiler(IProfiler* profiler) +{ + this->profiler = profiler; + return FS_OK; +} + +String FileSystem::getErrorString(int err) +{ + if(Error::isSystem(err)) { + return spiffsErrorToStr(Error::toSystem(err)); + } else { + return IFS::Error::toString(err); + } +} + +FileHandle FileSystem::open(const char* path, OpenFlags flags) +{ + FS_CHECK_PATH(path); + if(path == nullptr) { + return Error::BadParam; + } + + /* + * The file may be marked 'read-only' in its metadata, so avoid modifications + * (truncate) during the open phase until this has been checked. + */ + + spiffs_flags sflags; + if(mapFileOpenFlags(flags - OpenFlag::Truncate, sflags).any()) { + return FileHandle(Error::NotSupported); + } + + auto file = SPIFFS_open(handle(), path, sflags, 0); + if(file < 0) { + int err = Error::fromSystem(file); + debug_ifserr(err, "open('%s')", path); + return err; + } + + auto smb = initMetaBuffer(file); + // If file is marked read-only, fail write requests + if(smb != nullptr) { + if(flags[OpenFlag::Write] && smb->meta.attr[FileAttribute::ReadOnly]) { + SPIFFS_close(handle(), file); + return Error::ReadOnly; + } + } + + // Now truncate the file if so requested + if(flags[OpenFlag::Truncate]) { + int err = SPIFFS_ftruncate(handle(), file, 0); + if(err < 0) { + SPIFFS_close(handle(), file); + return Error::fromSystem(err); + } + + // Update modification timestamp + touch(file); + } + + return file; +} + +int FileSystem::close(FileHandle file) +{ + if(file < 0) { + return Error::FileNotOpen; + } + + int res = flushMeta(file); + int err = SPIFFS_close(handle(), file); + if(err < 0) { + res = Error::fromSystem(err); + } + return res; +} + +int FileSystem::eof(FileHandle file) +{ + int res = SPIFFS_eof(handle(), file); + return Error::fromSystem(res); +} + +int32_t FileSystem::tell(FileHandle file) +{ + int res = SPIFFS_tell(handle(), file); + return Error::fromSystem(res); +} + +int FileSystem::ftruncate(FileHandle file, size_t new_size) +{ + int res = SPIFFS_ftruncate(handle(), file, new_size); + return Error::fromSystem(res); +} + +int FileSystem::flush(FileHandle file) +{ + int res = flushMeta(file); + int err = SPIFFS_fflush(handle(), file); + if(err < 0) { + res = Error::fromSystem(err); + } + return res; +} + +int FileSystem::read(FileHandle file, void* data, size_t size) +{ + int res = SPIFFS_read(handle(), file, data, size); + if(res < 0) { + int err = Error::fromSystem(res); + debug_ifserr(err, "read()"); + return err; + } + + // debug_i("read(%d): %d", bufSize, res); + return res; +} + +int FileSystem::write(FileHandle file, const void* data, size_t size) +{ + int res = SPIFFS_write(handle(), file, const_cast(data), size); + if(res < 0) { + return Error::fromSystem(res); + } + + touch(file); + return res; +} + +int FileSystem::lseek(FileHandle file, int offset, SeekOrigin origin) +{ + int res = SPIFFS_lseek(handle(), file, offset, int(origin)); + if(res < 0) { + int err = Error::fromSystem(res); + debug_ifserr(err, "lseek()"); + return err; + } + + // debug_i("Seek(%d, %d): %d", offset, origin, res); + + return res; +} + +SpiffsMetaBuffer* FileSystem::initMetaBuffer(FileHandle file) +{ + auto smb = getMetaBuffer(file); + if(smb == nullptr) { + return nullptr; + } + + smb->init(); + +#ifdef SPIFFS_STORE_META + spiffs_stat stat; + int err = SPIFFS_fstat(handle(), file, &stat); + if(err >= 0) { + smb->assign(stat.meta); + } +#endif + + return smb; +} + +SpiffsMetaBuffer* FileSystem::getMetaBuffer(FileHandle file) +{ + unsigned off = SPIFFS_FH_UNOFFS(handle(), file) - 1; + if(off >= FFS_MAX_FILEDESC) { + debug_e("getMetaBuffer(%d) - bad file", file); + return nullptr; + } + return &metaCache[off]; +} + +/* + * If metadata has changed, flush it to SPIFFS. + */ +int FileSystem::flushMeta(FileHandle file) +{ +#ifdef SPIFFS_STORE_META + auto smb = getMetaBuffer(file); + if(smb == nullptr) { + return Error::InvalidHandle; + } + + // Changed ? + if(smb->flags[SpiffsMetaBuffer::Flag::dirty]) { + debug_d("Flushing Metadata to disk"); + smb->flags[SpiffsMetaBuffer::Flag::dirty] = false; + int err = SPIFFS_fupdate_meta(handle(), file, smb); + if(err < 0) { + err = Error::fromSystem(err); + debug_ifserr(err, "fupdate_meta()"); + return err; + } + } +#endif + return FS_OK; +} + +int FileSystem::stat(const char* path, Stat* stat) +{ + spiffs_stat ss; + int err = SPIFFS_stat(handle(), path ?: "", &ss); + if(err < 0) { + return Error::fromSystem(err); + } + + if(stat != nullptr) { + *stat = Stat{}; + stat->fs = this; + stat->name.copy(reinterpret_cast(ss.name)); + stat->size = ss.size; + stat->id = ss.obj_id; + SpiffsMetaBuffer smb; +#ifdef SPIFFS_STORE_META + smb.assign(ss.meta); +#else + smb.init(); +#endif + smb.copyTo(*stat); + } + + return FS_OK; +} + +int FileSystem::fstat(FileHandle file, Stat* stat) +{ + spiffs_stat ss; + int err = SPIFFS_fstat(handle(), file, &ss); + if(err < 0) { + return Error::fromSystem(err); + } + + auto smb = getMetaBuffer(file); + if(smb == nullptr) { + return Error::InvalidHandle; + } + +#ifdef SPIFFS_STORE_META + smb->assign(ss.meta); +#endif + + if(stat != nullptr) { + *stat = Stat{}; + stat->fs = this; + stat->name.copy(reinterpret_cast(ss.name)); + stat->size = ss.size; + stat->id = ss.obj_id; + smb->copyTo(*stat); + } + + return FS_OK; +} + +int FileSystem::fsetxattr(FileHandle file, AttributeTag tag, const void* data, size_t size) +{ + auto smb = getMetaBuffer(file); + if(smb == nullptr) { + return Error::InvalidHandle; + } + return smb->setxattr(tag, data, size); +} + +int FileSystem::fgetxattr(FileHandle file, AttributeTag tag, void* buffer, size_t size) +{ + auto smb = getMetaBuffer(file); + if(smb == nullptr) { + return Error::InvalidHandle; + } + return smb->getxattr(tag, buffer, size); +} + +int FileSystem::setxattr(const char* path, AttributeTag tag, const void* data, size_t size) +{ +#ifdef SPIFFS_STORE_META + FS_CHECK_PATH(path); + spiffs_stat ss; + int err = SPIFFS_stat(handle(), path ?: "", &ss); + if(err < 0) { + return Error::fromSystem(err); + } + SpiffsMetaBuffer smb; + smb.assign(ss.meta); + err = smb.setxattr(tag, data, size); + if(err < 0) { + return err; + } + if(!smb.flags[SpiffsMetaBuffer::Flag::dirty]) { + return FS_OK; + } + err = SPIFFS_update_meta(handle(), path, &smb); + return Error::fromSystem(err); +#else + return Error::NotSupported; +#endif +} + +int FileSystem::getxattr(const char* path, AttributeTag tag, void* buffer, size_t size) +{ +#ifdef SPIFFS_STORE_META + FS_CHECK_PATH(path); + spiffs_stat ss; + int err = SPIFFS_stat(handle(), path, &ss); + if(err < 0) { + return Error::fromSystem(err); + } + SpiffsMetaBuffer smb; + smb.assign(ss.meta); + return smb.getxattr(tag, buffer, size); +#else + return Error::NotSupported; +#endif +} + +int FileSystem::opendir(const char* path, DirHandle& dir) +{ + FS_CHECK_PATH(path); + unsigned pathlen = 0; + if(path != nullptr) { + pathlen = strlen(path); + if(pathlen >= sizeof(FileDir::path)) { + return Error::NameTooLong; + } + } + + auto d = new FileDir{}; + if(d == nullptr) { + return Error::NoMem; + } + + if(SPIFFS_opendir(handle(), nullptr, &d->d) == nullptr) { + int err = SPIFFS_errno(handle()); + err = Error::fromSystem(err); + debug_ifserr(err, "opendir"); + delete d; + return err; + } + + if(pathlen != 0) { + memcpy(d->path, path, pathlen); + } + d->path[pathlen] = '\0'; + d->pathlen = pathlen; + + dir = DirHandle(d); + return FS_OK; +} + +int FileSystem::rewinddir(DirHandle dir) +{ + GET_FILEDIR() + + SPIFFS_closedir(&d->d); + d->directories.setLength(0); + if(SPIFFS_opendir(handle(), nullptr, &d->d) == nullptr) { + int err = SPIFFS_errno(handle()); + return Error::fromSystem(err); + } + + return FS_OK; +} + +int FileSystem::readdir(DirHandle dir, Stat& stat) +{ + GET_FILEDIR() + + spiffs_dirent e; + for(;;) { + if(SPIFFS_readdir(&d->d, &e) == nullptr) { + int err = SPIFFS_errno(handle()); + if(err == SPIFFS_VIS_END) { + return Error::NoMoreFiles; + } + + return Error::fromSystem(err); + } + + /* The volume doesn't contain directory objects, so at each level we need + * to identify sub-folders and insert a virtual 'directory' object. + */ + + auto name = (char*)e.name; + + // For sub-directories, match the parsing path + auto len = strlen(name); + if(d->pathlen != 0) { + if(len <= d->pathlen) { + // debug_i("Ignoring '%s' - too short for '%s'", name, d->path); + continue; + } + + if(name[d->pathlen] != '/') { + // debug_i("Ignoring '%s' - no '/' in expected position to match '%s'", name, d->path); + continue; + } + + if(memcmp(d->path, name, d->pathlen) != 0) { + // debug_i("Ignoring '%s' - doesn't match '%s'", name, d->path); + continue; + } + + name += d->pathlen + 1; + } + + // This is a child directory + char* nextSep = strchr(name, '/'); + if(nextSep != nullptr) { + *nextSep = '\0'; + auto len = 1 + nextSep - name; // Include NUL terminator + // Emit directory names only once + auto dirEmitted = [&]() { + unsigned i = 0; + while(i < d->directories.length()) { + auto p = &d->directories[i]; + auto len = strlen(p); + if(strcmp(p, name) == 0) { + return true; + } + i += len + 1; + } + return false; + }; + if(dirEmitted()) { + continue; + } + d->directories.concat(name, len); + } + + stat = Stat{}; + stat.fs = this; + stat.name.copy(name); + if(nextSep != nullptr) { + stat.attr |= FileAttribute::Directory; + } else { + stat.size = e.size; + stat.id = e.obj_id; + SpiffsMetaBuffer smb; +#ifdef SPIFFS_STORE_META + smb.assign(e.meta); +#else + smb.init(); +#endif + smb.copyTo(stat); + } + + return FS_OK; + } +} + +int FileSystem::closedir(DirHandle dir) +{ + GET_FILEDIR() + + int err = SPIFFS_closedir(&d->d); + delete d; + return Error::fromSystem(err); +} + +int FileSystem::mkdir(const char* path) +{ + // TODO + return Error::NotImplemented; +} + +int FileSystem::rename(const char* oldpath, const char* newpath) +{ + FS_CHECK_PATH(oldpath); + FS_CHECK_PATH(newpath); + if(oldpath == nullptr || newpath == nullptr) { + return Error::BadParam; + } + + int err = SPIFFS_rename(handle(), oldpath, newpath); + return Error::fromSystem(err); +} + +int FileSystem::remove(const char* path) +{ + FS_CHECK_PATH(path); + if(path == nullptr) { + return Error::BadParam; + } + + // Check file is not marked read-only + int f = open(path, OpenFlag::Read); + if(f >= 0) { + auto smb = getMetaBuffer(f); + assert(smb != nullptr); + auto attr = smb->meta.attr; + close(f); + if(attr[FileAttribute::ReadOnly]) { + return Error::ReadOnly; + } + } + + int err = SPIFFS_remove(handle(), path); + err = Error::fromSystem(err); + debug_ifserr(err, "remove('%s')", path); + return err; +} + +int FileSystem::fremove(FileHandle file) +{ + // If file is marked read-only, fail request + auto smb = getMetaBuffer(file); + if(smb == nullptr) { + return Error::InvalidHandle; + } + if(smb->meta.attr[FileAttribute::ReadOnly]) { + return Error::ReadOnly; + } + + int err = SPIFFS_fremove(handle(), file); + return Error::fromSystem(err); +} + +int FileSystem::getFilePath(FileID fileid, NameBuffer& buffer) +{ + auto fs = handle(); + spiffs_block_ix block_ix; + int lu_entry; + int err = spiffs_obj_lu_find_id(fs, 0, 0, fileid | SPIFFS_OBJ_ID_IX_FLAG, &block_ix, &lu_entry); + if(err == SPIFFS_OK) { + spiffs_page_object_ix_header objix_hdr; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block_ix, lu_entry); + err = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(objix_hdr), reinterpret_cast(&objix_hdr)); + if(err == SPIFFS_OK) { + return buffer.copy(reinterpret_cast(objix_hdr.name)); + } + } + + return Error::fromSystem(err); +} + +} // namespace SPIFFS +} // namespace IFS diff --git a/Sming/Libraries/Spiffs/src/Spiffs.cpp b/Sming/Libraries/Spiffs/src/Spiffs.cpp new file mode 100644 index 0000000000..a6b37d047d --- /dev/null +++ b/Sming/Libraries/Spiffs/src/Spiffs.cpp @@ -0,0 +1,93 @@ +/** + * Spiffs.cpp + * + * Created on: 21 Jul 2018 + * + * Copyright 2019 mikee47 + * + * This file is part of the SPIFFS IFS Library + * + * This library is free software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, version 3 or later. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this library. + * If not, see . + * + */ + +#include "include/Spiffs.h" +#include "include/IFS/SPIFFS/FileSystem.h" +#include +#include + +namespace IFS +{ +FileSystem* createSpiffsFilesystem(Storage::Partition partition) +{ + auto fs = new SPIFFS::FileSystem(partition); + return FileSystem::cast(fs); +} + +} // namespace IFS + +bool spiffs_mount() +{ + auto part = Storage::findDefaultPartition(Storage::Partition::SubType::Data::spiffs); + return part ? spiffs_mount(part) : false; +} + +bool spiffs_mount(Storage::Partition partition) +{ + auto fs = IFS::createSpiffsFilesystem(partition); + return fileMountFileSystem(fs); +} + +void spiffs_unmount() +{ + if(fileSystemType() == IFS::IFileSystem::Type::SPIFFS) { + fileFreeFileSystem(); + } +} + +bool hyfs_mount() +{ + auto fwfsPart = Storage::findDefaultPartition(Storage::Partition::SubType::Data::fwfs); + auto spiffsPart = Storage::findDefaultPartition(Storage::Partition::SubType::Data::spiffs); + return (fwfsPart && spiffsPart) ? hyfs_mount(fwfsPart, spiffsPart) : false; +} + +bool hyfs_mount(Storage::Partition fwfsPartition, Storage::Partition spiffsPartition) +{ + auto ffs = IFS::createSpiffsFilesystem(spiffsPartition); + auto fs = IFS::createHybridFilesystem(fwfsPartition, ffs); + return fileMountFileSystem(fs); +} + +bool spiffs_format() +{ + if(fileSystemType() != IFS::IFileSystem::Type::SPIFFS) { + return false; + } + return fileSystemFormat() == FS_OK; +} + +bool spiffs_format(Storage::Partition& partition) +{ + if(fileSystemType() == IFS::IFileSystem::Type::SPIFFS) { + fileFreeFileSystem(); + } + auto fs = new IFS::SPIFFS::FileSystem(partition); + int err = fs->format(); + if(err < 0) { + debug_e("SPIFFS format failed: %s", fs->getErrorString(err).c_str()); + delete fs; + return false; + } + + fileSetFileSystem(fs); + return true; +} diff --git a/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/Error.h b/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/Error.h new file mode 100644 index 0000000000..f714e59cb3 --- /dev/null +++ b/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/Error.h @@ -0,0 +1,38 @@ +/** + * Error.h + * SPIFFS error codes + * + * Created on: 31 Aug 2018 + * + * Copyright 2019 mikee47 + * + * This file is part of the SPIFFS IFS Library + * + * This library is free software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, version 3 or later. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this library. + * If not, see . + * + ****/ + +#pragma once + +#include + +namespace IFS +{ +namespace SPIFFS +{ +inline bool isSpiffsError(int err) +{ + return err <= -10000; +} + +String spiffsErrorToStr(int err); +} // namespace SPIFFS +} // namespace IFS diff --git a/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileMeta.h b/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileMeta.h new file mode 100644 index 0000000000..eff71e8a44 --- /dev/null +++ b/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileMeta.h @@ -0,0 +1,265 @@ +/** + * FileMeta.h + * + * Created on: 21 Jul 2018 + * + * Copyright 2019 mikee47 + * + * This file is part of the SPIFFS IFS Library + * + * This library is free software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, version 3 or later. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this library. + * If not, see . + * + ****/ + +#pragma once + +#include +#include +#include + +namespace IFS +{ +namespace SPIFFS +{ +/** + * @brief Content of SPIFFS metadata area + */ +struct FileMeta { + /// This number is made up, but serves to identify that metadata is valid + static constexpr uint32_t Magic{0xE3457A77}; + + // Magic + uint32_t magic; + // Modification time + TimeStamp mtime; + // FileAttributes - default indicates content has changed + FileAttributes attr; + // Security + ACL acl; + // Compression + Compression compression; + + void init() + { + *this = FileMeta{}; + magic = Magic; + mtime = fsGetTimeUTC(); + acl.readAccess = UserRole::Admin; + acl.writeAccess = UserRole::Admin; + } + + void* getAttributePtr(AttributeTag tag) + { + switch(tag) { + case AttributeTag::ModifiedTime: + return &mtime; + case AttributeTag::Acl: + return &acl; + case AttributeTag::Compression: + return &compression; + case AttributeTag::FileAttributes: + return &attr; + default: + return nullptr; + } + } +}; + +#define FILEMETA_SIZE 16 +static_assert(sizeof(FileMeta) == FILEMETA_SIZE, "FileMeta wrong size"); + +#if SPIFFS_OBJ_META_LEN >= FILEMETA_SIZE +#define SPIFFS_STORE_META +#define SPIFFS_USER_METALEN (SPIFFS_OBJ_META_LEN - FILEMETA_SIZE) +#else +#define SPIFFS_USER_METALEN 0 +#endif + +struct SpiffsMetaBuffer { + FileMeta meta; + uint8_t user[SPIFFS_USER_METALEN]; + + enum class Flag { + dirty, + }; + BitSet flags; + + void init() + { + meta.init(); + memset(user, 0xFF, sizeof(user)); + } + + template void assign(const T& data) + { + static_assert(sizeof(data) == offsetof(SpiffsMetaBuffer, flags), "SPIFFS metadata assign() size incorrect"); + memcpy(reinterpret_cast(this), data, sizeof(data)); + + // If metadata uninitialised, then initialise it now + if(meta.magic != FileMeta::Magic) { + meta.init(); + flags += Flag::dirty; + } + } + + void copyTo(Stat& stat) + { + stat.acl = meta.acl; + stat.attr = meta.attr; + stat.mtime = meta.mtime; + stat.compression = meta.compression; + } + + void setFileTime(time_t t) + { + if(meta.mtime != t) { + meta.mtime = t; + flags += Flag::dirty; + } + } + + int getxattr(AttributeTag tag, void* buffer, size_t size) + { + if(tag >= AttributeTag::User) { + return getUserAttribute(unsigned(tag) - unsigned(AttributeTag::User), buffer, size); + } + + auto attrSize = getAttributeSize(tag); + if(attrSize == 0) { + return Error::BadParam; + } + if(size >= attrSize) { + auto value = meta.getAttributePtr(tag); + if(value != nullptr) { + memcpy(buffer, value, attrSize); + } + } + return attrSize; + } + + int setxattr(AttributeTag tag, const void* data, size_t size) + { + if(tag >= AttributeTag::User) { + return setUserAttribute(unsigned(tag) - unsigned(AttributeTag::User), data, size); + } + + // Cannot delete standard attributes + if(data == nullptr) { + return Error::NotSupported; + } + if(size != getAttributeSize(tag)) { + return Error::BadParam; + } + auto value = meta.getAttributePtr(tag); + if(value == nullptr) { + return Error::BadParam; + } + if(memcmp(value, data, size) == 0) { + // No change + return FS_OK; + } + memcpy(value, data, size); + if(tag == AttributeTag::Compression) { + meta.attr[FileAttribute::Compressed] = (meta.compression.type != Compression::Type::None); + } + flags += Flag::dirty; + return FS_OK; + } + + /* + * User tags are laid out in spare space as follows: + * + * uint8_t tag; + * uint8_t len; + * uint8_t data[]; + * + * Unused space is set to 0xFF. + */ + int getUserAttribute(unsigned userTag, void* buffer, size_t size) + { + if(userTag > 255) { + return Error::BadParam; + } + + for(unsigned i = 0; i < SPIFFS_USER_METALEN;) { + uint8_t tagIndex = user[i++]; + uint8_t tagSize = user[i++]; + if(tagIndex != userTag) { + i += tagSize; + continue; + } + if(size > tagSize) { + size = tagSize; + } + if(buffer != nullptr) { + memcpy(buffer, &user[i], std::min(size_t(tagSize), size)); + } + return tagSize; + } + + return Error::NotFound; + } + + int setUserAttribute(unsigned userTag, const void* data, size_t size) + { + if(userTag > 255) { + return Error::BadParam; + } + + bool deleteFlag = (data == nullptr); + + for(unsigned i = 0; i < SPIFFS_USER_METALEN;) { + uint8_t tagIndex = user[i++]; + uint8_t tagSize = user[i++]; + if(tagIndex == userTag) { + // Found the tag - compare with new data + if(!deleteFlag && tagSize == size && memcmp(data, &user[i], tagSize) == 0) { + // No change + return FS_OK; + } + // Remove the tag + i -= 2; + tagSize += 2; + // Shift items above down + memmove(&user[i], &user[i + tagSize], SPIFFS_USER_METALEN - i - tagSize); + // Clear unused space to 0xFF + memset(&user[SPIFFS_USER_METALEN - tagSize], 0xff, tagSize); + flags += Flag::dirty; + + if(deleteFlag) { + return FS_OK; + } + continue; + } + + // End of list? + if(tagIndex == 0xff && tagSize == 0xff) { + // Room for new tag? + if(i + size > SPIFFS_USER_METALEN) { + break; + } + user[i - 2] = userTag; + user[i - 1] = size; + memcpy(&user[i], data, size); + flags += Flag::dirty; + return size; + } + + i += tagSize; + } + + // No room for attribute + return Error::BufferTooSmall; + } +}; + +} // namespace SPIFFS +} // namespace IFS diff --git a/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileSystem.h b/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileSystem.h new file mode 100644 index 0000000000..0bb06246c2 --- /dev/null +++ b/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileSystem.h @@ -0,0 +1,149 @@ +/** + * FileSystem.h + * Provides an IFS FileSystem implementation for SPIFFS. + * + * Created on: 21 Jul 2018 + * + * Copyright 2019 mikee47 + * + * This file is part of the SPIFFS IFS Library + * + * This library is free software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, version 3 or later. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this library. + * If not, see . + * + * + * This is mostly a straightforward wrapper around SPIFFS, with a few enhancements: + * + * Metadata caching + * + * Metadata can be updated multiple times while a file is open so + * for efficiency it is kept in RAM and only written to SPIFFS on close or flush. + * + * Directory emulation + * + * SPIFFS stores all files in a flat format, so directory behaviour is emulated + * including opendir() and readdir() operations. Overall path length is fixed + * according to SPIFFS_OBJ_NAME_LEN. + * + * File truncation + * + * Standard IFS truncate() method allows file size to be reduced. + * This was added to Sming in version 4. + * + */ + +#pragma once + +#include +#include "FileMeta.h" +#include "../../../../spiffs/src/spiffs.h" +extern "C" { +#include "../../../../spiffs/src/spiffs_nucleus.h" +} + +/* + * Maxmimum number of open files + */ +#define FFS_MAX_FILEDESC 8 + +namespace IFS +{ +namespace SPIFFS +{ +/* + * Wraps SPIFFS + */ +class FileSystem : public IFileSystem +{ +public: + FileSystem(Storage::Partition partition) : partition(partition) + { + } + + ~FileSystem(); + + int mount() override; + int getinfo(Info& info) override; + int setProfiler(IProfiler* profiler) override; + String getErrorString(int err) override; + int opendir(const char* path, DirHandle& dir) override; + int readdir(DirHandle dir, Stat& stat) override; + int rewinddir(DirHandle dir) override; + int closedir(DirHandle dir) override; + int mkdir(const char* path) override; + int stat(const char* path, Stat* stat) override; + int fstat(FileHandle file, Stat* stat) override; + int fsetxattr(FileHandle file, AttributeTag tag, const void* data, size_t size) override; + int fgetxattr(FileHandle file, AttributeTag tag, void* buffer, size_t size) override; + int setxattr(const char* path, AttributeTag tag, const void* data, size_t size) override; + int getxattr(const char* path, AttributeTag tag, void* buffer, size_t size) override; + FileHandle open(const char* path, OpenFlags flags) override; + int close(FileHandle file) override; + int read(FileHandle file, void* data, size_t size) override; + int write(FileHandle file, const void* data, size_t size) override; + int lseek(FileHandle file, int offset, SeekOrigin origin) override; + int eof(FileHandle file) override; + int32_t tell(FileHandle file) override; + int ftruncate(FileHandle file, size_t new_size) override; + int flush(FileHandle file) override; + int rename(const char* oldpath, const char* newpath) override; + int remove(const char* path) override; + int fremove(FileHandle file) override; + int format() override; + int check() override; + + /** @brief get the full path of a file from its ID + * @param fileid + * @param buffer + * @retval int error code + */ + int getFilePath(FileID fileid, NameBuffer& buffer); + +private: + spiffs* handle() + { + return &fs; + } + + int tryMount(spiffs_config& cfg); + + SpiffsMetaBuffer* initMetaBuffer(FileHandle file); + SpiffsMetaBuffer* getMetaBuffer(FileHandle file); + int flushMeta(FileHandle file); + + void touch(FileHandle file) + { + auto smb = getMetaBuffer(file); + if(smb != nullptr) { + smb->setFileTime(fsGetTimeUTC()); + } + } + + static s32_t f_read(struct spiffs_t* spiffs, u32_t addr, u32_t size, u8_t* dst); + static s32_t f_write(struct spiffs_t* spiffs, u32_t addr, u32_t size, u8_t* src); + static s32_t f_erase(struct spiffs_t* spiffs, u32_t addr, u32_t size); + + static constexpr size_t CACHE_PAGES{8}; + static constexpr size_t LOG_PAGE_SIZE{256}; + static constexpr size_t MIN_BLOCKSIZE{256}; + static constexpr size_t CACHE_PAGE_SIZE{sizeof(spiffs_cache_page) + LOG_PAGE_SIZE}; + static constexpr size_t CACHE_SIZE{sizeof(spiffs_cache) + CACHE_PAGES * CACHE_PAGE_SIZE}; + + Storage::Partition partition; + IProfiler* profiler{nullptr}; + SpiffsMetaBuffer metaCache[FFS_MAX_FILEDESC]; + spiffs fs; + uint16_t workBuffer[LOG_PAGE_SIZE]; + spiffs_fd fileDescriptors[FFS_MAX_FILEDESC]; + uint8_t cache[CACHE_SIZE]; +}; + +} // namespace SPIFFS +} // namespace IFS diff --git a/Sming/Components/spiffs/spiffs_sming.h b/Sming/Libraries/Spiffs/src/include/Spiffs.h similarity index 50% rename from Sming/Components/spiffs/spiffs_sming.h rename to Sming/Libraries/Spiffs/src/include/Spiffs.h index 9480c00db1..0077163213 100644 --- a/Sming/Components/spiffs/spiffs_sming.h +++ b/Sming/Libraries/Spiffs/src/include/Spiffs.h @@ -4,13 +4,46 @@ * http://github.com/SmingHub/Sming * All files of the Sming Core are provided under the LGPL v3 license. * - * spiffs_sming.h + * Spiffs.h * ****/ #pragma once -#include -#include +#include + +namespace IFS +{ +/** + * @brief Create a SPIFFS filesystem + * @param partition + * @retval FileSystem* constructed filesystem object + */ +FileSystem* createSpiffsFilesystem(Storage::Partition partition); + +} // namespace IFS + +/** + * @brief Mount the first available SPIFFS volume + * @retval bool true on success + */ +bool spiffs_mount(); + +/** + * @brief Mount SPIFFS volume from a specific partition + */ +bool spiffs_mount(Storage::Partition partition); + +/** + * @brief Mount the first available FWFS and SPIFFS partitions as a hybrid filesystem + * @retval bool true on success + */ +bool hyfs_mount(); + +/** + * @brief Mount the given FWFS and SPIFFS partitions as a hybrid filesystem + * @retval bool true on success + */ +bool hyfs_mount(Storage::Partition fwfsPartition, Storage::Partition spiffsPartition); /** * @brief unmount SPIFFS filesystem diff --git a/Sming/Components/spiffs/spiffs_config.h b/Sming/Libraries/Spiffs/src/include/spiffs_config.h similarity index 100% rename from Sming/Components/spiffs/spiffs_config.h rename to Sming/Libraries/Spiffs/src/include/spiffs_config.h diff --git a/Sming/component.mk b/Sming/component.mk index 4d3ba173fa..5d4e4306df 100644 --- a/Sming/component.mk +++ b/Sming/component.mk @@ -16,7 +16,7 @@ COMPONENT_DEPENDS := \ Storage \ sming-arch \ FlashString \ - spiffs \ + Spiffs \ IFS \ http-parser \ libb64 \ diff --git a/tests/HostTests/modules/Spiffs.cpp b/tests/HostTests/modules/Spiffs.cpp index 9853e098ea..bde78215f3 100644 --- a/tests/HostTests/modules/Spiffs.cpp +++ b/tests/HostTests/modules/Spiffs.cpp @@ -3,8 +3,6 @@ #ifdef ARCH_HOST #include -#include -using FileSystem = IFS::FileSystem; #endif class SpiffsTest : public TestGroup @@ -83,13 +81,13 @@ class SpiffsTest : public TestGroup */ void checkSpiffsGen() { - FileSystem::Info info; + IFS::FileSystem::Info info; int err = fileGetSystemInfo(info); CHECK(err >= 0); debug_i("fs attr = %s", toString(info.attr).c_str()); - FileSystem* fsOld; - if(info.attr[FileSystem::Attribute::NoMeta]) { + IFS::FileSystem* fsOld; + if(info.attr[IFS::FileSystem::Attribute::NoMeta]) { fsOld = mountSpiffsFromFile("old", "spiffsgen/spiff_rom_orig.bin"); } else { fsOld = mountSpiffsFromFile("old", "spiffsgen/spiff_rom_meta.bin"); @@ -105,7 +103,7 @@ class SpiffsTest : public TestGroup delete Storage::findDevice("old"); } - FileSystem* mountSpiffsFromFile(const String& tag, const String& filename) + IFS::FileSystem* mountSpiffsFromFile(const String& tag, const String& filename) { auto& hfs = IFS::Host::getFileSystem(); auto f = hfs.open(filename, IFS::File::ReadOnly); @@ -118,7 +116,7 @@ class SpiffsTest : public TestGroup auto part = dev->createPartition(tag, Storage::Partition::SubType::Data::spiffs, 0, dev->getSize(), Storage::Partition::Flag::readOnly); - auto fs = new IFS::SPIFFS::FileSystem(part); + auto fs = IFS::createSpiffsFilesystem(part); int err = fs->mount(); if(err < 0) { debug_e("SPIFFS mount '%s' failed: %s", tag.c_str(), fs->getErrorString(err).c_str()); @@ -128,7 +126,7 @@ class SpiffsTest : public TestGroup } debug_i("Mounted '%s' as '%s'", filename.c_str(), tag.c_str()); - return FileSystem::cast(fs); + return fs; } void readCheck(IFS::FileSystem* fsOld, IFS::FileSystem* fsNew) From 8d65611da57030c57791cd077d638a3f996c7e6a Mon Sep 17 00:00:00 2001 From: slaff Date: Thu, 22 Apr 2021 10:43:43 +0200 Subject: [PATCH 011/130] Feature: hosted serial (#2312) * Adds easier way on Linux and Windows to connect to a real serial port and communicate with the device over it. * Added control over the DTS and RTS serial lines (pins). * Updated the documentation. --- .gitmodules | 5 + Sming/Components/Hosted/README.rst | 22 +++- Sming/Components/Hosted/component.mk | 12 +- .../Components/Hosted/include/Hosted/Serial.h | 122 ++++++++++++++++++ .../Hosted/init/serial/InitClient.cpp | 21 ++- Sming/Libraries/seriallib/component.mk | 6 + Sming/Libraries/seriallib/seriallib | 1 + 7 files changed, 185 insertions(+), 4 deletions(-) create mode 100644 Sming/Components/Hosted/include/Hosted/Serial.h create mode 100644 Sming/Libraries/seriallib/component.mk create mode 160000 Sming/Libraries/seriallib/seriallib diff --git a/.gitmodules b/.gitmodules index 1f60c88332..6432564096 100644 --- a/.gitmodules +++ b/.gitmodules @@ -255,6 +255,10 @@ path = Sming/Libraries/RingTone url = https://github.com/mikee47/RingTone ignore = dirty +[submodule "Libraries.seriallib"] + path = Sming/Libraries/seriallib/seriallib + url = https://github.com/imabot2/serialib.git + ignore = dirty [submodule "Libraries.SignalGenerator"] path = Sming/Libraries/SignalGenerator url = https://github.com/mikee47/SignalGenerator @@ -335,3 +339,4 @@ # END OF .gitmodules # ################################################################################################# + diff --git a/Sming/Components/Hosted/README.rst b/Sming/Components/Hosted/README.rst index 824f811d39..66f47eef3b 100644 --- a/Sming/Components/Hosted/README.rst +++ b/Sming/Components/Hosted/README.rst @@ -8,7 +8,7 @@ The communication is done via `simplePRC `_ a Overview -------- -Sming's host emulator allows easier debugging and development of embedded applications. This component named "HostEd" extends the host emulator +Sming's host emulator allows easier debugging and development of embedded applications. This component named "Hosted" extends the host emulator and facilitates testing functionality that only a real microcontroller can provide as for example digital I/O operations or SPI operations. For example in order to run the Basic_Blink application under the host emulator and run the actual blinking of a LED on a microcontroller @@ -49,4 +49,22 @@ Configuration Enables the hosted component. Valid values for the moment are: - tcp - for communication over TCP network. - - serial - for communication over serial interface \ No newline at end of file + - serial - for communication over serial interface + +.. envvar:: HOSTED_SERVER_IP + + Default: 192.168.13.1 + + Used only when ENABLE_HOSTED=tcp is specified. Specifies the IP address of the remote RPC server. + +.. envvar:: HOSTED_COM_PORT + + Default: /dev/ttyUSB0 or the value of the environment variable COM_PORT if defined + + Used only when ENABLE_HOSTED=serial is specified. Specifies which local communication port should be used to connect to the remote RPC server. + +.. envvar:: HOSTED_COM_SPEED + + Default: 115200 + + Used only when ENABLE_HOSTED=serial is specified. Specifies the communication baud rate. diff --git a/Sming/Components/Hosted/component.mk b/Sming/Components/Hosted/component.mk index 11f6778e28..49d6f81bd2 100644 --- a/Sming/Components/Hosted/component.mk +++ b/Sming/Components/Hosted/component.mk @@ -13,9 +13,19 @@ ENABLE_HOSTED ?= ifneq ($(ENABLE_HOSTED),) COMPONENT_SRCDIRS += $(COMPONENT_PATH)/init/$(ENABLE_HOSTED) EXTRA_LDFLAGS := -Wl,-wrap,host_init + COMPONENT_DEPENDS += seriallib endif COMPONENT_VARS += HOSTED_SERVER_IP -COMPONENT_CFLAGS = -DHOSTED_SERVER_IP=$(HOSTED_SERVER_IP) +COMPONENT_VARS += HOSTED_COM_PORT +HOSTED_COM_PORT := "/dev/ttyUSB0" +ifneq ($(COM_PORT),) + HOSTED_COM_PORT:=$(COM_PORT) +endif + +COMPONENT_VARS += HOSTED_COM_SPEED +HOSTED_COM_SPEED := 115200 + +COMPONENT_CFLAGS = -DHOSTED_SERVER_IP=$(HOSTED_SERVER_IP) -DHOSTED_COM_PORT="\"$(HOSTED_COM_PORT)"\" -DHOSTED_COM_SPEED=$(HOSTED_COM_SPEED) COMPONENT_CXXFLAGS := $(COMPONENT_CFLAGS) diff --git a/Sming/Components/Hosted/include/Hosted/Serial.h b/Sming/Components/Hosted/include/Hosted/Serial.h new file mode 100644 index 0000000000..1d26b52bd4 --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Serial.h @@ -0,0 +1,122 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Serial.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#pragma once + +#ifndef ARCH_HOST +#error "Hosted::Serial can be used only on the Host architecture!" +#endif + +#include +#include +#include + +namespace Hosted +{ +class Serial : public Stream +{ +public: + Serial(const String& ttyDevice) : ttyDevice(ttyDevice) + { + } + + ~Serial() + { + transport.closeDevice(); + } + + bool setDtr(bool on) + { + return transport.DTR(on); + } + + bool setRts(bool on) + { + return transport.RTS(on); + } + + /** @brief Initialise the serial port + * @param baud BAUD rate of the serial port (Default: 9600) + */ + bool begin(uint32_t baud = 9600) + { + char result = transport.openDevice(ttyDevice.c_str(), baud); + if(result == 1) { + return true; + } + + debug_w("Hosted::Serial:begin error: %d", result); + return false; + } + + size_t write(uint8_t c) override + { + if(transport.writeChar(c)) { + return 1; + } + + return 0; + } + + int available() override + { + return transport.available(); + } + + int read() override + { + int ch; + int result = transport.readChar(reinterpret_cast(&ch), 1); + if(result == 1) { + return ch; + } + + return -1; + } + + size_t readBytes(char* buffer, size_t length) override + { + int result = transport.readBytes(buffer, length, 100, 100); + if(result > 0) { + return result; + } + + return 0; + } + + size_t write(const uint8_t* buffer, size_t size) + { + char result = transport.writeBytes(buffer, size); + if(result == 1) { + return size; + } + + return 0; + } + + int peek() override + { + return -1; + } + + void flush() override + { + } + +private: + String ttyDevice; + + serialib transport; +}; + +} // namespace Hosted diff --git a/Sming/Components/Hosted/init/serial/InitClient.cpp b/Sming/Components/Hosted/init/serial/InitClient.cpp index cb9d2b2745..9a9d22cf19 100644 --- a/Sming/Components/Hosted/init/serial/InitClient.cpp +++ b/Sming/Components/Hosted/init/serial/InitClient.cpp @@ -13,6 +13,15 @@ #include #include +#include + +#ifndef HOSTED_COM_PORT +#define HOSTED_COM_PORT "/dev/ttyUSB0" +#endif + +#ifndef HOSTED_COM_SPEED +#define HOSTED_COM_SPEED 115200 +#endif Hosted::Client* hostedClient{nullptr}; @@ -23,10 +32,20 @@ void __real_host_init(); void __wrap_host_init(); } +Hosted::Serial hostedSerial(HOSTED_COM_PORT); + void __wrap_host_init() { Serial.begin(115200); - hostedClient = new Hosted::Client(Serial); + Serial.printf("Connecting to: %s ...\n", HOSTED_COM_PORT); + + bool serialReady = false; + do { + serialReady = hostedSerial.begin(HOSTED_COM_SPEED); + usleep(200); + } while(!serialReady); + + hostedClient = new Hosted::Client(hostedSerial); hostedClient->getRemoteCommands(); init(); } diff --git a/Sming/Libraries/seriallib/component.mk b/Sming/Libraries/seriallib/component.mk new file mode 100644 index 0000000000..a350642adf --- /dev/null +++ b/Sming/Libraries/seriallib/component.mk @@ -0,0 +1,6 @@ +COMPONENT_SUBMODULES += seriallib + +SERIALLIB_ROOT := $(COMPONENT_PATH)/seriallib + +COMPONENT_SRCDIRS := $(SERIALLIB_ROOT)/lib $(COMPONENT_PATH)/src +COMPONENT_INCDIRS := $(COMPONENT_SRCDIRS) $(COMPONENT_PATH)/include \ No newline at end of file diff --git a/Sming/Libraries/seriallib/seriallib b/Sming/Libraries/seriallib/seriallib new file mode 160000 index 0000000000..8e7e23aad8 --- /dev/null +++ b/Sming/Libraries/seriallib/seriallib @@ -0,0 +1 @@ +Subproject commit 8e7e23aad8a3bcd31f9776799e3f8c6b84025a87 From b49332a060185df9aad48272ea3b5d33227ddc36 Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 23 Apr 2021 08:39:30 +0100 Subject: [PATCH 012/130] Fix various coverity scan warnings (#2314) [scan:coverity] --- Sming/Arch/Host/Components/spi_flash/flashmem.cpp | 6 +++--- Sming/Components/IFS | 2 +- Sming/Components/ssl/Axtls/AxConnection.h | 6 +++--- Sming/Core/Data/Stream/SectionStream.h | 2 +- Sming/Core/Network/Ftp/FtpDataFileList.h | 4 ++-- .../Spiffs/src/include/IFS/SPIFFS/FileMeta.h | 12 ++++++++++++ Sming/Wiring/WString.cpp | 4 ++-- 7 files changed, 24 insertions(+), 12 deletions(-) diff --git a/Sming/Arch/Host/Components/spi_flash/flashmem.cpp b/Sming/Arch/Host/Components/spi_flash/flashmem.cpp index ae479fbcba..0e8f0bc0bd 100644 --- a/Sming/Arch/Host/Components/spi_flash/flashmem.cpp +++ b/Sming/Arch/Host/Components/spi_flash/flashmem.cpp @@ -40,7 +40,7 @@ constexpr uint32_t FLASHMEM_REAL_MASK{~FLASHMEM_REAL_BIT}; #define CHECK_RANGE(_addr, _size) \ if((_addr) + (_size) > flashFileSize) { \ - host_debug_e("addr = 0x%08x, size = 0x%08x", _addr, _size); \ + host_debug_e("addr = 0x%08x, size = 0x%08x", _addr, _size); \ return false; \ } @@ -51,7 +51,7 @@ bool host_flashmem_init(FlashmemConfig& config) flashFileName[sizeof(flashFileName) - 1] = '\0'; } else { size_t len = getHostAppDir(flashFileName, sizeof(flashFileName)); - if(len + sizeof(defaultFlashFileName) > sizeof(flashFileName)) { + if(len > sizeof(flashFileName) - sizeof(defaultFlashFileName)) { return false; } memcpy(&flashFileName[len], defaultFlashFileName, sizeof(defaultFlashFileName)); @@ -128,7 +128,7 @@ static int writeFlashFile(uint32_t offset, const void* data, size_t count) SPIFlashInfo flashmem_get_info() { - SPIFlashInfo info; + SPIFlashInfo info{}; info.mode = MODE_QIO; info.speed = SPEED_80MHZ; info.size = SIZE_32MBIT; diff --git a/Sming/Components/IFS b/Sming/Components/IFS index 57f93bf87d..7054533606 160000 --- a/Sming/Components/IFS +++ b/Sming/Components/IFS @@ -1 +1 @@ -Subproject commit 57f93bf87dc18e61733b0db1acb59da4ef709dcb +Subproject commit 70545336066e93e11bd5e6a70555c8a04d2e1b03 diff --git a/Sming/Components/ssl/Axtls/AxConnection.h b/Sming/Components/ssl/Axtls/AxConnection.h index 8ce0362ff5..d3cf7ef279 100644 --- a/Sming/Components/ssl/Axtls/AxConnection.h +++ b/Sming/Components/ssl/Axtls/AxConnection.h @@ -92,9 +92,9 @@ class AxConnection : public Connection } private: - SSL* ssl; - mutable AxCertificate* certificate = nullptr; - InputBuffer* input = nullptr; + SSL* ssl{nullptr}; + mutable AxCertificate* certificate{nullptr}; + InputBuffer* input{nullptr}; }; } // namespace Ssl diff --git a/Sming/Core/Data/Stream/SectionStream.h b/Sming/Core/Data/Stream/SectionStream.h index 37fd16997c..b5b3fb2ee7 100644 --- a/Sming/Core/Data/Stream/SectionStream.h +++ b/Sming/Core/Data/Stream/SectionStream.h @@ -98,7 +98,7 @@ class SectionStream : public IDataSourceStream const Section* getSection(unsigned index) const { - if(index >= 0 && index < sectionCount) { + if(index < sectionCount) { return §ions[index]; } else { return nullptr; diff --git a/Sming/Core/Network/Ftp/FtpDataFileList.h b/Sming/Core/Network/Ftp/FtpDataFileList.h index 637804ddf7..0dd2d3a8eb 100644 --- a/Sming/Core/Network/Ftp/FtpDataFileList.h +++ b/Sming/Core/Network/Ftp/FtpDataFileList.h @@ -91,7 +91,7 @@ class FtpDataFileList : public FtpDataStream private: IFS::Directory dir; - uint16_t year; + uint16_t year{0}; bool namesOnly; - bool statValid; + bool statValid{false}; }; diff --git a/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileMeta.h b/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileMeta.h index eff71e8a44..4aaf74bc5e 100644 --- a/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileMeta.h +++ b/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileMeta.h @@ -185,6 +185,7 @@ struct SpiffsMetaBuffer { */ int getUserAttribute(unsigned userTag, void* buffer, size_t size) { +#if SPIFFS_USER_METALEN if(userTag > 255) { return Error::BadParam; } @@ -204,12 +205,18 @@ struct SpiffsMetaBuffer { } return tagSize; } +#else + (void)userTag; + (void)buffer; + (void)size; +#endif return Error::NotFound; } int setUserAttribute(unsigned userTag, const void* data, size_t size) { +#if SPIFFS_USER_METALEN if(userTag > 255) { return Error::BadParam; } @@ -255,6 +262,11 @@ struct SpiffsMetaBuffer { i += tagSize; } +#else + (void)userTag; + (void)data; + (void)size; +#endif // No room for attribute return Error::BufferTooSmall; diff --git a/Sming/Wiring/WString.cpp b/Sming/Wiring/WString.cpp index 2cf141fbe2..d32e20afe2 100644 --- a/Sming/Wiring/WString.cpp +++ b/Sming/Wiring/WString.cpp @@ -289,8 +289,8 @@ void String::move(String& rhs) auto rhs_len = rhs.length(); if(rhs.sso.set) { - // Switch to SSO if required - reserve(rhs_len); + // Switch to SSO if required (note: we don't rely on this succeeding) + (void)reserve(rhs_len); } // If we already have capacity, copy the data and free rhs buffers From 40e22c3e6c79549e044b97adb67de0d6c4a126dc Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 25 Apr 2021 09:40:22 +0100 Subject: [PATCH 013/130] FWFS Mountpoint Support (#2315) **Remove object stores** LittleFS is an excellent choice for flexible metadata handling, making further development of FWFS writeable object stores a rather pointless excercise. This PR reverts FWFS back to pre-object store model which is faster and simpler. This is largely an internal change and does not affect applications, but does simplify operation considerably as the filing system is now just mounted directly on a partition. **Mountpoint support** Images can be built with nominated directories as mount-points. Instead of object stores, filesystems (LittleFS, SPIFFS, etc) can be mounted there instead using the new `IFileSystem::setVolume` method. See `Basic_IFS` sample for a demonstration of how this works. **Remove object alignment** This is a **Breaking change** and will require existing applications to rebuild their FWFS images. FWFS started life as a binary image linked into the firmware (IMPORT_FSTR) so benefitted from having object headers word-aligned. This was fast but inflexible, and with a partition-based system alignment is not necessary so the code can be simplified by removing this. It also saves a little space which could be significant for large numbers of small files, or files with large numbers of small attributes. **Fixes** Volume ID missing from returned FWFS filesystem information. LittleFS `stat` information sometimes a bit weird because structure initialisation not performed. Additional parameter checking to avoid exceptions calling filesystem functions if not mounted. Additional checks on paths, use general `isRootPath()` function. Fail FWFS file open requests with write access **Other changes** Add `FWFS_CACHE_SPACING` build variable to balance RAM usage vs. lookup speed. Allocate `opendir` descriptors on heap instead of consuming a regular file handle. This allows recursive directory enumeration without running out of handles. Add performance module to test application to help evaluate filesystems. Only generate MD5 Hash attributes for files. Previously directories were included, which is not necessary. Improve FWFS performance by caching root directory. --- Sming/Components/IFS | 2 +- Sming/Libraries/LittleFS | 2 +- Sming/Libraries/Spiffs/src/FileSystem.cpp | 51 ++++++++++++++++------- samples/Basic_IFS/app/application.cpp | 46 +++++++++++++++----- samples/Basic_IFS/fsimage.fwfs | 3 +- 5 files changed, 75 insertions(+), 29 deletions(-) diff --git a/Sming/Components/IFS b/Sming/Components/IFS index 7054533606..0b3588a4db 160000 --- a/Sming/Components/IFS +++ b/Sming/Components/IFS @@ -1 +1 @@ -Subproject commit 70545336066e93e11bd5e6a70555c8a04d2e1b03 +Subproject commit 0b3588a4db2f0650bc9f57cf0edcfb0cb12272fc diff --git a/Sming/Libraries/LittleFS b/Sming/Libraries/LittleFS index 4b3ecd8ba8..5d41c9e136 160000 --- a/Sming/Libraries/LittleFS +++ b/Sming/Libraries/LittleFS @@ -1 +1 @@ -Subproject commit 4b3ecd8ba82bb9121ac4faa64f3bf4845b5d9d8c +Subproject commit 5d41c9e136dc5d0a15118de6ed1c6a1fc9c84c5f diff --git a/Sming/Libraries/Spiffs/src/FileSystem.cpp b/Sming/Libraries/Spiffs/src/FileSystem.cpp index d779f4f5c7..a698b4468a 100644 --- a/Sming/Libraries/Spiffs/src/FileSystem.cpp +++ b/Sming/Libraries/Spiffs/src/FileSystem.cpp @@ -27,6 +27,11 @@ namespace IFS { namespace SPIFFS { +#define CHECK_MOUNTED() \ + if(!SPIFFS_mounted(handle())) { \ + return Error::NotMounted; \ + } + struct FileDir { char path[SPIFFS_OBJ_NAME_LEN]; ///< Filter for readdir() unsigned pathlen; @@ -34,12 +39,6 @@ struct FileDir { spiffs_DIR d; }; -#define GET_FILEDIR() \ - if(dir == nullptr) { \ - return Error::InvalidHandle; \ - } \ - auto d = reinterpret_cast(dir); - constexpr uint32_t logicalBlockSize{4096 * 2}; namespace @@ -210,6 +209,7 @@ int FileSystem::getinfo(Info& info) info.type = Type::SPIFFS; info.maxNameLength = SPIFFS_OBJ_NAME_LEN - 1; info.maxPathLength = info.maxNameLength; + if(SPIFFS_mounted(handle())) { info.volumeID = fs.config_magic; info.attr |= Attribute::Mounted; @@ -247,8 +247,7 @@ String FileSystem::getErrorString(int err) FileHandle FileSystem::open(const char* path, OpenFlags flags) { - FS_CHECK_PATH(path); - if(path == nullptr) { + if(isRootPath(path)) { return Error::BadParam; } @@ -295,6 +294,8 @@ FileHandle FileSystem::open(const char* path, OpenFlags flags) int FileSystem::close(FileHandle file) { + CHECK_MOUNTED() + if(file < 0) { return Error::FileNotOpen; } @@ -327,6 +328,8 @@ int FileSystem::ftruncate(FileHandle file, size_t new_size) int FileSystem::flush(FileHandle file) { + CHECK_MOUNTED() + int res = flushMeta(file); int err = SPIFFS_fflush(handle(), file); if(err < 0) { @@ -431,6 +434,17 @@ int FileSystem::flushMeta(FileHandle file) int FileSystem::stat(const char* path, Stat* stat) { + CHECK_MOUNTED() + + if(isRootPath(path)) { + if(stat != nullptr) { + *stat = Stat{}; + stat->fs = this; + stat->attr += FileAttribute::Directory; + } + return FS_OK; + } + spiffs_stat ss; int err = SPIFFS_stat(handle(), path ?: "", &ss); if(err < 0) { @@ -486,6 +500,8 @@ int FileSystem::fstat(FileHandle file, Stat* stat) int FileSystem::fsetxattr(FileHandle file, AttributeTag tag, const void* data, size_t size) { + CHECK_MOUNTED() + auto smb = getMetaBuffer(file); if(smb == nullptr) { return Error::InvalidHandle; @@ -495,6 +511,8 @@ int FileSystem::fsetxattr(FileHandle file, AttributeTag tag, const void* data, s int FileSystem::fgetxattr(FileHandle file, AttributeTag tag, void* buffer, size_t size) { + CHECK_MOUNTED() + auto smb = getMetaBuffer(file); if(smb == nullptr) { return Error::InvalidHandle; @@ -505,7 +523,7 @@ int FileSystem::fgetxattr(FileHandle file, AttributeTag tag, void* buffer, size_ int FileSystem::setxattr(const char* path, AttributeTag tag, const void* data, size_t size) { #ifdef SPIFFS_STORE_META - FS_CHECK_PATH(path); + FS_CHECK_PATH(path) spiffs_stat ss; int err = SPIFFS_stat(handle(), path ?: "", &ss); if(err < 0) { @@ -530,7 +548,7 @@ int FileSystem::setxattr(const char* path, AttributeTag tag, const void* data, s int FileSystem::getxattr(const char* path, AttributeTag tag, void* buffer, size_t size) { #ifdef SPIFFS_STORE_META - FS_CHECK_PATH(path); + FS_CHECK_PATH(path) spiffs_stat ss; int err = SPIFFS_stat(handle(), path, &ss); if(err < 0) { @@ -546,7 +564,9 @@ int FileSystem::getxattr(const char* path, AttributeTag tag, void* buffer, size_ int FileSystem::opendir(const char* path, DirHandle& dir) { - FS_CHECK_PATH(path); + CHECK_MOUNTED() + + isRootPath(path); unsigned pathlen = 0; if(path != nullptr) { pathlen = strlen(path); @@ -696,9 +716,7 @@ int FileSystem::mkdir(const char* path) int FileSystem::rename(const char* oldpath, const char* newpath) { - FS_CHECK_PATH(oldpath); - FS_CHECK_PATH(newpath); - if(oldpath == nullptr || newpath == nullptr) { + if(isRootPath(oldpath) || isRootPath(newpath)) { return Error::BadParam; } @@ -708,8 +726,7 @@ int FileSystem::rename(const char* oldpath, const char* newpath) int FileSystem::remove(const char* path) { - FS_CHECK_PATH(path); - if(path == nullptr) { + if(isRootPath(path)) { return Error::BadParam; } @@ -733,6 +750,8 @@ int FileSystem::remove(const char* path) int FileSystem::fremove(FileHandle file) { + CHECK_MOUNTED() + // If file is marked read-only, fail request auto smb = getMetaBuffer(file); if(smb == nullptr) { diff --git a/samples/Basic_IFS/app/application.cpp b/samples/Basic_IFS/app/application.cpp index a721a7907f..e1a9223c1c 100644 --- a/samples/Basic_IFS/app/application.cpp +++ b/samples/Basic_IFS/app/application.cpp @@ -110,10 +110,8 @@ bool initFileSystem() { fileFreeFileSystem(); -#if DEBUG_BUILD - auto freeheap = system_get_free_heap_size(); -#endif - debug_i("1: heap = %u", freeheap); + auto initialFreeheap = system_get_free_heap_size(); + debug_i("Initial freeheap = %u", initialFreeheap); #ifdef ENABLE_FLASHSTRING_IMAGE // Create a partition wrapping some flashstring data @@ -124,25 +122,53 @@ bool initFileSystem() // Read-only auto fs = IFS::createFirmwareFilesystem(part); - debug_i("2: heap = -%u", freeheap - system_get_free_heap_size()); if(fs == nullptr) { debug_e("Failed to created filesystem object"); return false; } - int res = fs->mount(); - debug_i("3: heap = -%u", freeheap - system_get_free_heap_size()); - - debug_i("mount() returned %d (%s)", res, fs->getErrorString(res).c_str()); + auto mount = [&](IFS::FileSystem* fs) { + int res = fs->mount(); + debug_i("heap used: %u, mount() returned %d (%s)", initialFreeheap - system_get_free_heap_size(), res, + fs->getErrorString(res).c_str()); + return res == FS_OK; + }; - if(res < 0) { + if(!mount(fs)) { delete fs; return false; } + // Make this the default filesystem fileSetFileSystem(fs); + // Let's mount an LFS volume as well + initialFreeheap = system_get_free_heap_size(); + part = Storage::findDefaultPartition(Storage::Partition::SubType::Data::littlefs); + auto lfs = IFS::createLfsFilesystem(part); + if(lfs == nullptr) { + debug_e("Failed to create LFS filesystem"); + } else if(mount(lfs)) { + // Place the root of this volume at index #0 (the corresponding directory is given in `fwimage.fwfs`) + fs->setVolume(0, lfs); + } else { + delete lfs; + } + + // And we'll mount a SPIFFS volume too + initialFreeheap = system_get_free_heap_size(); + part = Storage::findDefaultPartition(Storage::Partition::SubType::Data::spiffs); + auto spiffs = IFS::createSpiffsFilesystem(part); + if(spiffs == nullptr) { + debug_e("Failed to create SPIFFS filesystem"); + } else if(mount(spiffs)) { + // Place the root of this volume at index #1 + fs->setVolume(1, spiffs); + } else { + delete spiffs; + } + debug_i("File system initialised"); return true; } diff --git a/samples/Basic_IFS/fsimage.fwfs b/samples/Basic_IFS/fsimage.fwfs index b8e7cbe19a..8e63a810cd 100644 --- a/samples/Basic_IFS/fsimage.fwfs +++ b/samples/Basic_IFS/fsimage.fwfs @@ -15,7 +15,8 @@ }, // Directories to mount other object stores "mountpoints": { - "config": 1 + "littlefs": 0, + "spiffs": 1 }, // Rules for file metadata. All rules are evaluated in sequence for every file "rules": [ From 639c06b33f4d0d3de743208c86b574d9db286aa6 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 26 Apr 2021 16:35:26 +0100 Subject: [PATCH 014/130] Fix bug in StreamTransformer - ending fragment not getting sent (#2317) * Fix bug in StreamTransformer - ending fragment not getting sent * Add test to verify fix Before: ``` > ChunkedStream / StreamTransformer 758 OK: output.moveString(s) OUTPUT: 65 0d 0a 53 6f 6d 65 20 74 65 73 74 20 64 61 74 e..Some test dat 61 0d 0a a.. 792 FAIL: FS_OUTPUT == s FAIL in `virtual void StreamTest::execute()` ``` After: ``` >> ChunkedStream / StreamTransformer 741 OK: output.moveString(s) OUTPUT: 65 0d 0a 53 6f 6d 65 20 74 65 73 74 20 64 61 74 e..Some test dat 61 0d 0a 30 0d 0a 0d 0a a..0.... 760 OK: FS_OUTPUT == s ``` --- Sming/Core/Data/Buffer/CircularBuffer.cpp | 2 +- Sming/Core/Data/StreamTransformer.cpp | 6 ++++-- tests/HostTests/modules/Stream.cpp | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Sming/Core/Data/Buffer/CircularBuffer.cpp b/Sming/Core/Data/Buffer/CircularBuffer.cpp index 6f0a221384..9e9064bf71 100644 --- a/Sming/Core/Data/Buffer/CircularBuffer.cpp +++ b/Sming/Core/Data/Buffer/CircularBuffer.cpp @@ -68,7 +68,7 @@ size_t CircularBuffer::room() const size_t CircularBuffer::write(uint8_t charToWrite) { - if(!room()) { + if(room() == 0) { return 0; } diff --git a/Sming/Core/Data/StreamTransformer.cpp b/Sming/Core/Data/StreamTransformer.cpp index 4090ec6122..f719e4698f 100644 --- a/Sming/Core/Data/StreamTransformer.cpp +++ b/Sming/Core/Data/StreamTransformer.cpp @@ -37,7 +37,7 @@ void StreamTransformer::fillTempStream(char* buffer, size_t bufSize) while((room = tempStream->room()) >= maxChunkSize) { auto chunkSize = sourceStream->readMemoryBlock(buffer, maxChunkSize); if(chunkSize == 0) { - return; + break; } saveState(); @@ -55,7 +55,9 @@ void StreamTransformer::fillTempStream(char* buffer, size_t bufSize) if(sourceStream->isFinished()) { auto outLength = transform(nullptr, 0, result, resultSize); - tempStream->write(result, outLength); + auto written = tempStream->write(result, outLength); + (void)written; + assert(written == outLength); } } diff --git a/tests/HostTests/modules/Stream.cpp b/tests/HostTests/modules/Stream.cpp index 90802023ac..a3d98ffa83 100644 --- a/tests/HostTests/modules/Stream.cpp +++ b/tests/HostTests/modules/Stream.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include DEFINE_FSTR_LOCAL(template1, "Stream containing {var1}, {var2} and {var3}. {} {{}} {{12345") @@ -86,6 +87,19 @@ class StreamTest : public TestGroup REQUIRE(Resource::image_png == s); } + TEST_CASE("ChunkedStream / StreamTransformer") + { + DEFINE_FSTR_LOCAL(FS_INPUT, "Some test data"); + DEFINE_FSTR_LOCAL(FS_OUTPUT, "e\r\nSome test data\r\n0\r\n\r\n"); + ChunkedStream chunked(new FlashMemoryStream(FS_INPUT)); + MemoryDataStream output; + output.copyFrom(&chunked); + String s; + REQUIRE(output.moveString(s)); + m_printHex("OUTPUT", s.c_str(), s.length()); + REQUIRE(FS_OUTPUT == s); + } + TEST_CASE("MultipartStream / MultiStream") { unsigned itemIndex{0}; From 8b853ff9f9cc3a1f6248f064c3a336633226e018 Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 27 Apr 2021 20:18:01 +0100 Subject: [PATCH 015/130] Pull Core/Network out into separate Component (#2316) The `DISABLE_WIFI` setting was introduced as an experimental feature for Esp8266 to reduce image sizes. This PR moves the networking support code into a separate library so it doesn't need to be built for these applications. This also improves built times for tools using the Host architecture, for example the LittleFS `fscopy` tool. The related code for each architecture has also been moved, including related units from `Platform`. Documentation has been revised and a few minor fixes included. --- .../Esp32/Components/sming-arch/component.mk | 13 --- .../Components/spi_flash/include/flashmem.h | 2 +- .../Esp8266/Components/esp8266/component.mk | 5 + .../Esp8266/Components/sming-arch/README.rst | 10 +- .../Components/sming-arch/component.mk | 18 --- .../Components/spi_flash/include/flashmem.h | 2 +- .../Arch/Host/Components/hostlib/component.mk | 10 +- .../Arch/Host/Components/hostlib/startup.cpp | 39 +++++-- .../Host/Components/sming-arch/component.mk | 8 -- Sming/Arch/Host/README.rst | 2 +- Sming/Arch/Host/System/include/user_config.h | 2 + .../Arch/Esp32/Platform/AccessPointImpl.cpp | 0 .../Arch/Esp32/Platform/AccessPointImpl.h | 0 .../Arch/Esp32/Platform/LwipAdapter.cpp | 0 .../Esp32/Platform/LwipAdapter.cpp.changed | 0 .../Arch/Esp32/Platform/LwipAdapter.cpp.copy | 0 .../Arch/Esp32/Platform/LwipAdapter.h | 0 .../Arch/Esp32/Platform/StationImpl.cpp | 0 .../Arch/Esp32/Platform/StationImpl.h | 0 .../Arch/Esp32/Platform/WifiEventsImpl.cpp | 0 .../Arch/Esp32/Platform/WifiEventsImpl.h | 0 .../Arch/Esp8266/Platform/AccessPointImpl.cpp | 0 .../Arch/Esp8266/Platform/AccessPointImpl.h | 0 .../Arch/Esp8266/Platform/StationImpl.cpp | 0 .../Arch/Esp8266/Platform/StationImpl.h | 0 .../Arch/Esp8266/Platform/WifiEventsImpl.cpp | 0 .../Arch/Esp8266/Platform/WifiEventsImpl.h | 0 .../Arch/Esp8266/Platform/WifiSniffer.cpp | 0 .../Arch/Host/Platform/AccessPointImpl.cpp | 0 .../Arch/Host/Platform/AccessPointImpl.h | 0 .../Arch/Host/Platform/StationImpl.cpp | 0 .../Network}/Arch/Host/Platform/StationImpl.h | 0 .../Arch/Host/Platform/WifiEventsImpl.cpp | 0 .../Arch/Host/Platform/WifiEventsImpl.h | 0 Sming/Components/Network/README.rst | 19 +++ Sming/Components/Network/component.mk | 60 ++++++++++ .../Components/Network}/dns.rst | 0 .../Components/Network}/ftp.rst | 0 .../Components/Network}/http.rst | 0 .../Components/Network}/mqtt.rst | 0 .../Components/Network}/ntp.rst | 0 .../Components/Network}/smtp.rst | 0 .../src}/Data/Stream/MultipartStream.cpp | 4 +- .../Data/Stream/UrlencodedOutputStream.cpp | 0 .../src}/Data/Stream/UrlencodedOutputStream.h | 2 +- .../Network/src}/IpAddress.cpp | 0 .../Network/src}/IpAddress.h | 0 .../Network/src}/Network/DnsServer.cpp | 0 .../Network/src}/Network/DnsServer.h | 4 +- .../src}/Network/Ftp/FtpDataFileList.h | 0 .../src}/Network/Ftp/FtpDataRetrieve.h | 0 .../Network/src}/Network/Ftp/FtpDataStore.h | 0 .../Network/src}/Network/Ftp/FtpDataStream.h | 0 .../src}/Network/Ftp/FtpServerConnection.cpp | 0 .../src}/Network/Ftp/FtpServerConnection.h | 2 +- .../Network/src}/Network/FtpServer.cpp | 0 .../Network/src}/Network/FtpServer.h | 0 .../src}/Network/Http/BasicHttpHeaders.cpp | 0 .../src}/Network/Http/BasicHttpHeaders.h | 0 .../src}/Network/Http/HttpBodyParser.cpp | 2 +- .../src}/Network/Http/HttpBodyParser.h | 0 .../Network/Http/HttpClientConnection.cpp | 0 .../src}/Network/Http/HttpClientConnection.h | 0 .../Network/src}/Network/Http/HttpCommon.cpp | 0 .../Network/src}/Network/Http/HttpCommon.h | 2 +- .../src}/Network/Http/HttpConnection.cpp | 0 .../src}/Network/Http/HttpConnection.h | 0 .../src}/Network/Http/HttpHeaderBuilder.h | 0 .../src}/Network/Http/HttpHeaderFields.cpp | 0 .../src}/Network/Http/HttpHeaderFields.h | 0 .../Network/src}/Network/Http/HttpHeaders.cpp | 0 .../Network/src}/Network/Http/HttpHeaders.h | 0 .../Network/src}/Network/Http/HttpParams.cpp | 2 +- .../Network/src}/Network/Http/HttpParams.h | 0 .../Network/src}/Network/Http/HttpRequest.cpp | 0 .../Network/src}/Network/Http/HttpRequest.h | 0 .../src}/Network/Http/HttpRequestAuth.cpp | 2 +- .../src}/Network/Http/HttpRequestAuth.h | 0 .../Network/src}/Network/Http/HttpResource.h | 0 .../src}/Network/Http/HttpResourceTree.cpp | 0 .../src}/Network/Http/HttpResourceTree.h | 0 .../src}/Network/Http/HttpResponse.cpp | 2 +- .../Network/src}/Network/Http/HttpResponse.h | 0 .../Network/Http/HttpServerConnection.cpp | 2 +- .../src}/Network/Http/HttpServerConnection.h | 0 .../Http/Websocket/WebsocketConnection.cpp | 9 +- .../Http/Websocket/WebsocketConnection.h | 0 .../Http/Websocket/WebsocketResource.cpp | 0 .../Http/Websocket/WebsocketResource.h | 0 .../Http/Websocket/WsCommandHandlerResource.h | 0 .../Network/src}/Network/HttpClient.cpp | 0 .../Network/src}/Network/HttpClient.h | 0 .../Network/src}/Network/HttpServer.cpp | 0 .../Network/src}/Network/HttpServer.h | 0 .../Network/src}/Network/IpConnection.cpp | 0 .../Network/src}/Network/IpConnection.h | 2 +- .../Network/src/Network}/MailMessage.cpp | 2 +- .../Network/src/Network}/MailMessage.h | 10 +- .../src}/Network/Mqtt/MqttPayloadParser.cpp | 0 .../src}/Network/Mqtt/MqttPayloadParser.h | 0 .../Network/src}/Network/MqttClient.cpp | 0 .../Network/src}/Network/MqttClient.h | 0 .../Network/src}/Network/NetUtils.cpp | 0 .../Network/src}/Network/NetUtils.h | 0 .../Network/src}/Network/NtpClient.cpp | 0 .../Network/src}/Network/NtpClient.h | 0 .../Network/src}/Network/SmtpClient.cpp | 2 +- .../Network/src}/Network/SmtpClient.h | 2 +- .../Network/src}/Network/TcpClient.cpp | 0 .../Network/src}/Network/TcpClient.h | 0 .../Network/src}/Network/TcpConnection.cpp | 0 .../Network/src}/Network/TcpConnection.h | 0 .../Network/src}/Network/TcpServer.cpp | 0 .../Network/src}/Network/TcpServer.h | 0 .../Network/src}/Network/TelnetServer.cpp | 0 .../Network/src}/Network/TelnetServer.h | 0 .../Network/src}/Network/UdpConnection.cpp | 0 .../Network/src}/Network/UdpConnection.h | 0 .../Network/src}/Network/Url.cpp | 2 +- .../Network/src}/Network/Url.h | 0 .../Network/src/Network/WebHelpers/base64.h | 2 + .../Network/src/Network/WebHelpers/escape.h | 2 + .../Network/src}/Network/WebsocketClient.cpp | 8 +- .../Network/src}/Network/WebsocketClient.h | 0 .../Network/src}/Platform/AccessPoint.cpp | 0 .../Network/src}/Platform/AccessPoint.h | 0 .../Network/src}/Platform/BssInfo.cpp | 0 .../Network/src}/Platform/BssInfo.h | 0 .../Network/src}/Platform/Station.cpp | 0 .../Network/src}/Platform/Station.h | 0 .../Network/src}/Platform/WifiEvents.cpp | 0 .../Network/src}/Platform/WifiEvents.h | 0 .../Network/src}/Platform/WifiSniffer.h | 2 +- .../Components/Network}/tcp.rst | 0 .../Components/Network}/telnet.rst | 0 .../Components/Network}/udp.rst | 0 .../Components/Network}/url.rst | 0 .../Components/Network}/websocket.rst | 0 Sming/Components/Storage/README.rst | 2 +- .../Storage/src/include/Storage/Device.h | 2 +- Sming/Components/rboot/component.mk | 4 + .../src/{ => Network}/RbootHttpUpdater.cpp | 0 Sming/Core/Data/Format/Formatter.h | 2 +- Sming/Core/Data/Format/Html.cpp | 2 +- Sming/Core/Data/Stream/DataSourceStream.h | 2 +- .../Data/Stream/IFS/HtmlDirectoryTemplate.cpp | 4 +- Sming/Core/Data/Stream/SectionTemplate.cpp | 2 +- Sming/Core/{Network => Data}/WebConstants.cpp | 0 Sming/Core/{Network => Data}/WebConstants.h | 0 .../{Network => Data}/WebHelpers/base64.cpp | 0 .../{Network => Data}/WebHelpers/base64.h | 0 .../{Network => Data}/WebHelpers/escape.cpp | 0 .../{Network => Data}/WebHelpers/escape.h | 0 Sming/Core/Network/WebHelpers/aw-sha1.cpp | 110 ------------------ Sming/Core/Network/WebHelpers/aw-sha1.h | 44 ------- Sming/Core/SmingCore.h | 5 +- Sming/Libraries/HueEmulator | 2 +- Sming/Libraries/LittleFS | 2 +- Sming/Libraries/OtaUpgradeMqtt/README.rst | 2 +- .../OtaUpgradeMqtt/samples/Upgrade/README.rst | 6 +- .../Spiffs/src/include/spiffs_config.h | 9 ++ Sming/Libraries/UPnP | 2 +- Sming/README.rst | 21 ++++ .../CommandProcessing/CommandDelegate.h | 4 +- .../CommandProcessing/CommandExecutor.cpp | 14 ++- .../CommandProcessing/CommandExecutor.h | 9 +- .../CommandProcessing/CommandHandler.cpp | 2 + .../CommandProcessing/CommandOutput.cpp | 20 ++-- .../CommandProcessing/CommandOutput.h | 22 +++- Sming/SmingCore/SmingCore.h | 2 +- Sming/Wiring/FlashString.h | 2 +- Sming/Wiring/WiringFrameworkIncludes.h | 7 +- Sming/building.rst | 2 +- Sming/component.mk | 48 ++------ .../framework/core/data/cstringarray.rst | 4 +- docs/source/framework/core/index.rst | 1 - docs/source/framework/core/network/index.rst | 10 -- docs/source/framework/index.rst | 2 + docs/source/getting-started/windows/index.rst | 4 +- docs/source/index.rst | 2 +- .../source/information/develop/components.rst | 4 +- .../information/develop/documentation.rst | 4 +- docs/source/information/events.rst | 8 +- .../source/troubleshooting/random-restart.rst | 2 +- docs/source/upgrading/4.1-4.2.rst | 2 +- docs/source/upgrading/4.2-4.3.rst | 4 +- docs/source/upgrading/4.3-4.4.rst | 19 +++ docs/source/upgrading/index.rst | 1 + samples/Basic_Utility/component.mk | 1 + samples/FtpServer_Files/fsimage.fwfs | 4 +- tests/HostTests/modules/Base64.cpp | 2 +- tests/HostTests/modules/Http.cpp | 2 +- tests/HostTests/modules/Stream.cpp | 2 +- 193 files changed, 329 insertions(+), 361 deletions(-) rename Sming/{ => Components/Network}/Arch/Esp32/Platform/AccessPointImpl.cpp (100%) rename Sming/{ => Components/Network}/Arch/Esp32/Platform/AccessPointImpl.h (100%) rename Sming/{ => Components/Network}/Arch/Esp32/Platform/LwipAdapter.cpp (100%) rename Sming/{ => Components/Network}/Arch/Esp32/Platform/LwipAdapter.cpp.changed (100%) rename Sming/{ => Components/Network}/Arch/Esp32/Platform/LwipAdapter.cpp.copy (100%) rename Sming/{ => Components/Network}/Arch/Esp32/Platform/LwipAdapter.h (100%) rename Sming/{ => Components/Network}/Arch/Esp32/Platform/StationImpl.cpp (100%) rename Sming/{ => Components/Network}/Arch/Esp32/Platform/StationImpl.h (100%) rename Sming/{ => Components/Network}/Arch/Esp32/Platform/WifiEventsImpl.cpp (100%) rename Sming/{ => Components/Network}/Arch/Esp32/Platform/WifiEventsImpl.h (100%) rename Sming/{ => Components/Network}/Arch/Esp8266/Platform/AccessPointImpl.cpp (100%) rename Sming/{ => Components/Network}/Arch/Esp8266/Platform/AccessPointImpl.h (100%) rename Sming/{ => Components/Network}/Arch/Esp8266/Platform/StationImpl.cpp (100%) rename Sming/{ => Components/Network}/Arch/Esp8266/Platform/StationImpl.h (100%) rename Sming/{ => Components/Network}/Arch/Esp8266/Platform/WifiEventsImpl.cpp (100%) rename Sming/{ => Components/Network}/Arch/Esp8266/Platform/WifiEventsImpl.h (100%) rename Sming/{ => Components/Network}/Arch/Esp8266/Platform/WifiSniffer.cpp (100%) rename Sming/{ => Components/Network}/Arch/Host/Platform/AccessPointImpl.cpp (100%) rename Sming/{ => Components/Network}/Arch/Host/Platform/AccessPointImpl.h (100%) rename Sming/{ => Components/Network}/Arch/Host/Platform/StationImpl.cpp (100%) rename Sming/{ => Components/Network}/Arch/Host/Platform/StationImpl.h (100%) rename Sming/{ => Components/Network}/Arch/Host/Platform/WifiEventsImpl.cpp (100%) rename Sming/{ => Components/Network}/Arch/Host/Platform/WifiEventsImpl.h (100%) create mode 100644 Sming/Components/Network/README.rst create mode 100644 Sming/Components/Network/component.mk rename {docs/source/framework/core/network => Sming/Components/Network}/dns.rst (100%) rename {docs/source/framework/core/network => Sming/Components/Network}/ftp.rst (100%) rename {docs/source/framework/core/network => Sming/Components/Network}/http.rst (100%) rename {docs/source/framework/core/network => Sming/Components/Network}/mqtt.rst (100%) rename {docs/source/framework/core/network => Sming/Components/Network}/ntp.rst (100%) rename {docs/source/framework/core/network => Sming/Components/Network}/smtp.rst (100%) rename Sming/{Core => Components/Network/src}/Data/Stream/MultipartStream.cpp (95%) rename Sming/{Core => Components/Network/src}/Data/Stream/UrlencodedOutputStream.cpp (100%) rename Sming/{Core => Components/Network/src}/Data/Stream/UrlencodedOutputStream.h (96%) rename Sming/{Wiring => Components/Network/src}/IpAddress.cpp (100%) rename Sming/{Wiring => Components/Network/src}/IpAddress.h (100%) rename Sming/{Core => Components/Network/src}/Network/DnsServer.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/DnsServer.h (98%) rename Sming/{Core => Components/Network/src}/Network/Ftp/FtpDataFileList.h (100%) rename Sming/{Core => Components/Network/src}/Network/Ftp/FtpDataRetrieve.h (100%) rename Sming/{Core => Components/Network/src}/Network/Ftp/FtpDataStore.h (100%) rename Sming/{Core => Components/Network/src}/Network/Ftp/FtpDataStream.h (100%) rename Sming/{Core => Components/Network/src}/Network/Ftp/FtpServerConnection.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/Ftp/FtpServerConnection.h (98%) rename Sming/{Core => Components/Network/src}/Network/FtpServer.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/FtpServer.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/BasicHttpHeaders.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/Http/BasicHttpHeaders.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpBodyParser.cpp (98%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpBodyParser.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpClientConnection.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpClientConnection.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpCommon.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpCommon.h (98%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpConnection.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpConnection.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpHeaderBuilder.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpHeaderFields.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpHeaderFields.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpHeaders.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpHeaders.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpParams.cpp (98%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpParams.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpRequest.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpRequest.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpRequestAuth.cpp (97%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpRequestAuth.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpResource.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpResourceTree.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpResourceTree.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpResponse.cpp (99%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpResponse.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpServerConnection.cpp (99%) rename Sming/{Core => Components/Network/src}/Network/Http/HttpServerConnection.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/Websocket/WebsocketConnection.cpp (97%) rename Sming/{Core => Components/Network/src}/Network/Http/Websocket/WebsocketConnection.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/Websocket/WebsocketResource.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/Http/Websocket/WebsocketResource.h (100%) rename Sming/{Core => Components/Network/src}/Network/Http/Websocket/WsCommandHandlerResource.h (100%) rename Sming/{Core => Components/Network/src}/Network/HttpClient.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/HttpClient.h (100%) rename Sming/{Core => Components/Network/src}/Network/HttpServer.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/HttpServer.h (100%) rename Sming/{Core => Components/Network/src}/Network/IpConnection.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/IpConnection.h (98%) rename Sming/{Core/Data => Components/Network/src/Network}/MailMessage.cpp (98%) rename Sming/{Core/Data => Components/Network/src/Network}/MailMessage.h (92%) rename Sming/{Core => Components/Network/src}/Network/Mqtt/MqttPayloadParser.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/Mqtt/MqttPayloadParser.h (100%) rename Sming/{Core => Components/Network/src}/Network/MqttClient.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/MqttClient.h (100%) rename Sming/{Core => Components/Network/src}/Network/NetUtils.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/NetUtils.h (100%) rename Sming/{Core => Components/Network/src}/Network/NtpClient.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/NtpClient.h (100%) rename Sming/{Core => Components/Network/src}/Network/SmtpClient.cpp (99%) rename Sming/{Core => Components/Network/src}/Network/SmtpClient.h (99%) rename Sming/{Core => Components/Network/src}/Network/TcpClient.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/TcpClient.h (100%) rename Sming/{Core => Components/Network/src}/Network/TcpConnection.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/TcpConnection.h (100%) rename Sming/{Core => Components/Network/src}/Network/TcpServer.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/TcpServer.h (100%) rename Sming/{Core => Components/Network/src}/Network/TelnetServer.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/TelnetServer.h (100%) rename Sming/{Core => Components/Network/src}/Network/UdpConnection.cpp (100%) rename Sming/{Core => Components/Network/src}/Network/UdpConnection.h (100%) rename Sming/{Core => Components/Network/src}/Network/Url.cpp (98%) rename Sming/{Core => Components/Network/src}/Network/Url.h (100%) create mode 100644 Sming/Components/Network/src/Network/WebHelpers/base64.h create mode 100644 Sming/Components/Network/src/Network/WebHelpers/escape.h rename Sming/{Core => Components/Network/src}/Network/WebsocketClient.cpp (93%) rename Sming/{Core => Components/Network/src}/Network/WebsocketClient.h (100%) rename Sming/{ => Components/Network/src}/Platform/AccessPoint.cpp (100%) rename Sming/{ => Components/Network/src}/Platform/AccessPoint.h (100%) rename Sming/{ => Components/Network/src}/Platform/BssInfo.cpp (100%) rename Sming/{ => Components/Network/src}/Platform/BssInfo.h (100%) rename Sming/{ => Components/Network/src}/Platform/Station.cpp (100%) rename Sming/{ => Components/Network/src}/Platform/Station.h (100%) rename Sming/{ => Components/Network/src}/Platform/WifiEvents.cpp (100%) rename Sming/{ => Components/Network/src}/Platform/WifiEvents.h (100%) rename Sming/{ => Components/Network/src}/Platform/WifiSniffer.h (99%) rename {docs/source/framework/core/network => Sming/Components/Network}/tcp.rst (100%) rename {docs/source/framework/core/network => Sming/Components/Network}/telnet.rst (100%) rename {docs/source/framework/core/network => Sming/Components/Network}/udp.rst (100%) rename {docs/source/framework/core/network => Sming/Components/Network}/url.rst (100%) rename {docs/source/framework/core/network => Sming/Components/Network}/websocket.rst (100%) rename Sming/Components/rboot/src/{ => Network}/RbootHttpUpdater.cpp (100%) rename Sming/Core/{Network => Data}/WebConstants.cpp (100%) rename Sming/Core/{Network => Data}/WebConstants.h (100%) rename Sming/Core/{Network => Data}/WebHelpers/base64.cpp (100%) rename Sming/Core/{Network => Data}/WebHelpers/base64.h (100%) rename Sming/Core/{Network => Data}/WebHelpers/escape.cpp (100%) rename Sming/Core/{Network => Data}/WebHelpers/escape.h (100%) delete mode 100644 Sming/Core/Network/WebHelpers/aw-sha1.cpp delete mode 100644 Sming/Core/Network/WebHelpers/aw-sha1.h delete mode 100644 docs/source/framework/core/network/index.rst create mode 100644 docs/source/upgrading/4.3-4.4.rst diff --git a/Sming/Arch/Esp32/Components/sming-arch/component.mk b/Sming/Arch/Esp32/Components/sming-arch/component.mk index b32d5fae47..1051b13cb9 100644 --- a/Sming/Arch/Esp32/Components/sming-arch/component.mk +++ b/Sming/Arch/Esp32/Components/sming-arch/component.mk @@ -22,19 +22,6 @@ COMPONENT_DEPENDS := \ gdbstub \ esptool -# -DISABLE_WIFI ?= 0 -ifeq ($(DISABLE_WIFI),1) -GLOBAL_CFLAGS += -DDISABLE_WIFI=1 -endif - -# => Platform WiFi -COMPONENT_VARS := \ - ENABLE_WPS \ - ENABLE_SMART_CONFIG \ - DISABLE_WIFI - - # ELF and BIN files DEBUG_VARS += TARGET_BIN TARGET_OUT = $(BUILD_BASE)/$(APP_NAME).out diff --git a/Sming/Arch/Esp32/Components/spi_flash/include/flashmem.h b/Sming/Arch/Esp32/Components/spi_flash/include/flashmem.h index e1fe37a8d4..f1d8fa7a80 100644 --- a/Sming/Arch/Esp32/Components/spi_flash/include/flashmem.h +++ b/Sming/Arch/Esp32/Components/spi_flash/include/flashmem.h @@ -1,4 +1,4 @@ #pragma once -#pragma warning "Please update to #include " +#pragma GCC warning "Please update to #include " #include "esp_spi_flash.h" diff --git a/Sming/Arch/Esp8266/Components/esp8266/component.mk b/Sming/Arch/Esp8266/Components/esp8266/component.mk index e3843a245f..ff8ddb3495 100644 --- a/Sming/Arch/Esp8266/Components/esp8266/component.mk +++ b/Sming/Arch/Esp8266/Components/esp8266/component.mk @@ -11,6 +11,11 @@ FLASH_INIT_DATA_VCC = $(SDK_BASE)/bin/esp_init_data_vdd_default.bin CUSTOM_TARGETS += $(FLASH_INIT_DATA) $(FLASH_INIT_DATA_VCC) +# => LWIP basic support required by SDK +ifeq ($(DISABLE_WIFI),1) +COMPONENT_DEPENDS += esp-lwip +endif + # => 'Internal' SDK - for SDK Version 3+ as submodule in Sming repository # SDK_BASE just needs to point into our repo as it's overridden with the correct submodule path # This provides backward-compatiblity, so $(SMING)/third-party/ESP8266_NONOS_SDK) still works diff --git a/Sming/Arch/Esp8266/Components/sming-arch/README.rst b/Sming/Arch/Esp8266/Components/sming-arch/README.rst index 0c005fcead..7ca74663cb 100644 --- a/Sming/Arch/Esp8266/Components/sming-arch/README.rst +++ b/Sming/Arch/Esp8266/Components/sming-arch/README.rst @@ -11,10 +11,14 @@ Default: OFF. In order to use SDK 3.0.0 or newer please follow the instructions No-WiFi build ------------- -.. envvar:: DISABLE_WIFI +.. note:: + + This is an EXPERIMENTAL feature. Not all hardware functions may be available. + +If a project does not require WiFi (or networking) then setting the :envvar:`DISABLE_WIFI` variable +will reduce code size and RAM usage significantly. +It does this using an un-official :component-esp8266:`esp_no_wifi` Component. - If a project does not require WiFi (or networking) bulding with this option enabled reduces code size - and memory usage signficantly. It does this using an un-official :component-esp8266:`esp_no_wifi` Component. Custom LWIP diff --git a/Sming/Arch/Esp8266/Components/sming-arch/component.mk b/Sming/Arch/Esp8266/Components/sming-arch/component.mk index bf050efef6..19e419c499 100644 --- a/Sming/Arch/Esp8266/Components/sming-arch/component.mk +++ b/Sming/Arch/Esp8266/Components/sming-arch/component.mk @@ -20,32 +20,14 @@ COMPONENT_DEPENDS := \ gdbstub \ spi_flash -# => Platform WiFi -COMPONENT_VARS := \ - ENABLE_WPS \ - ENABLE_SMART_CONFIG - # RELINK_VARS += DISABLE_WIFI -DISABLE_WIFI ?= 0 ifeq ($(DISABLE_WIFI),1) COMPONENT_DEPENDS += esp_no_wifi -GLOBAL_CFLAGS += -DDISABLE_WIFI=1 else COMPONENT_DEPENDS += esp_wifi endif -# => LWIP -COMPONENT_VARS += ENABLE_CUSTOM_LWIP -ENABLE_CUSTOM_LWIP ?= 1 -ifeq ($(ENABLE_CUSTOM_LWIP), 0) - COMPONENT_DEPENDS += esp-lwip -else ifeq ($(ENABLE_CUSTOM_LWIP), 1) - COMPONENT_DEPENDS += esp-open-lwip -else ifeq ($(ENABLE_CUSTOM_LWIP), 2) - COMPONENT_DEPENDS += lwip2 -endif - # rBoot creates ROM images from one or both of these targets TARGET_OUT_0 := $(BUILD_BASE)/$(APP_NAME)_0.out TARGET_OUT_1 := $(BUILD_BASE)/$(APP_NAME)_1.out diff --git a/Sming/Arch/Esp8266/Components/spi_flash/include/flashmem.h b/Sming/Arch/Esp8266/Components/spi_flash/include/flashmem.h index e1fe37a8d4..f1d8fa7a80 100644 --- a/Sming/Arch/Esp8266/Components/spi_flash/include/flashmem.h +++ b/Sming/Arch/Esp8266/Components/spi_flash/include/flashmem.h @@ -1,4 +1,4 @@ #pragma once -#pragma warning "Please update to #include " +#pragma GCC warning "Please update to #include " #include "esp_spi_flash.h" diff --git a/Sming/Arch/Host/Components/hostlib/component.mk b/Sming/Arch/Host/Components/hostlib/component.mk index 4fcce19dca..551f63afe4 100644 --- a/Sming/Arch/Host/Components/hostlib/component.mk +++ b/Sming/Arch/Host/Components/hostlib/component.mk @@ -4,7 +4,15 @@ ifeq ($(UNAME),Windows) EXTRA_LIBS += wsock32 endif -COMPONENT_DEPENDS := esp_wifi lwip driver spi_flash +COMPONENT_DEPENDS := \ + driver \ + spi_flash + +ifneq ($(DISABLE_WIFI),1) +COMPONENT_DEPENDS += \ + esp_wifi \ + lwip +endif COMPONENT_INCDIRS := include COMPONENT_SRCDIRS := . diff --git a/Sming/Arch/Host/Components/hostlib/startup.cpp b/Sming/Arch/Host/Components/hostlib/startup.cpp index b59632a496..0f650f0168 100644 --- a/Sming/Arch/Host/Components/hostlib/startup.cpp +++ b/Sming/Arch/Host/Components/hostlib/startup.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include "include/hostlib/init.h" #include "include/hostlib/emu.h" @@ -40,12 +39,16 @@ #include #include -static int exitCode = 0; -static bool done = false; -static bool lwip_initialised = false; +#ifndef DISABLE_WIFI +#include +extern void host_wifi_lwip_init_complete(); +static bool lwip_initialised; +#endif + +static int exitCode; +static bool done; static OneShotElapseTimer lwipServiceTimer; -extern void host_wifi_lwip_init_complete(); extern void host_init_bootloader(); static void cleanup() @@ -54,7 +57,9 @@ static void cleanup() host_flashmem_cleanup(); CUartServer::shutdown(); sockets_finalise(); +#ifndef DISABLE_WIFI host_lwip_shutdown(); +#endif host_debug_i("Goodbye!"); } @@ -112,10 +117,12 @@ void host_main_loop() { host_service_tasks(); host_service_timers(); - if (lwip_initialised && lwipServiceTimer.expired()) { +#ifndef DISABLE_WIFI + if(lwip_initialised && lwipServiceTimer.expired()) { host_lwip_service(); lwipServiceTimer.start(); } +#endif system_soft_wdt_feed(); } @@ -130,7 +137,9 @@ int main(int argc, char* argv[]) bool enable_network; UartServerConfig uart; FlashmemConfig flash; +#ifndef DISABLE_WIFI struct lwip_param lwip; +#endif } config = { .pause = -1, .exitpause = -1, @@ -147,11 +156,13 @@ int main(int argc, char* argv[]) .createSize = 0, }, +#ifndef DISABLE_WIFI .lwip = { .ifname = nullptr, .ipaddr = nullptr, }, +#endif }; option_tag_t opt; @@ -170,6 +181,13 @@ int main(int argc, char* argv[]) config.uart.portBase = atoi(arg); break; +#ifdef DISABLE_WIFI + case opt_ifname: + case opt_ipaddr: + case opt_gateway: + case opt_netmask: + break; +#else case opt_ifname: config.lwip.ifname = arg; break; @@ -185,6 +203,7 @@ int main(int argc, char* argv[]) case opt_netmask: config.lwip.netmask = arg; break; +#endif case opt_pause: config.pause = arg ? atoi(arg) : 0; @@ -214,7 +233,8 @@ int main(int argc, char* argv[]) host_debug_level = atoi(arg); break; - default:; + case opt_none: + break; } } @@ -245,7 +265,7 @@ int main(int argc, char* argv[]) sockets_initialise(); CUartServer::startup(config.uart); - +#ifndef DISABLE_WIFI if(config.enable_network) { lwip_initialised = host_lwip_init(&config.lwip); if(lwip_initialised) { @@ -254,6 +274,7 @@ int main(int argc, char* argv[]) } else { host_debug_i("Network initialisation skipped as requested"); } +#endif host_debug_i("If required, you may start terminal application(s) now"); pause(config.pause); @@ -264,7 +285,9 @@ int main(int argc, char* argv[]) host_init(); +#ifndef DISABLE_WIFI lwipServiceTimer.reset(); +#endif while(!done) { host_main_loop(); } diff --git a/Sming/Arch/Host/Components/sming-arch/component.mk b/Sming/Arch/Host/Components/sming-arch/component.mk index db03e70106..6c49459dad 100644 --- a/Sming/Arch/Host/Components/sming-arch/component.mk +++ b/Sming/Arch/Host/Components/sming-arch/component.mk @@ -28,22 +28,14 @@ COMPONENT_INCDIRS := \ COMPONENT_DEPENDS := \ driver \ esp_hal \ - esp_wifi \ gdbstub \ heap \ hostlib \ libc \ - lwip \ spi_flash \ vflash \ rboot - ifneq ($(ENABLE_HOSTED),) COMPONENT_DEPENDS += Hosted-Lib endif - -# => Platform WiFi -COMPONENT_VARS := \ - ENABLE_WPS \ - ENABLE_SMART_CONFIG diff --git a/Sming/Arch/Host/README.rst b/Sming/Arch/Host/README.rst index 96867e170f..9326a585eb 100644 --- a/Sming/Arch/Host/README.rst +++ b/Sming/Arch/Host/README.rst @@ -26,7 +26,7 @@ For Linux, you may require the ``gcc-multilib`` and ``g++-multilib`` packages to build 32-bit executables on a 64-bit OS. For Windows, make sure your ``MinGW`` distro is up to date. -See :doc:`/getting-started/windows` for further details. +See :doc:`/getting-started/windows/index` for further details. Building -------- diff --git a/Sming/Arch/Host/System/include/user_config.h b/Sming/Arch/Host/System/include/user_config.h index e41b804952..e2f3dbcbcd 100644 --- a/Sming/Arch/Host/System/include/user_config.h +++ b/Sming/Arch/Host/System/include/user_config.h @@ -10,11 +10,13 @@ #include // Network base API +#ifndef DISABLE_WIFI #include #include #include #include #include #include +#endif #endif /* __USER_CONFIG_H__ */ diff --git a/Sming/Arch/Esp32/Platform/AccessPointImpl.cpp b/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.cpp similarity index 100% rename from Sming/Arch/Esp32/Platform/AccessPointImpl.cpp rename to Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.cpp diff --git a/Sming/Arch/Esp32/Platform/AccessPointImpl.h b/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.h similarity index 100% rename from Sming/Arch/Esp32/Platform/AccessPointImpl.h rename to Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.h diff --git a/Sming/Arch/Esp32/Platform/LwipAdapter.cpp b/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp similarity index 100% rename from Sming/Arch/Esp32/Platform/LwipAdapter.cpp rename to Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp diff --git a/Sming/Arch/Esp32/Platform/LwipAdapter.cpp.changed b/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp.changed similarity index 100% rename from Sming/Arch/Esp32/Platform/LwipAdapter.cpp.changed rename to Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp.changed diff --git a/Sming/Arch/Esp32/Platform/LwipAdapter.cpp.copy b/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp.copy similarity index 100% rename from Sming/Arch/Esp32/Platform/LwipAdapter.cpp.copy rename to Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp.copy diff --git a/Sming/Arch/Esp32/Platform/LwipAdapter.h b/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.h similarity index 100% rename from Sming/Arch/Esp32/Platform/LwipAdapter.h rename to Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.h diff --git a/Sming/Arch/Esp32/Platform/StationImpl.cpp b/Sming/Components/Network/Arch/Esp32/Platform/StationImpl.cpp similarity index 100% rename from Sming/Arch/Esp32/Platform/StationImpl.cpp rename to Sming/Components/Network/Arch/Esp32/Platform/StationImpl.cpp diff --git a/Sming/Arch/Esp32/Platform/StationImpl.h b/Sming/Components/Network/Arch/Esp32/Platform/StationImpl.h similarity index 100% rename from Sming/Arch/Esp32/Platform/StationImpl.h rename to Sming/Components/Network/Arch/Esp32/Platform/StationImpl.h diff --git a/Sming/Arch/Esp32/Platform/WifiEventsImpl.cpp b/Sming/Components/Network/Arch/Esp32/Platform/WifiEventsImpl.cpp similarity index 100% rename from Sming/Arch/Esp32/Platform/WifiEventsImpl.cpp rename to Sming/Components/Network/Arch/Esp32/Platform/WifiEventsImpl.cpp diff --git a/Sming/Arch/Esp32/Platform/WifiEventsImpl.h b/Sming/Components/Network/Arch/Esp32/Platform/WifiEventsImpl.h similarity index 100% rename from Sming/Arch/Esp32/Platform/WifiEventsImpl.h rename to Sming/Components/Network/Arch/Esp32/Platform/WifiEventsImpl.h diff --git a/Sming/Arch/Esp8266/Platform/AccessPointImpl.cpp b/Sming/Components/Network/Arch/Esp8266/Platform/AccessPointImpl.cpp similarity index 100% rename from Sming/Arch/Esp8266/Platform/AccessPointImpl.cpp rename to Sming/Components/Network/Arch/Esp8266/Platform/AccessPointImpl.cpp diff --git a/Sming/Arch/Esp8266/Platform/AccessPointImpl.h b/Sming/Components/Network/Arch/Esp8266/Platform/AccessPointImpl.h similarity index 100% rename from Sming/Arch/Esp8266/Platform/AccessPointImpl.h rename to Sming/Components/Network/Arch/Esp8266/Platform/AccessPointImpl.h diff --git a/Sming/Arch/Esp8266/Platform/StationImpl.cpp b/Sming/Components/Network/Arch/Esp8266/Platform/StationImpl.cpp similarity index 100% rename from Sming/Arch/Esp8266/Platform/StationImpl.cpp rename to Sming/Components/Network/Arch/Esp8266/Platform/StationImpl.cpp diff --git a/Sming/Arch/Esp8266/Platform/StationImpl.h b/Sming/Components/Network/Arch/Esp8266/Platform/StationImpl.h similarity index 100% rename from Sming/Arch/Esp8266/Platform/StationImpl.h rename to Sming/Components/Network/Arch/Esp8266/Platform/StationImpl.h diff --git a/Sming/Arch/Esp8266/Platform/WifiEventsImpl.cpp b/Sming/Components/Network/Arch/Esp8266/Platform/WifiEventsImpl.cpp similarity index 100% rename from Sming/Arch/Esp8266/Platform/WifiEventsImpl.cpp rename to Sming/Components/Network/Arch/Esp8266/Platform/WifiEventsImpl.cpp diff --git a/Sming/Arch/Esp8266/Platform/WifiEventsImpl.h b/Sming/Components/Network/Arch/Esp8266/Platform/WifiEventsImpl.h similarity index 100% rename from Sming/Arch/Esp8266/Platform/WifiEventsImpl.h rename to Sming/Components/Network/Arch/Esp8266/Platform/WifiEventsImpl.h diff --git a/Sming/Arch/Esp8266/Platform/WifiSniffer.cpp b/Sming/Components/Network/Arch/Esp8266/Platform/WifiSniffer.cpp similarity index 100% rename from Sming/Arch/Esp8266/Platform/WifiSniffer.cpp rename to Sming/Components/Network/Arch/Esp8266/Platform/WifiSniffer.cpp diff --git a/Sming/Arch/Host/Platform/AccessPointImpl.cpp b/Sming/Components/Network/Arch/Host/Platform/AccessPointImpl.cpp similarity index 100% rename from Sming/Arch/Host/Platform/AccessPointImpl.cpp rename to Sming/Components/Network/Arch/Host/Platform/AccessPointImpl.cpp diff --git a/Sming/Arch/Host/Platform/AccessPointImpl.h b/Sming/Components/Network/Arch/Host/Platform/AccessPointImpl.h similarity index 100% rename from Sming/Arch/Host/Platform/AccessPointImpl.h rename to Sming/Components/Network/Arch/Host/Platform/AccessPointImpl.h diff --git a/Sming/Arch/Host/Platform/StationImpl.cpp b/Sming/Components/Network/Arch/Host/Platform/StationImpl.cpp similarity index 100% rename from Sming/Arch/Host/Platform/StationImpl.cpp rename to Sming/Components/Network/Arch/Host/Platform/StationImpl.cpp diff --git a/Sming/Arch/Host/Platform/StationImpl.h b/Sming/Components/Network/Arch/Host/Platform/StationImpl.h similarity index 100% rename from Sming/Arch/Host/Platform/StationImpl.h rename to Sming/Components/Network/Arch/Host/Platform/StationImpl.h diff --git a/Sming/Arch/Host/Platform/WifiEventsImpl.cpp b/Sming/Components/Network/Arch/Host/Platform/WifiEventsImpl.cpp similarity index 100% rename from Sming/Arch/Host/Platform/WifiEventsImpl.cpp rename to Sming/Components/Network/Arch/Host/Platform/WifiEventsImpl.cpp diff --git a/Sming/Arch/Host/Platform/WifiEventsImpl.h b/Sming/Components/Network/Arch/Host/Platform/WifiEventsImpl.h similarity index 100% rename from Sming/Arch/Host/Platform/WifiEventsImpl.h rename to Sming/Components/Network/Arch/Host/Platform/WifiEventsImpl.h diff --git a/Sming/Components/Network/README.rst b/Sming/Components/Network/README.rst new file mode 100644 index 0000000000..00a41b8857 --- /dev/null +++ b/Sming/Components/Network/README.rst @@ -0,0 +1,19 @@ +Networking Support +================== + +Contains core networking protocol support classes. + +.. toctree:: + :glob: + :maxdepth: 1 + + * + +Other networking libraries: + +- :library:`GoogleCast` +- :library:`MDNS` +- :library:`SSDP` +- :component:`ssl` +- :library:`UPnP` +- :library:`UPnP-Schema` diff --git a/Sming/Components/Network/component.mk b/Sming/Components/Network/component.mk new file mode 100644 index 0000000000..c2341b907c --- /dev/null +++ b/Sming/Components/Network/component.mk @@ -0,0 +1,60 @@ +COMPONENT_SRCDIRS := \ + src \ + $(call ListAllSubDirs,$(COMPONENT_PATH)/src) \ + $(call ListAllSubDirs,$(COMPONENT_PATH)/Arch/$(SMING_ARCH)) + +COMPONENT_INCDIRS := src + +COMPONENT_DOXYGEN_INPUT := src + +COMPONENT_DEPENDS := \ + ssl \ + http-parser \ + libb64 \ + ws_parser \ + mqtt-codec \ + libyuarel + +# => WPS +COMPONENT_VARS += ENABLE_WPS +ifeq ($(ENABLE_WPS), 1) + GLOBAL_CFLAGS += -DENABLE_WPS=1 +endif + +# => Smart Config +COMPONENT_VARS += ENABLE_SMART_CONFIG +ifeq ($(ENABLE_SMART_CONFIG),1) + GLOBAL_CFLAGS += -DENABLE_SMART_CONFIG=1 +endif + +# => HTTP server +COMPONENT_VARS += HTTP_SERVER_EXPOSE_NAME +HTTP_SERVER_EXPOSE_NAME ?= 1 +GLOBAL_CFLAGS += -DHTTP_SERVER_EXPOSE_NAME=$(HTTP_SERVER_EXPOSE_NAME) + +COMPONENT_VARS += HTTP_SERVER_EXPOSE_VERSION +HTTP_SERVER_EXPOSE_VERSION ?= 0 +GLOBAL_CFLAGS += -DHTTP_SERVER_EXPOSE_VERSION=$(HTTP_SERVER_EXPOSE_VERSION) + + + +# => LWIP +COMPONENT_VARS += ENABLE_CUSTOM_LWIP +ifeq ($(SMING_ARCH),Esp8266) + +ENABLE_CUSTOM_LWIP ?= 1 +ifeq ($(ENABLE_CUSTOM_LWIP), 0) + COMPONENT_DEPENDS += esp-lwip +else ifeq ($(ENABLE_CUSTOM_LWIP), 1) + COMPONENT_DEPENDS += esp-open-lwip +else ifeq ($(ENABLE_CUSTOM_LWIP), 2) + COMPONENT_DEPENDS += lwip2 +endif + +else ifeq ($(SMING_ARCH),Host) + +COMPONENT_DEPENDS += \ + esp_wifi \ + lwip + +endif diff --git a/docs/source/framework/core/network/dns.rst b/Sming/Components/Network/dns.rst similarity index 100% rename from docs/source/framework/core/network/dns.rst rename to Sming/Components/Network/dns.rst diff --git a/docs/source/framework/core/network/ftp.rst b/Sming/Components/Network/ftp.rst similarity index 100% rename from docs/source/framework/core/network/ftp.rst rename to Sming/Components/Network/ftp.rst diff --git a/docs/source/framework/core/network/http.rst b/Sming/Components/Network/http.rst similarity index 100% rename from docs/source/framework/core/network/http.rst rename to Sming/Components/Network/http.rst diff --git a/docs/source/framework/core/network/mqtt.rst b/Sming/Components/Network/mqtt.rst similarity index 100% rename from docs/source/framework/core/network/mqtt.rst rename to Sming/Components/Network/mqtt.rst diff --git a/docs/source/framework/core/network/ntp.rst b/Sming/Components/Network/ntp.rst similarity index 100% rename from docs/source/framework/core/network/ntp.rst rename to Sming/Components/Network/ntp.rst diff --git a/docs/source/framework/core/network/smtp.rst b/Sming/Components/Network/smtp.rst similarity index 100% rename from docs/source/framework/core/network/smtp.rst rename to Sming/Components/Network/smtp.rst diff --git a/Sming/Core/Data/Stream/MultipartStream.cpp b/Sming/Components/Network/src/Data/Stream/MultipartStream.cpp similarity index 95% rename from Sming/Core/Data/Stream/MultipartStream.cpp rename to Sming/Components/Network/src/Data/Stream/MultipartStream.cpp index 5546ea55de..a8393f81f2 100644 --- a/Sming/Core/Data/Stream/MultipartStream.cpp +++ b/Sming/Components/Network/src/Data/Stream/MultipartStream.cpp @@ -10,8 +10,8 @@ * ****/ -#include "MultipartStream.h" -#include "MemoryDataStream.h" +#include +#include IDataSourceStream* MultipartStream::getNextStream() { diff --git a/Sming/Core/Data/Stream/UrlencodedOutputStream.cpp b/Sming/Components/Network/src/Data/Stream/UrlencodedOutputStream.cpp similarity index 100% rename from Sming/Core/Data/Stream/UrlencodedOutputStream.cpp rename to Sming/Components/Network/src/Data/Stream/UrlencodedOutputStream.cpp diff --git a/Sming/Core/Data/Stream/UrlencodedOutputStream.h b/Sming/Components/Network/src/Data/Stream/UrlencodedOutputStream.h similarity index 96% rename from Sming/Core/Data/Stream/UrlencodedOutputStream.h rename to Sming/Components/Network/src/Data/Stream/UrlencodedOutputStream.h index 33c7517142..bd32391a7b 100644 --- a/Sming/Core/Data/Stream/UrlencodedOutputStream.h +++ b/Sming/Components/Network/src/Data/Stream/UrlencodedOutputStream.h @@ -12,7 +12,7 @@ #pragma once -#include "MemoryDataStream.h" +#include #include "Network/Http/HttpParams.h" /** diff --git a/Sming/Wiring/IpAddress.cpp b/Sming/Components/Network/src/IpAddress.cpp similarity index 100% rename from Sming/Wiring/IpAddress.cpp rename to Sming/Components/Network/src/IpAddress.cpp diff --git a/Sming/Wiring/IpAddress.h b/Sming/Components/Network/src/IpAddress.h similarity index 100% rename from Sming/Wiring/IpAddress.h rename to Sming/Components/Network/src/IpAddress.h diff --git a/Sming/Core/Network/DnsServer.cpp b/Sming/Components/Network/src/Network/DnsServer.cpp similarity index 100% rename from Sming/Core/Network/DnsServer.cpp rename to Sming/Components/Network/src/Network/DnsServer.cpp diff --git a/Sming/Core/Network/DnsServer.h b/Sming/Components/Network/src/Network/DnsServer.h similarity index 98% rename from Sming/Core/Network/DnsServer.h rename to Sming/Components/Network/src/Network/DnsServer.h index cd7543efa4..3f5eefc1cf 100644 --- a/Sming/Core/Network/DnsServer.h +++ b/Sming/Components/Network/src/Network/DnsServer.h @@ -23,8 +23,8 @@ #pragma once #include "UdpConnection.h" -#include "WString.h" -#include "IpAddress.h" +#include +#include #define DNS_QR_QUERY 0 #define DNS_QR_RESPONSE 1 diff --git a/Sming/Core/Network/Ftp/FtpDataFileList.h b/Sming/Components/Network/src/Network/Ftp/FtpDataFileList.h similarity index 100% rename from Sming/Core/Network/Ftp/FtpDataFileList.h rename to Sming/Components/Network/src/Network/Ftp/FtpDataFileList.h diff --git a/Sming/Core/Network/Ftp/FtpDataRetrieve.h b/Sming/Components/Network/src/Network/Ftp/FtpDataRetrieve.h similarity index 100% rename from Sming/Core/Network/Ftp/FtpDataRetrieve.h rename to Sming/Components/Network/src/Network/Ftp/FtpDataRetrieve.h diff --git a/Sming/Core/Network/Ftp/FtpDataStore.h b/Sming/Components/Network/src/Network/Ftp/FtpDataStore.h similarity index 100% rename from Sming/Core/Network/Ftp/FtpDataStore.h rename to Sming/Components/Network/src/Network/Ftp/FtpDataStore.h diff --git a/Sming/Core/Network/Ftp/FtpDataStream.h b/Sming/Components/Network/src/Network/Ftp/FtpDataStream.h similarity index 100% rename from Sming/Core/Network/Ftp/FtpDataStream.h rename to Sming/Components/Network/src/Network/Ftp/FtpDataStream.h diff --git a/Sming/Core/Network/Ftp/FtpServerConnection.cpp b/Sming/Components/Network/src/Network/Ftp/FtpServerConnection.cpp similarity index 100% rename from Sming/Core/Network/Ftp/FtpServerConnection.cpp rename to Sming/Components/Network/src/Network/Ftp/FtpServerConnection.cpp diff --git a/Sming/Core/Network/Ftp/FtpServerConnection.h b/Sming/Components/Network/src/Network/Ftp/FtpServerConnection.h similarity index 98% rename from Sming/Core/Network/Ftp/FtpServerConnection.h rename to Sming/Components/Network/src/Network/Ftp/FtpServerConnection.h index b0f9a7c69e..85292cfd46 100644 --- a/Sming/Core/Network/Ftp/FtpServerConnection.h +++ b/Sming/Components/Network/src/Network/Ftp/FtpServerConnection.h @@ -11,7 +11,7 @@ #pragma once #include "Network/TcpConnection.h" -#include "IpAddress.h" +#include #include "WString.h" #include diff --git a/Sming/Core/Network/FtpServer.cpp b/Sming/Components/Network/src/Network/FtpServer.cpp similarity index 100% rename from Sming/Core/Network/FtpServer.cpp rename to Sming/Components/Network/src/Network/FtpServer.cpp diff --git a/Sming/Core/Network/FtpServer.h b/Sming/Components/Network/src/Network/FtpServer.h similarity index 100% rename from Sming/Core/Network/FtpServer.h rename to Sming/Components/Network/src/Network/FtpServer.h diff --git a/Sming/Core/Network/Http/BasicHttpHeaders.cpp b/Sming/Components/Network/src/Network/Http/BasicHttpHeaders.cpp similarity index 100% rename from Sming/Core/Network/Http/BasicHttpHeaders.cpp rename to Sming/Components/Network/src/Network/Http/BasicHttpHeaders.cpp diff --git a/Sming/Core/Network/Http/BasicHttpHeaders.h b/Sming/Components/Network/src/Network/Http/BasicHttpHeaders.h similarity index 100% rename from Sming/Core/Network/Http/BasicHttpHeaders.h rename to Sming/Components/Network/src/Network/Http/BasicHttpHeaders.h diff --git a/Sming/Core/Network/Http/HttpBodyParser.cpp b/Sming/Components/Network/src/Network/Http/HttpBodyParser.cpp similarity index 98% rename from Sming/Core/Network/Http/HttpBodyParser.cpp rename to Sming/Components/Network/src/Network/Http/HttpBodyParser.cpp index 07cb7166f0..e5cd17aa1d 100644 --- a/Sming/Core/Network/Http/HttpBodyParser.cpp +++ b/Sming/Components/Network/src/Network/Http/HttpBodyParser.cpp @@ -15,7 +15,7 @@ ****/ #include "HttpBodyParser.h" -#include "Network/WebHelpers/escape.h" +#include /* * Content is received in chunks which we need to reassemble into name=value pairs. diff --git a/Sming/Core/Network/Http/HttpBodyParser.h b/Sming/Components/Network/src/Network/Http/HttpBodyParser.h similarity index 100% rename from Sming/Core/Network/Http/HttpBodyParser.h rename to Sming/Components/Network/src/Network/Http/HttpBodyParser.h diff --git a/Sming/Core/Network/Http/HttpClientConnection.cpp b/Sming/Components/Network/src/Network/Http/HttpClientConnection.cpp similarity index 100% rename from Sming/Core/Network/Http/HttpClientConnection.cpp rename to Sming/Components/Network/src/Network/Http/HttpClientConnection.cpp diff --git a/Sming/Core/Network/Http/HttpClientConnection.h b/Sming/Components/Network/src/Network/Http/HttpClientConnection.h similarity index 100% rename from Sming/Core/Network/Http/HttpClientConnection.h rename to Sming/Components/Network/src/Network/Http/HttpClientConnection.h diff --git a/Sming/Core/Network/Http/HttpCommon.cpp b/Sming/Components/Network/src/Network/Http/HttpCommon.cpp similarity index 100% rename from Sming/Core/Network/Http/HttpCommon.cpp rename to Sming/Components/Network/src/Network/Http/HttpCommon.cpp diff --git a/Sming/Core/Network/Http/HttpCommon.h b/Sming/Components/Network/src/Network/Http/HttpCommon.h similarity index 98% rename from Sming/Core/Network/Http/HttpCommon.h rename to Sming/Components/Network/src/Network/Http/HttpCommon.h index 58f1337503..8acb25fb19 100644 --- a/Sming/Core/Network/Http/HttpCommon.h +++ b/Sming/Components/Network/src/Network/Http/HttpCommon.h @@ -15,7 +15,7 @@ #define ENABLE_HTTP_REQUEST_AUTH 1 #include "WString.h" -#include "../WebConstants.h" +#include #include "../Url.h" #include "Data/Stream/ReadWriteStream.h" #include "Data/ObjectMap.h" diff --git a/Sming/Core/Network/Http/HttpConnection.cpp b/Sming/Components/Network/src/Network/Http/HttpConnection.cpp similarity index 100% rename from Sming/Core/Network/Http/HttpConnection.cpp rename to Sming/Components/Network/src/Network/Http/HttpConnection.cpp diff --git a/Sming/Core/Network/Http/HttpConnection.h b/Sming/Components/Network/src/Network/Http/HttpConnection.h similarity index 100% rename from Sming/Core/Network/Http/HttpConnection.h rename to Sming/Components/Network/src/Network/Http/HttpConnection.h diff --git a/Sming/Core/Network/Http/HttpHeaderBuilder.h b/Sming/Components/Network/src/Network/Http/HttpHeaderBuilder.h similarity index 100% rename from Sming/Core/Network/Http/HttpHeaderBuilder.h rename to Sming/Components/Network/src/Network/Http/HttpHeaderBuilder.h diff --git a/Sming/Core/Network/Http/HttpHeaderFields.cpp b/Sming/Components/Network/src/Network/Http/HttpHeaderFields.cpp similarity index 100% rename from Sming/Core/Network/Http/HttpHeaderFields.cpp rename to Sming/Components/Network/src/Network/Http/HttpHeaderFields.cpp diff --git a/Sming/Core/Network/Http/HttpHeaderFields.h b/Sming/Components/Network/src/Network/Http/HttpHeaderFields.h similarity index 100% rename from Sming/Core/Network/Http/HttpHeaderFields.h rename to Sming/Components/Network/src/Network/Http/HttpHeaderFields.h diff --git a/Sming/Core/Network/Http/HttpHeaders.cpp b/Sming/Components/Network/src/Network/Http/HttpHeaders.cpp similarity index 100% rename from Sming/Core/Network/Http/HttpHeaders.cpp rename to Sming/Components/Network/src/Network/Http/HttpHeaders.cpp diff --git a/Sming/Core/Network/Http/HttpHeaders.h b/Sming/Components/Network/src/Network/Http/HttpHeaders.h similarity index 100% rename from Sming/Core/Network/Http/HttpHeaders.h rename to Sming/Components/Network/src/Network/Http/HttpHeaders.h diff --git a/Sming/Core/Network/Http/HttpParams.cpp b/Sming/Components/Network/src/Network/Http/HttpParams.cpp similarity index 98% rename from Sming/Core/Network/Http/HttpParams.cpp rename to Sming/Components/Network/src/Network/Http/HttpParams.cpp index 25c1c05511..a84fabe589 100644 --- a/Sming/Core/Network/Http/HttpParams.cpp +++ b/Sming/Components/Network/src/Network/Http/HttpParams.cpp @@ -11,7 +11,7 @@ ****/ #include "HttpParams.h" -#include "Network/WebHelpers/escape.h" +#include #include "Print.h" #include "libyuarel/yuarel.h" diff --git a/Sming/Core/Network/Http/HttpParams.h b/Sming/Components/Network/src/Network/Http/HttpParams.h similarity index 100% rename from Sming/Core/Network/Http/HttpParams.h rename to Sming/Components/Network/src/Network/Http/HttpParams.h diff --git a/Sming/Core/Network/Http/HttpRequest.cpp b/Sming/Components/Network/src/Network/Http/HttpRequest.cpp similarity index 100% rename from Sming/Core/Network/Http/HttpRequest.cpp rename to Sming/Components/Network/src/Network/Http/HttpRequest.cpp diff --git a/Sming/Core/Network/Http/HttpRequest.h b/Sming/Components/Network/src/Network/Http/HttpRequest.h similarity index 100% rename from Sming/Core/Network/Http/HttpRequest.h rename to Sming/Components/Network/src/Network/Http/HttpRequest.h diff --git a/Sming/Core/Network/Http/HttpRequestAuth.cpp b/Sming/Components/Network/src/Network/Http/HttpRequestAuth.cpp similarity index 97% rename from Sming/Core/Network/Http/HttpRequestAuth.cpp rename to Sming/Components/Network/src/Network/Http/HttpRequestAuth.cpp index 2ee1d1e982..0270a3c03b 100644 --- a/Sming/Core/Network/Http/HttpRequestAuth.cpp +++ b/Sming/Components/Network/src/Network/Http/HttpRequestAuth.cpp @@ -12,7 +12,7 @@ #include "HttpRequestAuth.h" #include "HttpRequest.h" -#include "Network/WebHelpers/base64.h" +#include // Basic Auth void HttpBasicAuth::setRequest(HttpRequest* request) diff --git a/Sming/Core/Network/Http/HttpRequestAuth.h b/Sming/Components/Network/src/Network/Http/HttpRequestAuth.h similarity index 100% rename from Sming/Core/Network/Http/HttpRequestAuth.h rename to Sming/Components/Network/src/Network/Http/HttpRequestAuth.h diff --git a/Sming/Core/Network/Http/HttpResource.h b/Sming/Components/Network/src/Network/Http/HttpResource.h similarity index 100% rename from Sming/Core/Network/Http/HttpResource.h rename to Sming/Components/Network/src/Network/Http/HttpResource.h diff --git a/Sming/Core/Network/Http/HttpResourceTree.cpp b/Sming/Components/Network/src/Network/Http/HttpResourceTree.cpp similarity index 100% rename from Sming/Core/Network/Http/HttpResourceTree.cpp rename to Sming/Components/Network/src/Network/Http/HttpResourceTree.cpp diff --git a/Sming/Core/Network/Http/HttpResourceTree.h b/Sming/Components/Network/src/Network/Http/HttpResourceTree.h similarity index 100% rename from Sming/Core/Network/Http/HttpResourceTree.h rename to Sming/Components/Network/src/Network/Http/HttpResourceTree.h diff --git a/Sming/Core/Network/Http/HttpResponse.cpp b/Sming/Components/Network/src/Network/Http/HttpResponse.cpp similarity index 99% rename from Sming/Core/Network/Http/HttpResponse.cpp rename to Sming/Components/Network/src/Network/Http/HttpResponse.cpp index ac7fbe8eb4..95b5a3ab9d 100644 --- a/Sming/Core/Network/Http/HttpResponse.cpp +++ b/Sming/Components/Network/src/Network/Http/HttpResponse.cpp @@ -11,7 +11,7 @@ ****/ #include "HttpResponse.h" -#include "../WebConstants.h" +#include #include "Data/Stream/MemoryDataStream.h" #include "Data/Stream/FileStream.h" diff --git a/Sming/Core/Network/Http/HttpResponse.h b/Sming/Components/Network/src/Network/Http/HttpResponse.h similarity index 100% rename from Sming/Core/Network/Http/HttpResponse.h rename to Sming/Components/Network/src/Network/Http/HttpResponse.h diff --git a/Sming/Core/Network/Http/HttpServerConnection.cpp b/Sming/Components/Network/src/Network/Http/HttpServerConnection.cpp similarity index 99% rename from Sming/Core/Network/Http/HttpServerConnection.cpp rename to Sming/Components/Network/src/Network/Http/HttpServerConnection.cpp index ca7eeb173c..c3161bfca6 100644 --- a/Sming/Core/Network/Http/HttpServerConnection.cpp +++ b/Sming/Components/Network/src/Network/Http/HttpServerConnection.cpp @@ -14,7 +14,7 @@ #include "HttpResourceTree.h" #include "Network/HttpServer.h" #include "Network/TcpServer.h" -#include "Network/WebConstants.h" +#include #include "Data/Stream/ChunkedStream.h" #include diff --git a/Sming/Core/Network/Http/HttpServerConnection.h b/Sming/Components/Network/src/Network/Http/HttpServerConnection.h similarity index 100% rename from Sming/Core/Network/Http/HttpServerConnection.h rename to Sming/Components/Network/src/Network/Http/HttpServerConnection.h diff --git a/Sming/Core/Network/Http/Websocket/WebsocketConnection.cpp b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.cpp similarity index 97% rename from Sming/Core/Network/Http/Websocket/WebsocketConnection.cpp rename to Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.cpp index d6a5f96dc4..0651d39a5d 100644 --- a/Sming/Core/Network/Http/Websocket/WebsocketConnection.cpp +++ b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.cpp @@ -10,8 +10,8 @@ #include "WebsocketConnection.h" #include -#include -#include +#include +#include DEFINE_FSTR(WSSTR_CONNECTION, "connection") DEFINE_FSTR(WSSTR_UPGRADE, "upgrade") @@ -63,12 +63,11 @@ bool WebsocketConnection::bind(HttpRequest& request, HttpResponse& response) String token = request.headers[HTTP_HEADER_SEC_WEBSOCKET_KEY]; token.trim(); token += WSSTR_SECRET; - unsigned char hash[SHA1_SIZE]; - sha1(hash, token.c_str(), token.length()); + auto hash = Crypto::Sha1().calculate(token); response.code = HTTP_STATUS_SWITCHING_PROTOCOLS; response.headers[HTTP_HEADER_CONNECTION] = WSSTR_UPGRADE; response.headers[HTTP_HEADER_UPGRADE] = WSSTR_WEBSOCKET; - response.headers[HTTP_HEADER_SEC_WEBSOCKET_ACCEPT] = base64_encode(hash, SHA1_SIZE); + response.headers[HTTP_HEADER_SEC_WEBSOCKET_ACCEPT] = base64_encode(hash.data(), hash.size()); isClientConnection = false; diff --git a/Sming/Core/Network/Http/Websocket/WebsocketConnection.h b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.h similarity index 100% rename from Sming/Core/Network/Http/Websocket/WebsocketConnection.h rename to Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.h diff --git a/Sming/Core/Network/Http/Websocket/WebsocketResource.cpp b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketResource.cpp similarity index 100% rename from Sming/Core/Network/Http/Websocket/WebsocketResource.cpp rename to Sming/Components/Network/src/Network/Http/Websocket/WebsocketResource.cpp diff --git a/Sming/Core/Network/Http/Websocket/WebsocketResource.h b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketResource.h similarity index 100% rename from Sming/Core/Network/Http/Websocket/WebsocketResource.h rename to Sming/Components/Network/src/Network/Http/Websocket/WebsocketResource.h diff --git a/Sming/Core/Network/Http/Websocket/WsCommandHandlerResource.h b/Sming/Components/Network/src/Network/Http/Websocket/WsCommandHandlerResource.h similarity index 100% rename from Sming/Core/Network/Http/Websocket/WsCommandHandlerResource.h rename to Sming/Components/Network/src/Network/Http/Websocket/WsCommandHandlerResource.h diff --git a/Sming/Core/Network/HttpClient.cpp b/Sming/Components/Network/src/Network/HttpClient.cpp similarity index 100% rename from Sming/Core/Network/HttpClient.cpp rename to Sming/Components/Network/src/Network/HttpClient.cpp diff --git a/Sming/Core/Network/HttpClient.h b/Sming/Components/Network/src/Network/HttpClient.h similarity index 100% rename from Sming/Core/Network/HttpClient.h rename to Sming/Components/Network/src/Network/HttpClient.h diff --git a/Sming/Core/Network/HttpServer.cpp b/Sming/Components/Network/src/Network/HttpServer.cpp similarity index 100% rename from Sming/Core/Network/HttpServer.cpp rename to Sming/Components/Network/src/Network/HttpServer.cpp diff --git a/Sming/Core/Network/HttpServer.h b/Sming/Components/Network/src/Network/HttpServer.h similarity index 100% rename from Sming/Core/Network/HttpServer.h rename to Sming/Components/Network/src/Network/HttpServer.h diff --git a/Sming/Core/Network/IpConnection.cpp b/Sming/Components/Network/src/Network/IpConnection.cpp similarity index 100% rename from Sming/Core/Network/IpConnection.cpp rename to Sming/Components/Network/src/Network/IpConnection.cpp diff --git a/Sming/Core/Network/IpConnection.h b/Sming/Components/Network/src/Network/IpConnection.h similarity index 98% rename from Sming/Core/Network/IpConnection.h rename to Sming/Components/Network/src/Network/IpConnection.h index e49cfc1c0e..335d1e597e 100644 --- a/Sming/Core/Network/IpConnection.h +++ b/Sming/Components/Network/src/Network/IpConnection.h @@ -10,7 +10,7 @@ #pragma once -#include +#include "IpAddress.h" /** @defgroup ip IP * @brief Provides common IP functions diff --git a/Sming/Core/Data/MailMessage.cpp b/Sming/Components/Network/src/Network/MailMessage.cpp similarity index 98% rename from Sming/Core/Data/MailMessage.cpp rename to Sming/Components/Network/src/Network/MailMessage.cpp index 3c30a0a4a9..19e33f92cb 100644 --- a/Sming/Core/Data/MailMessage.cpp +++ b/Sming/Components/Network/src/Network/MailMessage.cpp @@ -11,7 +11,7 @@ ****/ #include "MailMessage.h" -#include "Stream/MemoryDataStream.h" +#include HttpHeaders& MailMessage::getHeaders() { diff --git a/Sming/Core/Data/MailMessage.h b/Sming/Components/Network/src/Network/MailMessage.h similarity index 92% rename from Sming/Core/Data/MailMessage.h rename to Sming/Components/Network/src/Network/MailMessage.h index 0c3cdfe4d0..f5357ebc9e 100644 --- a/Sming/Core/Data/MailMessage.h +++ b/Sming/Components/Network/src/Network/MailMessage.h @@ -20,11 +20,11 @@ #include "WString.h" #include "WVector.h" -#include "Network/WebConstants.h" -#include "Network/Http/HttpHeaders.h" -#include "Stream/MultipartStream.h" -#include "Stream/DataSourceStream.h" -#include "Stream/FileStream.h" +#include "Http/HttpHeaders.h" +#include +#include +#include +#include class SmtpClient; diff --git a/Sming/Core/Network/Mqtt/MqttPayloadParser.cpp b/Sming/Components/Network/src/Network/Mqtt/MqttPayloadParser.cpp similarity index 100% rename from Sming/Core/Network/Mqtt/MqttPayloadParser.cpp rename to Sming/Components/Network/src/Network/Mqtt/MqttPayloadParser.cpp diff --git a/Sming/Core/Network/Mqtt/MqttPayloadParser.h b/Sming/Components/Network/src/Network/Mqtt/MqttPayloadParser.h similarity index 100% rename from Sming/Core/Network/Mqtt/MqttPayloadParser.h rename to Sming/Components/Network/src/Network/Mqtt/MqttPayloadParser.h diff --git a/Sming/Core/Network/MqttClient.cpp b/Sming/Components/Network/src/Network/MqttClient.cpp similarity index 100% rename from Sming/Core/Network/MqttClient.cpp rename to Sming/Components/Network/src/Network/MqttClient.cpp diff --git a/Sming/Core/Network/MqttClient.h b/Sming/Components/Network/src/Network/MqttClient.h similarity index 100% rename from Sming/Core/Network/MqttClient.h rename to Sming/Components/Network/src/Network/MqttClient.h diff --git a/Sming/Core/Network/NetUtils.cpp b/Sming/Components/Network/src/Network/NetUtils.cpp similarity index 100% rename from Sming/Core/Network/NetUtils.cpp rename to Sming/Components/Network/src/Network/NetUtils.cpp diff --git a/Sming/Core/Network/NetUtils.h b/Sming/Components/Network/src/Network/NetUtils.h similarity index 100% rename from Sming/Core/Network/NetUtils.h rename to Sming/Components/Network/src/Network/NetUtils.h diff --git a/Sming/Core/Network/NtpClient.cpp b/Sming/Components/Network/src/Network/NtpClient.cpp similarity index 100% rename from Sming/Core/Network/NtpClient.cpp rename to Sming/Components/Network/src/Network/NtpClient.cpp diff --git a/Sming/Core/Network/NtpClient.h b/Sming/Components/Network/src/Network/NtpClient.h similarity index 100% rename from Sming/Core/Network/NtpClient.h rename to Sming/Components/Network/src/Network/NtpClient.h diff --git a/Sming/Core/Network/SmtpClient.cpp b/Sming/Components/Network/src/Network/SmtpClient.cpp similarity index 99% rename from Sming/Core/Network/SmtpClient.cpp rename to Sming/Components/Network/src/Network/SmtpClient.cpp index c78a04cee0..5f60c5fc27 100644 --- a/Sming/Core/Network/SmtpClient.cpp +++ b/Sming/Components/Network/src/Network/SmtpClient.cpp @@ -17,7 +17,7 @@ */ #include "SmtpClient.h" -#include "WebHelpers/base64.h" +#include #include #include #include diff --git a/Sming/Core/Network/SmtpClient.h b/Sming/Components/Network/src/Network/SmtpClient.h similarity index 99% rename from Sming/Core/Network/SmtpClient.h rename to Sming/Components/Network/src/Network/SmtpClient.h index 2b4b4149ad..45be230ecc 100644 --- a/Sming/Core/Network/SmtpClient.h +++ b/Sming/Components/Network/src/Network/SmtpClient.h @@ -27,7 +27,7 @@ */ #include "TcpClient.h" -#include +#include "MailMessage.h" #include "Url.h" #include #include diff --git a/Sming/Core/Network/TcpClient.cpp b/Sming/Components/Network/src/Network/TcpClient.cpp similarity index 100% rename from Sming/Core/Network/TcpClient.cpp rename to Sming/Components/Network/src/Network/TcpClient.cpp diff --git a/Sming/Core/Network/TcpClient.h b/Sming/Components/Network/src/Network/TcpClient.h similarity index 100% rename from Sming/Core/Network/TcpClient.h rename to Sming/Components/Network/src/Network/TcpClient.h diff --git a/Sming/Core/Network/TcpConnection.cpp b/Sming/Components/Network/src/Network/TcpConnection.cpp similarity index 100% rename from Sming/Core/Network/TcpConnection.cpp rename to Sming/Components/Network/src/Network/TcpConnection.cpp diff --git a/Sming/Core/Network/TcpConnection.h b/Sming/Components/Network/src/Network/TcpConnection.h similarity index 100% rename from Sming/Core/Network/TcpConnection.h rename to Sming/Components/Network/src/Network/TcpConnection.h diff --git a/Sming/Core/Network/TcpServer.cpp b/Sming/Components/Network/src/Network/TcpServer.cpp similarity index 100% rename from Sming/Core/Network/TcpServer.cpp rename to Sming/Components/Network/src/Network/TcpServer.cpp diff --git a/Sming/Core/Network/TcpServer.h b/Sming/Components/Network/src/Network/TcpServer.h similarity index 100% rename from Sming/Core/Network/TcpServer.h rename to Sming/Components/Network/src/Network/TcpServer.h diff --git a/Sming/Core/Network/TelnetServer.cpp b/Sming/Components/Network/src/Network/TelnetServer.cpp similarity index 100% rename from Sming/Core/Network/TelnetServer.cpp rename to Sming/Components/Network/src/Network/TelnetServer.cpp diff --git a/Sming/Core/Network/TelnetServer.h b/Sming/Components/Network/src/Network/TelnetServer.h similarity index 100% rename from Sming/Core/Network/TelnetServer.h rename to Sming/Components/Network/src/Network/TelnetServer.h diff --git a/Sming/Core/Network/UdpConnection.cpp b/Sming/Components/Network/src/Network/UdpConnection.cpp similarity index 100% rename from Sming/Core/Network/UdpConnection.cpp rename to Sming/Components/Network/src/Network/UdpConnection.cpp diff --git a/Sming/Core/Network/UdpConnection.h b/Sming/Components/Network/src/Network/UdpConnection.h similarity index 100% rename from Sming/Core/Network/UdpConnection.h rename to Sming/Components/Network/src/Network/UdpConnection.h diff --git a/Sming/Core/Network/Url.cpp b/Sming/Components/Network/src/Network/Url.cpp similarity index 98% rename from Sming/Core/Network/Url.cpp rename to Sming/Components/Network/src/Network/Url.cpp index 915f4647b7..1eaa1c3515 100644 --- a/Sming/Core/Network/Url.cpp +++ b/Sming/Components/Network/src/Network/Url.cpp @@ -12,7 +12,7 @@ #include "Url.h" #include "libyuarel/yuarel.h" -#include "WebHelpers/escape.h" +#include #include "Print.h" /** diff --git a/Sming/Core/Network/Url.h b/Sming/Components/Network/src/Network/Url.h similarity index 100% rename from Sming/Core/Network/Url.h rename to Sming/Components/Network/src/Network/Url.h diff --git a/Sming/Components/Network/src/Network/WebHelpers/base64.h b/Sming/Components/Network/src/Network/WebHelpers/base64.h new file mode 100644 index 0000000000..012cddf636 --- /dev/null +++ b/Sming/Components/Network/src/Network/WebHelpers/base64.h @@ -0,0 +1,2 @@ +#include +#pragma GCC warning "Please update to #include - see https://sming.readthedocs.io/en/4.3.0/upgrading/4.3-4.4.html" diff --git a/Sming/Components/Network/src/Network/WebHelpers/escape.h b/Sming/Components/Network/src/Network/WebHelpers/escape.h new file mode 100644 index 0000000000..3661e0999b --- /dev/null +++ b/Sming/Components/Network/src/Network/WebHelpers/escape.h @@ -0,0 +1,2 @@ +#include +#pragma GCC warning "Please update to #include - see https://sming.readthedocs.io/en/4.3.0/upgrading/4.3-4.4.html" diff --git a/Sming/Core/Network/WebsocketClient.cpp b/Sming/Components/Network/src/Network/WebsocketClient.cpp similarity index 93% rename from Sming/Core/Network/WebsocketClient.cpp rename to Sming/Components/Network/src/Network/WebsocketClient.cpp index 36dd1546ff..8e5fe09537 100644 --- a/Sming/Core/Network/WebsocketClient.cpp +++ b/Sming/Components/Network/src/Network/WebsocketClient.cpp @@ -15,8 +15,7 @@ #include "WebsocketClient.h" #include "Http/HttpHeaders.h" -#include "WebHelpers/aw-sha1.h" -#include "WebHelpers/base64.h" +#include HttpConnection* WebsocketClient::getHttpConnection() { @@ -85,9 +84,8 @@ int WebsocketClient::verifyKey(HttpConnection& connection, HttpResponse& respons String serverHashedKey = response.headers[HTTP_HEADER_SEC_WEBSOCKET_ACCEPT]; String keyToHash = key + WSSTR_SECRET; - unsigned char hash[SHA1_SIZE]; - sha1(hash, keyToHash.c_str(), keyToHash.length()); - String base64hash = base64_encode(hash, sizeof(hash)); + auto hash = Crypto::Sha1().calculate(keyToHash); + String base64hash = base64_encode(hash.data(), hash.size()); if(base64hash != serverHashedKey) { debug_e("wscli key mismatch: %s | %s", serverHashedKey.c_str(), base64hash.c_str()); state = eWSCS_Closed; diff --git a/Sming/Core/Network/WebsocketClient.h b/Sming/Components/Network/src/Network/WebsocketClient.h similarity index 100% rename from Sming/Core/Network/WebsocketClient.h rename to Sming/Components/Network/src/Network/WebsocketClient.h diff --git a/Sming/Platform/AccessPoint.cpp b/Sming/Components/Network/src/Platform/AccessPoint.cpp similarity index 100% rename from Sming/Platform/AccessPoint.cpp rename to Sming/Components/Network/src/Platform/AccessPoint.cpp diff --git a/Sming/Platform/AccessPoint.h b/Sming/Components/Network/src/Platform/AccessPoint.h similarity index 100% rename from Sming/Platform/AccessPoint.h rename to Sming/Components/Network/src/Platform/AccessPoint.h diff --git a/Sming/Platform/BssInfo.cpp b/Sming/Components/Network/src/Platform/BssInfo.cpp similarity index 100% rename from Sming/Platform/BssInfo.cpp rename to Sming/Components/Network/src/Platform/BssInfo.cpp diff --git a/Sming/Platform/BssInfo.h b/Sming/Components/Network/src/Platform/BssInfo.h similarity index 100% rename from Sming/Platform/BssInfo.h rename to Sming/Components/Network/src/Platform/BssInfo.h diff --git a/Sming/Platform/Station.cpp b/Sming/Components/Network/src/Platform/Station.cpp similarity index 100% rename from Sming/Platform/Station.cpp rename to Sming/Components/Network/src/Platform/Station.cpp diff --git a/Sming/Platform/Station.h b/Sming/Components/Network/src/Platform/Station.h similarity index 100% rename from Sming/Platform/Station.h rename to Sming/Components/Network/src/Platform/Station.h diff --git a/Sming/Platform/WifiEvents.cpp b/Sming/Components/Network/src/Platform/WifiEvents.cpp similarity index 100% rename from Sming/Platform/WifiEvents.cpp rename to Sming/Components/Network/src/Platform/WifiEvents.cpp diff --git a/Sming/Platform/WifiEvents.h b/Sming/Components/Network/src/Platform/WifiEvents.h similarity index 100% rename from Sming/Platform/WifiEvents.h rename to Sming/Components/Network/src/Platform/WifiEvents.h diff --git a/Sming/Platform/WifiSniffer.h b/Sming/Components/Network/src/Platform/WifiSniffer.h similarity index 99% rename from Sming/Platform/WifiSniffer.h rename to Sming/Components/Network/src/Platform/WifiSniffer.h index 0eed4d3764..5d89ea8e6a 100644 --- a/Sming/Platform/WifiSniffer.h +++ b/Sming/Components/Network/src/Platform/WifiSniffer.h @@ -16,7 +16,7 @@ #pragma once -#include "System.h" +#include #include "WVector.h" /** @defgroup wifi_sniffer WiFi Sniffer diff --git a/docs/source/framework/core/network/tcp.rst b/Sming/Components/Network/tcp.rst similarity index 100% rename from docs/source/framework/core/network/tcp.rst rename to Sming/Components/Network/tcp.rst diff --git a/docs/source/framework/core/network/telnet.rst b/Sming/Components/Network/telnet.rst similarity index 100% rename from docs/source/framework/core/network/telnet.rst rename to Sming/Components/Network/telnet.rst diff --git a/docs/source/framework/core/network/udp.rst b/Sming/Components/Network/udp.rst similarity index 100% rename from docs/source/framework/core/network/udp.rst rename to Sming/Components/Network/udp.rst diff --git a/docs/source/framework/core/network/url.rst b/Sming/Components/Network/url.rst similarity index 100% rename from docs/source/framework/core/network/url.rst rename to Sming/Components/Network/url.rst diff --git a/docs/source/framework/core/network/websocket.rst b/Sming/Components/Network/websocket.rst similarity index 100% rename from docs/source/framework/core/network/websocket.rst rename to Sming/Components/Network/websocket.rst diff --git a/Sming/Components/Storage/README.rst b/Sming/Components/Storage/README.rst index 0ab3666e67..c4643c1408 100644 --- a/Sming/Components/Storage/README.rst +++ b/Sming/Components/Storage/README.rst @@ -286,7 +286,7 @@ Configuration Overrides ``standard`` to set 4Mbyte flash size spiffs - Adds a single SPIFFS partition. See :component:`spiffs`. + Adds a single SPIFFS partition. See :library:`Spiffs`. Other configurations may be available, depending on architecture. You can see these by running ``make hwconfig-list``. diff --git a/Sming/Components/Storage/src/include/Storage/Device.h b/Sming/Components/Storage/src/include/Storage/Device.h index 3b7ca0bb89..c6100e1561 100644 --- a/Sming/Components/Storage/src/include/Storage/Device.h +++ b/Sming/Components/Storage/src/include/Storage/Device.h @@ -10,7 +10,7 @@ #pragma once #include -#include +#include #include "PartitionTable.h" #define STORAGE_TYPE_MAP(XX) \ diff --git a/Sming/Components/rboot/component.mk b/Sming/Components/rboot/component.mk index 1e67a632b4..f432ac460c 100644 --- a/Sming/Components/rboot/component.mk +++ b/Sming/Components/rboot/component.mk @@ -11,6 +11,10 @@ COMPONENT_SUBMODULES := rboot COMPONENT_INCDIRS := rboot rboot/appcode include COMPONENT_SRCDIRS := src src/Arch/$(SMING_ARCH) +ifneq ($(DISABLE_WIFI),1) +COMPONENT_SRCDIRS += src/Network +endif + DEBUG_VARS += RBOOT_DIR RBOOT_DIR := $(COMPONENT_PATH) diff --git a/Sming/Components/rboot/src/RbootHttpUpdater.cpp b/Sming/Components/rboot/src/Network/RbootHttpUpdater.cpp similarity index 100% rename from Sming/Components/rboot/src/RbootHttpUpdater.cpp rename to Sming/Components/rboot/src/Network/RbootHttpUpdater.cpp diff --git a/Sming/Core/Data/Format/Formatter.h b/Sming/Core/Data/Format/Formatter.h index 24eaa55daa..3ba6790c65 100644 --- a/Sming/Core/Data/Format/Formatter.h +++ b/Sming/Core/Data/Format/Formatter.h @@ -13,7 +13,7 @@ #pragma once #include -#include +#include namespace Format { diff --git a/Sming/Core/Data/Format/Html.cpp b/Sming/Core/Data/Format/Html.cpp index cedb7180d0..7d4b41f746 100644 --- a/Sming/Core/Data/Format/Html.cpp +++ b/Sming/Core/Data/Format/Html.cpp @@ -11,7 +11,7 @@ ****/ #include "Html.h" -#include +#include namespace Format { diff --git a/Sming/Core/Data/Stream/DataSourceStream.h b/Sming/Core/Data/Stream/DataSourceStream.h index 9a90272024..053100fbfc 100644 --- a/Sming/Core/Data/Stream/DataSourceStream.h +++ b/Sming/Core/Data/Stream/DataSourceStream.h @@ -14,7 +14,7 @@ #include #include #include "SeekOrigin.h" -#include +#include "../WebConstants.h" /** @defgroup stream Stream functions * @brief Data stream classes diff --git a/Sming/Core/Data/Stream/IFS/HtmlDirectoryTemplate.cpp b/Sming/Core/Data/Stream/IFS/HtmlDirectoryTemplate.cpp index 256f270037..1a2feb3c6c 100644 --- a/Sming/Core/Data/Stream/IFS/HtmlDirectoryTemplate.cpp +++ b/Sming/Core/Data/Stream/IFS/HtmlDirectoryTemplate.cpp @@ -11,8 +11,8 @@ ****/ #include "HtmlDirectoryTemplate.h" -#include -#include +#include +#include namespace IFS { diff --git a/Sming/Core/Data/Stream/SectionTemplate.cpp b/Sming/Core/Data/Stream/SectionTemplate.cpp index c8a00acf5e..999aad8cfb 100644 --- a/Sming/Core/Data/Stream/SectionTemplate.cpp +++ b/Sming/Core/Data/Stream/SectionTemplate.cpp @@ -13,7 +13,7 @@ #include "SectionTemplate.h" #include "TemplateStream.h" #include -#include +#include "../WebConstants.h" namespace { diff --git a/Sming/Core/Network/WebConstants.cpp b/Sming/Core/Data/WebConstants.cpp similarity index 100% rename from Sming/Core/Network/WebConstants.cpp rename to Sming/Core/Data/WebConstants.cpp diff --git a/Sming/Core/Network/WebConstants.h b/Sming/Core/Data/WebConstants.h similarity index 100% rename from Sming/Core/Network/WebConstants.h rename to Sming/Core/Data/WebConstants.h diff --git a/Sming/Core/Network/WebHelpers/base64.cpp b/Sming/Core/Data/WebHelpers/base64.cpp similarity index 100% rename from Sming/Core/Network/WebHelpers/base64.cpp rename to Sming/Core/Data/WebHelpers/base64.cpp diff --git a/Sming/Core/Network/WebHelpers/base64.h b/Sming/Core/Data/WebHelpers/base64.h similarity index 100% rename from Sming/Core/Network/WebHelpers/base64.h rename to Sming/Core/Data/WebHelpers/base64.h diff --git a/Sming/Core/Network/WebHelpers/escape.cpp b/Sming/Core/Data/WebHelpers/escape.cpp similarity index 100% rename from Sming/Core/Network/WebHelpers/escape.cpp rename to Sming/Core/Data/WebHelpers/escape.cpp diff --git a/Sming/Core/Network/WebHelpers/escape.h b/Sming/Core/Data/WebHelpers/escape.h similarity index 100% rename from Sming/Core/Network/WebHelpers/escape.h rename to Sming/Core/Data/WebHelpers/escape.h diff --git a/Sming/Core/Network/WebHelpers/aw-sha1.cpp b/Sming/Core/Network/WebHelpers/aw-sha1.cpp deleted file mode 100644 index d05a38b5f8..0000000000 --- a/Sming/Core/Network/WebHelpers/aw-sha1.cpp +++ /dev/null @@ -1,110 +0,0 @@ - -/* - Copyright (c) 2014 Malte Hildingsson, malte (at) afterwi.se - - 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 the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - */ - -#include "aw-sha1.h" - -static inline void sha1mix(unsigned* _sha1_restrict r, unsigned* _sha1_restrict w) -{ - unsigned a = r[0]; - unsigned b = r[1]; - unsigned c = r[2]; - unsigned d = r[3]; - unsigned e = r[4]; - unsigned t, i = 0; - -#define rol(x, s) ((x) << (s) | (unsigned)(x) >> (32 - (s))) -#define mix(f, v) \ - do { \ - t = (f) + (v) + rol(a, 5) + e + w[i & 0xf]; \ - e = d; \ - d = c; \ - c = rol(b, 30); \ - b = a; \ - a = t; \ - } while(0) - - for(; i < 16; ++i) - mix(d ^ (b & (c ^ d)), 0x5a827999); - - for(; i < 20; ++i) { - w[i & 0xf] = rol(w[(i + 13) & 0xf] ^ w[(i + 8) & 0xf] ^ w[(i + 2) & 0xf] ^ w[i & 0xf], 1); - mix(d ^ (b & (c ^ d)), 0x5a827999); - } - - for(; i < 40; ++i) { - w[i & 0xf] = rol(w[(i + 13) & 0xf] ^ w[(i + 8) & 0xf] ^ w[(i + 2) & 0xf] ^ w[i & 0xf], 1); - mix(b ^ c ^ d, 0x6ed9eba1); - } - - for(; i < 60; ++i) { - w[i & 0xf] = rol(w[(i + 13) & 0xf] ^ w[(i + 8) & 0xf] ^ w[(i + 2) & 0xf] ^ w[i & 0xf], 1); - mix((b & c) | (d & (b | c)), 0x8f1bbcdc); - } - - for(; i < 80; ++i) { - w[i & 0xf] = rol(w[(i + 13) & 0xf] ^ w[(i + 8) & 0xf] ^ w[(i + 2) & 0xf] ^ w[i & 0xf], 1); - mix(b ^ c ^ d, 0xca62c1d6); - } - -#undef mix -#undef rol - - r[0] += a; - r[1] += b; - r[2] += c; - r[3] += d; - r[4] += e; -} - -void sha1(unsigned char h[SHA1_SIZE], const void* _sha1_restrict p, size_t n) -{ - size_t i = 0; - unsigned w[16], r[5] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0}; - - for(; i < (n & ~0x3f);) { - do - w[i >> 2 & 0xf] = ((const unsigned char*)p)[i + 3] << 0x00 | ((const unsigned char*)p)[i + 2] << 0x08 | - ((const unsigned char*)p)[i + 1] << 0x10 | ((const unsigned char*)p)[i + 0] << 0x18; - while((i += 4) & 0x3f); - sha1mix(r, w); - } - - memset(w, 0, sizeof w); - - for(; i < n; ++i) - w[i >> 2 & 0xf] |= ((const unsigned char*)p)[i] << (((3 ^ i) & 3) << 3); - - w[i >> 2 & 0xf] |= 0x80 << (((3 ^ i) & 3) << 3); - - if((n & 0x3f) > 56) { - sha1mix(r, w); - memset(w, 0, sizeof w); - } - - w[15] = n << 3; - sha1mix(r, w); - - for(i = 0; i < 5; ++i) - h[(i << 2) + 0] = (unsigned char)(r[i] >> 0x18), h[(i << 2) + 1] = (unsigned char)(r[i] >> 0x10), - h[(i << 2) + 2] = (unsigned char)(r[i] >> 0x08), h[(i << 2) + 3] = (unsigned char)(r[i] >> 0x00); -} diff --git a/Sming/Core/Network/WebHelpers/aw-sha1.h b/Sming/Core/Network/WebHelpers/aw-sha1.h deleted file mode 100644 index 6132c58422..0000000000 --- a/Sming/Core/Network/WebHelpers/aw-sha1.h +++ /dev/null @@ -1,44 +0,0 @@ - -/* - Copyright (c) 2014 Malte Hildingsson, malte (at) afterwi.se - - 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 the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - */ - -#pragma once - -#include - -#if defined(_MSC_VER) -#define _sha1_restrict __restrict -#else -#define _sha1_restrict __restrict__ -#endif - -#define SHA1_SIZE 20 - -#ifdef __cplusplus -extern "C" { -#endif - -void sha1(unsigned char h[SHA1_SIZE], const void* _sha1_restrict p, size_t n); - -#ifdef __cplusplus -} /* extern "C" */ -#endif diff --git a/Sming/Core/SmingCore.h b/Sming/Core/SmingCore.h index 7420d1ce26..928e339938 100644 --- a/Sming/Core/SmingCore.h +++ b/Sming/Core/SmingCore.h @@ -30,10 +30,12 @@ #include "Platform/RTC.h" #include "Platform/System.h" +#include "Platform/WDT.h" + +#ifndef DISABLE_WIFI #include "Platform/WifiEvents.h" #include "Platform/Station.h" #include "Platform/AccessPoint.h" -#include "Platform/WDT.h" #include "Network/DnsServer.h" #include "Network/HttpClient.h" @@ -49,6 +51,7 @@ #include "Network/TcpConnection.h" #include "Network/UdpConnection.h" #include "Network/Url.h" +#endif #include "Data/Stream/FileStream.h" #include "Data/Stream/TemplateFileStream.h" diff --git a/Sming/Libraries/HueEmulator b/Sming/Libraries/HueEmulator index 79d7e79754..72c333af81 160000 --- a/Sming/Libraries/HueEmulator +++ b/Sming/Libraries/HueEmulator @@ -1 +1 @@ -Subproject commit 79d7e797540519a4fcd9e73a6ca7c6e04715794e +Subproject commit 72c333af8161ce0343c440e5a0e6dbf8d1569171 diff --git a/Sming/Libraries/LittleFS b/Sming/Libraries/LittleFS index 5d41c9e136..1ff7443af7 160000 --- a/Sming/Libraries/LittleFS +++ b/Sming/Libraries/LittleFS @@ -1 +1 @@ -Subproject commit 5d41c9e136dc5d0a15118de6ed1c6a1fc9c84c5f +Subproject commit 1ff7443af7781518fcd3bb966de1a97a848441aa diff --git a/Sming/Libraries/OtaUpgradeMqtt/README.rst b/Sming/Libraries/OtaUpgradeMqtt/README.rst index 55386d1f0a..3756a75401 100644 --- a/Sming/Libraries/OtaUpgradeMqtt/README.rst +++ b/Sming/Libraries/OtaUpgradeMqtt/README.rst @@ -151,5 +151,5 @@ Configuration Default: 0 (disabled) If set to 1 the library will work with OtaUpgradeStream which supports signature and encryption of the firmware data itself. - See :component:`OtaUpgrade` for details. In the application the AdvancedPayloadParser can be used to do the MQTT message handling. + See :library:`OtaUpgrade` for details. In the application the AdvancedPayloadParser can be used to do the MQTT message handling. diff --git a/Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/README.rst b/Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/README.rst index cc14bab324..b0126b3b18 100644 --- a/Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/README.rst +++ b/Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/README.rst @@ -7,7 +7,7 @@ Introduction ------------ This example demonstrates how you can create an application that updates its firmware Over The Air (OTA) using the MQTT protocol. -This application uses :component:`OtaUpgradeMqtt` and follows the recommended versioning principles. +This application uses :library:`OtaUpgradeMqtt` and follows the recommended versioning principles. Based on :envvar:`ENABLE_OTA_ADVANCED` the firmware data can be either without any encoding or be signed and encrypted. @@ -15,7 +15,7 @@ Tools ----- There are two tools that facilitate the packiging and deployment of a new firmware. -For more information read ``Firmware packaging`` in the documentation of the :component:`OtaUpgradeMqtt` component. +For more information read ``Firmware packaging`` in the documentation of the :library:`OtaUpgradeMqtt` component. Security -------- @@ -67,7 +67,7 @@ Configuration Default: 0 (disabled) If set to 1 the library will work with OtaUpgradeStream which supports signature and encryption of the firmware data itself. - See :component:`OtaUpgrade` for details. + See :library:`OtaUpgrade` for details. .. envvar:: ENABLE_SSL diff --git a/Sming/Libraries/Spiffs/src/include/spiffs_config.h b/Sming/Libraries/Spiffs/src/include/spiffs_config.h index 6699389ed5..f72525e495 100644 --- a/Sming/Libraries/Spiffs/src/include/spiffs_config.h +++ b/Sming/Libraries/Spiffs/src/include/spiffs_config.h @@ -294,6 +294,15 @@ // the logical block size (log_block_size), and the logical page size // (log_page_size) +#ifdef ARCH_HOST +typedef uint8_t u8_t; +typedef int8_t s8_t; +typedef uint16_t u16_t; +typedef int16_t s16_t; +typedef uint32_t u32_t; +typedef int32_t s32_t; +#endif + // Block index type. Make sure the size of this type can hold // the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size typedef u16_t spiffs_block_ix; diff --git a/Sming/Libraries/UPnP b/Sming/Libraries/UPnP index e04f503c15..fdc5ee9915 160000 --- a/Sming/Libraries/UPnP +++ b/Sming/Libraries/UPnP @@ -1 +1 @@ -Subproject commit e04f503c151f9cd67d7ee06f156ede583b5f06e5 +Subproject commit fdc5ee991534c3e9caf8cd0ed002f3861b3a62fd diff --git a/Sming/README.rst b/Sming/README.rst index bc32255df2..0fc35032ef 100644 --- a/Sming/README.rst +++ b/Sming/README.rst @@ -81,6 +81,27 @@ Localisation This is provided as a #define symbol for your application to use. See :source:`Sming/Core/SmingLocale.h` for further details. + +Networking +~~~~~~~~~~ + +.. envvar:: DISABLE_WIFI + + .. note:: + + EXPERIMENTAL + + 0 (Default) + 1 - Remove core networking support + + Applications which do not require networking can set this flag to avoid building + or linking the core :component:`Network` library. + + This will reduce build times, application size and RAM usage. + Builds will not succeeded if network code has been inadvertently included. + + + Components ---------- diff --git a/Sming/Services/CommandProcessing/CommandDelegate.h b/Sming/Services/CommandProcessing/CommandDelegate.h index 772971372d..2acab00e7f 100644 --- a/Sming/Services/CommandProcessing/CommandDelegate.h +++ b/Sming/Services/CommandProcessing/CommandDelegate.h @@ -10,8 +10,8 @@ #pragma once -#include "WString.h" -#include "Network/TcpClient.h" +#include +#include #include "CommandOutput.h" /** @brief Command delegate function diff --git a/Sming/Services/CommandProcessing/CommandExecutor.cpp b/Sming/Services/CommandProcessing/CommandExecutor.cpp index f7c7f35535..46c4c81ddc 100644 --- a/Sming/Services/CommandProcessing/CommandExecutor.cpp +++ b/Sming/Services/CommandProcessing/CommandExecutor.cpp @@ -13,19 +13,20 @@ CommandExecutor::CommandExecutor() commandHandler.registerSystemCommands(); } -CommandExecutor::CommandExecutor(TcpClient* cmdClient) : CommandExecutor() +CommandExecutor::CommandExecutor(Stream* reqStream) : CommandExecutor() { - commandOutput = new CommandOutput(cmdClient); + commandOutput = new CommandOutput(reqStream); if(commandHandler.getVerboseMode() != SILENT) { - commandOutput->println(_F("Welcome to the Tcp Command executor")); + commandOutput->println(_F("Welcome to the Stream Command executor")); } } -CommandExecutor::CommandExecutor(Stream* reqStream) : CommandExecutor() +#ifndef DISABLE_WIFI +CommandExecutor::CommandExecutor(TcpClient* cmdClient) : CommandExecutor() { - commandOutput = new CommandOutput(reqStream); + commandOutput = new CommandOutput(cmdClient); if(commandHandler.getVerboseMode() != SILENT) { - commandOutput->println(_F("Welcome to the Stream Command executor")); + commandOutput->println(_F("Welcome to the Tcp Command executor")); } } @@ -36,6 +37,7 @@ CommandExecutor::CommandExecutor(WebsocketConnection* reqSocket) reqSocket->sendString(_F("Welcome to the Websocket Command Executor")); } } +#endif CommandExecutor::~CommandExecutor() { diff --git a/Sming/Services/CommandProcessing/CommandExecutor.h b/Sming/Services/CommandProcessing/CommandExecutor.h index fc01c15ab1..a1be0932c6 100644 --- a/Sming/Services/CommandProcessing/CommandExecutor.h +++ b/Sming/Services/CommandProcessing/CommandExecutor.h @@ -7,19 +7,24 @@ #pragma once -#include #include "CommandHandler.h" #include "CommandOutput.h" #include +#ifndef DISABLE_WIFI +#include +#endif + #define MAX_COMMANDSIZE 64 class CommandExecutor { public: +#ifndef DISABLE_WIFI CommandExecutor(TcpClient* cmdClient); - CommandExecutor(Stream* reqStream); CommandExecutor(WebsocketConnection* reqSocket); +#endif + CommandExecutor(Stream* reqStream); ~CommandExecutor(); int executorReceive(char* recvData, int recvSize); diff --git a/Sming/Services/CommandProcessing/CommandHandler.cpp b/Sming/Services/CommandProcessing/CommandHandler.cpp index 34e541259d..752dd42a25 100644 --- a/Sming/Services/CommandProcessing/CommandHandler.cpp +++ b/Sming/Services/CommandProcessing/CommandHandler.cpp @@ -99,8 +99,10 @@ void CommandHandler::procesStatusCommand(String commandLine, CommandOutput* comm commandOutput->print(_F("ESP SDK version : ")); commandOutput->print(system_get_sdk_version()); commandOutput->println(); +#ifndef DISABLE_WIFI commandOutput->printf(_F("lwIP version : %d.%d.%d(%s)\r\n"), LWIP_VERSION_MAJOR, LWIP_VERSION_MINOR, LWIP_VERSION_REVISION, LWIP_HASH_STR); +#endif commandOutput->print(_F("Time = ")); commandOutput->print(SystemClock.getSystemTimeString()); commandOutput->println(); diff --git a/Sming/Services/CommandProcessing/CommandOutput.cpp b/Sming/Services/CommandProcessing/CommandOutput.cpp index b8f85eced3..66d97d8a5d 100644 --- a/Sming/Services/CommandProcessing/CommandOutput.cpp +++ b/Sming/Services/CommandProcessing/CommandOutput.cpp @@ -6,19 +6,12 @@ */ #include "CommandOutput.h" - -CommandOutput::CommandOutput(TcpClient* reqClient) : outputTcpClient(reqClient) -{ -} +#include CommandOutput::CommandOutput(Stream* reqStream) : outputStream(reqStream) { } -CommandOutput::CommandOutput(WebsocketConnection* reqSocket) : outputSocket(reqSocket) -{ -} - CommandOutput::~CommandOutput() { debugf("destruct"); @@ -26,15 +19,15 @@ CommandOutput::~CommandOutput() size_t CommandOutput::write(uint8_t outChar) { - if(outputTcpClient) { - char outBuf[1] = {char(outChar)}; - return outputTcpClient->write(outBuf, 1); - } - if(outputStream) { return outputStream->write(outChar); } +#ifndef DISABLE_WIFI + if(outputTcpClient) { + char outBuf[1] = {char(outChar)}; + return outputTcpClient->write(outBuf, 1); + } if(outputSocket) { if(outChar == '\r') { outputSocket->sendString(tempSocket); @@ -45,6 +38,7 @@ size_t CommandOutput::write(uint8_t outChar) return 1; } +#endif return 0; } diff --git a/Sming/Services/CommandProcessing/CommandOutput.h b/Sming/Services/CommandProcessing/CommandOutput.h index 9bef0cdd0e..b1f4cd4b12 100644 --- a/Sming/Services/CommandProcessing/CommandOutput.h +++ b/Sming/Services/CommandProcessing/CommandOutput.h @@ -7,23 +7,37 @@ #pragma once -#include #include #include + +#ifndef DISABLE_WIFI +#include #include +#endif class CommandOutput : public Print { public: - CommandOutput(TcpClient* reqClient); +#ifndef DISABLE_WIFI + CommandOutput(TcpClient* reqClient) : outputTcpClient(reqClient) + { + } + + CommandOutput(WebsocketConnection* reqSocket) : outputSocket(reqSocket) + { + } + +#endif + CommandOutput(Stream* reqStream); - CommandOutput(WebsocketConnection* reqSocket); virtual ~CommandOutput(); size_t write(uint8_t outChar); +#ifndef DISABLE_WIFI TcpClient* outputTcpClient = nullptr; - Stream* outputStream = nullptr; WebsocketConnection* outputSocket = nullptr; +#endif + Stream* outputStream = nullptr; String tempSocket = ""; }; diff --git a/Sming/SmingCore/SmingCore.h b/Sming/SmingCore/SmingCore.h index 006b45476c..3bc5bbde27 100644 --- a/Sming/SmingCore/SmingCore.h +++ b/Sming/SmingCore/SmingCore.h @@ -1,2 +1,2 @@ #include -#pragma warning "Please update to #include - see https://github.com/SmingHub/Sming/wiki/Migrating-from-Sming-v3.8.x-to-v4" +#pragma GCC warning "Please update to #include - see https://sming.readthedocs.io/en/4.3.0/upgrading/3.8-4.0.html" diff --git a/Sming/Wiring/FlashString.h b/Sming/Wiring/FlashString.h index 22377f2518..2c47de23ed 100644 --- a/Sming/Wiring/FlashString.h +++ b/Sming/Wiring/FlashString.h @@ -14,4 +14,4 @@ #include "WString.h" -#pragma warning "It is no longer necessary to #include FlashString.h in your projects, WString.h is sufficient" +#pragma GCC warning "It is no longer necessary to #include FlashString.h in your projects, WString.h is sufficient" diff --git a/Sming/Wiring/WiringFrameworkIncludes.h b/Sming/Wiring/WiringFrameworkIncludes.h index 66c8f86176..8b40ba7660 100644 --- a/Sming/Wiring/WiringFrameworkIncludes.h +++ b/Sming/Wiring/WiringFrameworkIncludes.h @@ -1,7 +1,7 @@ /* * WiringFrameworkIncludes.h * - * Created on: 28 ÿíâ. 2015 ã. + * Created on: 28 ���. 2015 �. * Author: Anakonda */ @@ -24,4 +24,7 @@ #include "Stream.h" #include "Display.h" #include "WHashMap.h" -#include "IpAddress.h" + +#ifndef DISABLE_WIFI +#include +#endif diff --git a/Sming/building.rst b/Sming/building.rst index d6557b053d..89fe66115c 100644 --- a/Sming/building.rst +++ b/Sming/building.rst @@ -513,7 +513,7 @@ changed as required. part of the Component, prefixed with ``$(COMPONENT_RULE)``. If targets should be built for each application, use :envvar:`CUSTOM_TARGETS` instead. - See :component:`spiffs` for an example. + See :library:`Spiffs` for an example. .. envvar:: COMPONENT_PREREQUISITES diff --git a/Sming/component.mk b/Sming/component.mk index 5d4e4306df..e597ce07c6 100644 --- a/Sming/component.mk +++ b/Sming/component.mk @@ -3,8 +3,7 @@ COMPONENT_SRCDIRS := \ Platform \ System \ Wiring \ - Services/HexDump \ - Services/Yeelight + Services/HexDump COMPONENT_INCDIRS := \ Components \ @@ -18,23 +17,8 @@ COMPONENT_DEPENDS := \ FlashString \ Spiffs \ IFS \ - http-parser \ - libb64 \ - ws_parser \ - mqtt-codec \ - libyuarel \ - ssl \ terminal -COMPONENT_DOCFILES := \ - Core/Network/*.rst \ - Wiring/*.rst \ - Platform/*.rst \ - Services/*.rst \ - Services/CommandProcessing/*.rst \ - Services/Profiling/*.rst \ - Core/Data/*.rst - COMPONENT_DOXYGEN_PREDEFINED := \ ENABLE_CMD_EXECUTOR=1 @@ -61,6 +45,15 @@ ifeq ($(MQTT_NO_COMPAT),1) GLOBAL_CFLAGS += -DMQTT_NO_COMPAT=1 endif +# +RELINK_VARS += DISABLE_WIFI +DISABLE_WIFI ?= 0 +ifeq ($(DISABLE_WIFI),1) +GLOBAL_CFLAGS += -DDISABLE_WIFI=1 +else +COMPONENT_DEPENDS += Network +endif + # WiFi settings may be provide via Environment variables CONFIG_VARS += WIFI_SSID WIFI_PWD ifdef WIFI_SSID @@ -68,33 +61,12 @@ ifdef WIFI_SSID APP_CFLAGS += -DWIFI_PWD=\"$(WIFI_PWD)\" endif -# => WPS -COMPONENT_VARS += ENABLE_WPS -ifeq ($(ENABLE_WPS), 1) - GLOBAL_CFLAGS += -DENABLE_WPS=1 -endif - -# => Smart Config -COMPONENT_VARS += ENABLE_SMART_CONFIG -ifeq ($(ENABLE_SMART_CONFIG),1) - GLOBAL_CFLAGS += -DENABLE_SMART_CONFIG=1 -endif - # => LOCALE COMPONENT_VARS += LOCALE ifdef LOCALE GLOBAL_CFLAGS += -DLOCALE=$(LOCALE) endif -# => HTTP server -COMPONENT_VARS += HTTP_SERVER_EXPOSE_NAME -HTTP_SERVER_EXPOSE_NAME ?= 1 -GLOBAL_CFLAGS += -DHTTP_SERVER_EXPOSE_NAME=$(HTTP_SERVER_EXPOSE_NAME) - -COMPONENT_VARS += HTTP_SERVER_EXPOSE_VERSION -HTTP_SERVER_EXPOSE_VERSION ?= 0 -GLOBAL_CFLAGS += -DHTTP_SERVER_EXPOSE_VERSION=$(HTTP_SERVER_EXPOSE_VERSION) - # => SPI COMPONENT_VARS += ENABLE_SPI_DEBUG ENABLE_SPI_DEBUG ?= 0 diff --git a/docs/source/framework/core/data/cstringarray.rst b/docs/source/framework/core/data/cstringarray.rst index d2b5cc5d2b..78a7dfe746 100644 --- a/docs/source/framework/core/data/cstringarray.rst +++ b/docs/source/framework/core/data/cstringarray.rst @@ -13,7 +13,7 @@ stored in a single :cpp:class:`String` object. (CStringArray is a subclass of St You can see some examples in :source:`Sming/Core/DateTime.cpp` and -:source:`Sming/Core/Network/WebConstants.cpp`. +:source:`Sming/Core/Data/WebConstants.cpp`. Background ---------- @@ -144,8 +144,6 @@ Disadvantages Slower. Items must be iterated using multiple strlen() calls Ordering and insertions / deletions not supported -An example of use can be found in :source:`Sming/Core/Network/Http/HttpHeaders.h`. - API Documentation ----------------- diff --git a/docs/source/framework/core/index.rst b/docs/source/framework/core/index.rst index 83ee2ae4a5..081c9c6f23 100644 --- a/docs/source/framework/core/index.rst +++ b/docs/source/framework/core/index.rst @@ -6,4 +6,3 @@ Core Framework pgmspace data/index - network/index diff --git a/docs/source/framework/core/network/index.rst b/docs/source/framework/core/network/index.rst deleted file mode 100644 index f4ada09578..0000000000 --- a/docs/source/framework/core/network/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -Networking Protocols -==================== - -.. toctree:: - :glob: - :maxdepth: 1 - - * - /_inc/Sming/Components/mdns/index - /_inc/Sming/Components/ssl/index diff --git a/docs/source/framework/index.rst b/docs/source/framework/index.rst index 01f596f284..6d145f7d05 100644 --- a/docs/source/framework/index.rst +++ b/docs/source/framework/index.rst @@ -9,3 +9,5 @@ Framework platform/index services/index wiring/index + +- :component:`Network` diff --git a/docs/source/getting-started/windows/index.rst b/docs/source/getting-started/windows/index.rst index b31bd9b026..3c23e8ec52 100644 --- a/docs/source/getting-started/windows/index.rst +++ b/docs/source/getting-started/windows/index.rst @@ -1,7 +1,7 @@ Windows Installation ==================== -.. highlight:: powershell +.. highlight:: batch This page describes how to install the required tools and obtain the current release version of Sming using the `Chocolatey `__ package manager. @@ -36,7 +36,7 @@ At the moment the Esp32 toolchain is not installed by default. If you want to in It is also inadvisable to continue running with elevated privileges. If you followed and executed carefully the steps above Sming should be installed and configured. -You can scroll down to :ref:`Build Basic_Blink` to check the installation. +You can scroll down to `Build Basic_Blink`_ to check the installation. Optional step: Switch to stable version ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/source/index.rst b/docs/source/index.rst index 9111286243..7019f1eeb8 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -21,7 +21,7 @@ Summary - Fast and user friendly development - Simple yet powerful hardware API wrappers - Compatible with standard :doc:`libraries` - use any popular hardware in few lines of code -- Built-in file system: :component:`spiffs` +- Multiple file system support: :component:`IFS`, :library:`Spiffs`, :library:`LittleFS` - Built-in powerful wireless modules - Powerful asynchronous (async) network stack. diff --git a/docs/source/information/develop/components.rst b/docs/source/information/develop/components.rst index 5e880c3c2e..8f9d0c0e20 100644 --- a/docs/source/information/develop/components.rst +++ b/docs/source/information/develop/components.rst @@ -13,7 +13,7 @@ The aim here is to make an existing library, https://github.com/nomis/ModbusMast available for applications to use within the framework. Various options and settings are only mentioned here, but are linked through to -:doc:`building` where you can find full details of what they are and how they work. +:doc:`/_inc/Sming/building` where you can find full details of what they are and how they work. You will need to be familiar with GIT and the use of submodules. @@ -169,5 +169,5 @@ with a level 1 heading and brief introduction at an absolute minimum. You may not be familiar with ``.rst`` files but they are a considerably improvement on markdown and well worth investing a little time to learn. - See :doc:`/contribute/documentation` for further details. + See :doc:`/contribute/index` for further details. diff --git a/docs/source/information/develop/documentation.rst b/docs/source/information/develop/documentation.rst index 0b7ba17927..d9164045e8 100644 --- a/docs/source/information/develop/documentation.rst +++ b/docs/source/information/develop/documentation.rst @@ -240,7 +240,7 @@ Inserting a link to a Component page, using the title of that page by default: :: - See :component:`spiffs` for details of the flash filing system. + See :library:`Spiffs` for details of the flash filing system. We use :component-esp8266:`axtls-8266` for SSL support. @@ -276,7 +276,7 @@ To refer to source code use the path relative to the root working directory, for :: - See :source:`Sming/Core/Network/Url.h` + See :source:`Sming/Core/DateTime.h` If the documentation is built locally, it will use the local file path, otherwise it will create a link to the source file on github. diff --git a/docs/source/information/events.rst b/docs/source/information/events.rst index 87e228d230..42585042e1 100644 --- a/docs/source/information/events.rst +++ b/docs/source/information/events.rst @@ -43,10 +43,10 @@ Interrupt events The :sample:`Basic_Interrupts` sample can be summarised as follows: -- We have a button connected to GPIO0 as an input -- Button pressed - - Hardware interrupt on GPIO0 - - Change output state of LED via GPIO2 +- We have a button connected to GPIO0 as an input +- Button pressed + - Hardware interrupt on GPIO0 + - Change output state of LED via GPIO2 There are two ways we can determine when the state of our GPIO0 pin changes: diff --git a/docs/source/troubleshooting/random-restart.rst b/docs/source/troubleshooting/random-restart.rst index c9d4032ed0..10b5ec2a70 100644 --- a/docs/source/troubleshooting/random-restart.rst +++ b/docs/source/troubleshooting/random-restart.rst @@ -25,4 +25,4 @@ To achieve this do the following: 3) Re-program your device:: - make flash + make flash diff --git a/docs/source/upgrading/4.1-4.2.rst b/docs/source/upgrading/4.1-4.2.rst index b2398a3ed8..3e76580158 100644 --- a/docs/source/upgrading/4.1-4.2.rst +++ b/docs/source/upgrading/4.1-4.2.rst @@ -41,7 +41,7 @@ getBody methods The :cpp:func:`HttpRequest::getBody` and :cpp:func:`HttpResponse::getBody` methods have been revised to use move semantics. Previously, the data was copied into a new String which effectively doubled memory usage. -If you have set a non-memory stream type (e.g. :cpp:class:`FileStream`) which does not implement `:cpp:func:`IDataSourceStream::moveString()` +If you have set a non-memory stream type (e.g. :cpp:class:`FileStream`) which does not implement :cpp:func:`IDataSourceStream::moveString()` then an invalid String will be returned. In this situation you should use :cpp:func:`HttpResponse::getBodyStream` instead. diff --git a/docs/source/upgrading/4.2-4.3.rst b/docs/source/upgrading/4.2-4.3.rst index 6698e12634..01e24235c8 100644 --- a/docs/source/upgrading/4.2-4.3.rst +++ b/docs/source/upgrading/4.2-4.3.rst @@ -19,9 +19,9 @@ See also background on :doc:`/information/flash`. Removed build targets spiffs-image-update - Use the new `buildpart` target instead + Use the new ``buildpart`` target instead spiffs-image-clean - Use the new `part-clean` target instead + Use the new ``part-clean`` target instead New and updated build targets hwconfig diff --git a/docs/source/upgrading/4.3-4.4.rst b/docs/source/upgrading/4.3-4.4.rst new file mode 100644 index 0000000000..db0dd88adf --- /dev/null +++ b/docs/source/upgrading/4.3-4.4.rst @@ -0,0 +1,19 @@ +From v4.3 to v4.4 +================= + +4.3.1 +----- + +.. highlight:: c++ + +Network support +~~~~~~~~~~~~~~~ + +The core network code has been moved out of ``Sming/Core/Network`` and into a separate component at ``Components/Network``. +This is to extend the use of the :envvar:`DISABLE_WIFI` setting to reduce the code required to be built for applications which +do not require networking. + +Some support code has been moved into ``Core/Data/WebHelpers``: applications should still build OK but you will get +a compiler warning advising of the changes. + +Note that ``Network/WebHelpers/aw-sha1.h`` has been removed in favour of the :component:`crypto` library. diff --git a/docs/source/upgrading/index.rst b/docs/source/upgrading/index.rst index 4b024707b8..0365edf5b5 100644 --- a/docs/source/upgrading/index.rst +++ b/docs/source/upgrading/index.rst @@ -7,6 +7,7 @@ For newer versions we have dedicated pages. .. toctree:: :maxdepth: 1 + 4.3-4.4 4.2-4.3 4.1-4.2 4.0-4.1 diff --git a/samples/Basic_Utility/component.mk b/samples/Basic_Utility/component.mk index bde909c92d..e531cfcec6 100644 --- a/samples/Basic_Utility/component.mk +++ b/samples/Basic_Utility/component.mk @@ -6,4 +6,5 @@ APP_NAME := utility ENABLE_MALLOC_COUNT := 0 # +DISABLE_WIFI := 1 HOST_NETWORK_OPTIONS := --nonet diff --git a/samples/FtpServer_Files/fsimage.fwfs b/samples/FtpServer_Files/fsimage.fwfs index 8cfe051a20..03d14739d8 100644 --- a/samples/FtpServer_Files/fsimage.fwfs +++ b/samples/FtpServer_Files/fsimage.fwfs @@ -2,8 +2,8 @@ "name": "FTP Server Demo Volume", "id": "0x12345678", "source": { - "/": "${SMING_HOME}/../docs/source/framework/core/network", - "Network": "${SMING_HOME}/Core/Network" + "/": "${SMING_HOME}/../docs/source/framework", + "Network": "${SMING_HOME}/Components/Network" }, "mountpoints": {}, "rules": [ diff --git a/tests/HostTests/modules/Base64.cpp b/tests/HostTests/modules/Base64.cpp index 916899b38e..0c4cdd58b1 100644 --- a/tests/HostTests/modules/Base64.cpp +++ b/tests/HostTests/modules/Base64.cpp @@ -1,6 +1,6 @@ #include -#include +#include class Base64Test : public TestGroup { diff --git a/tests/HostTests/modules/Http.cpp b/tests/HostTests/modules/Http.cpp index 607c9c2805..e8b1e2657f 100644 --- a/tests/HostTests/modules/Http.cpp +++ b/tests/HostTests/modules/Http.cpp @@ -2,7 +2,7 @@ #include "Network/Http/HttpCommon.h" #include "Network/Http/HttpHeaders.h" -#include "Network/WebConstants.h" +#include #include class HttpTest : public TestGroup diff --git a/tests/HostTests/modules/Stream.cpp b/tests/HostTests/modules/Stream.cpp index a3d98ffa83..b5894ea850 100644 --- a/tests/HostTests/modules/Stream.cpp +++ b/tests/HostTests/modules/Stream.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include DEFINE_FSTR_LOCAL(template1, "Stream containing {var1}, {var2} and {var3}. {} {{}} {{12345") DEFINE_FSTR_LOCAL(template1_1, "Stream containing value #1, value #2 and {var3}. {} {{}} {{12345") From f84d80d0c851b62072e48b65d1f3e95fbaf9ee93 Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 27 Apr 2021 20:20:02 +0100 Subject: [PATCH 016/130] Fix vscode GDB launch configuration (#2319) * Load full debug configuration * Fix GDB launch targets --- Sming/Arch/Esp8266/build.mk | 1 - Sming/build.mk | 11 ++++ Sming/project.mk | 12 +--- Tools/vscode/setup.py | 112 +++++++++++++++++++----------------- 4 files changed, 72 insertions(+), 64 deletions(-) diff --git a/Sming/Arch/Esp8266/build.mk b/Sming/Arch/Esp8266/build.mk index 84f5e6376f..1d41353210 100644 --- a/Sming/Arch/Esp8266/build.mk +++ b/Sming/Arch/Esp8266/build.mk @@ -30,7 +30,6 @@ CONFIG_TOOLPREFIX := xtensa-lx106-elf- TOOLSPEC := $(XTENSA_TOOLS_ROOT)/$(CONFIG_TOOLPREFIX) # select which tools to use as assembler, compiler, librarian and linker -DEBUG_VARS += GDB AS := $(TOOLSPEC)gcc CC := $(TOOLSPEC)gcc CXX := $(TOOLSPEC)g++ diff --git a/Sming/build.mk b/Sming/build.mk index 6ee11a70a9..9ad74cbaec 100644 --- a/Sming/build.mk +++ b/Sming/build.mk @@ -10,6 +10,17 @@ override SMING_ARCH := Esp8266 endif export SMING_ARCH +# Paths for standard build tools +DEBUG_VARS += \ + AS \ + CC \ + CXX \ + AR \ + LD \ + OBJCOPY \ + OBJDUMP \ + GDB + DEBUG_VARS += SMING_RELEASE ifeq ($(SMING_RELEASE),1) BUILD_TYPE := release diff --git a/Sming/project.mk b/Sming/project.mk index 7cf647de90..ae6f973e0e 100644 --- a/Sming/project.mk +++ b/Sming/project.mk @@ -98,6 +98,8 @@ LDFLAGS = \ -Wl,--gc-sections \ -Wl,-Map=$(basename $@).map +# Name of the default output target +DEBUG_VARS += TARGET_OUT_0 # Name of the application to use for link output targets CACHE_VARS += APP_NAME @@ -529,15 +531,7 @@ export HOST_PARAMETERS .PHONY: ide-vscode-update ide-vscode-update: - $(Q) CXX=$(CXX) \ - SMING_HOME=$(SMING_HOME) \ - ESP_HOME=$(ESP_HOME) \ - IDF_PATH=$(IDF_PATH) \ - IDF_TOOLS_PATH=$(IDF_TOOLS_PATH) \ - SMING_ARCH=$(SMING_ARCH) \ - GDB=$(GDB) \ - COM_SPEED_GDB=$(COM_SPEED_GDB) \ - WSL_ROOT=$(WSL_ROOT) \ + $(Q) SMING_HOME=$(SMING_HOME) OUT_BASE=$(OUT_BASE) \ $(PYTHON) $(SMING_HOME)/../Tools/vscode/setup.py ##@Testing diff --git a/Tools/vscode/setup.py b/Tools/vscode/setup.py index 8363406a23..3576b36451 100644 --- a/Tools/vscode/setup.py +++ b/Tools/vscode/setup.py @@ -3,28 +3,29 @@ # Sming hardware configuration tool # -import os, sys, json, shutil +import os, sys, json, shutil, configparser, string -class Env: - """ Cache required environment variables""" +class Env(dict): + """ Cache build environment variables""" def __init__(self): - self.SMING_HOME = os.environ['SMING_HOME'] - self.SMING_ARCH = os.environ['SMING_ARCH'] - self.WSL_ROOT = os.environ.get('WSL_ROOT', '') - vars = [] - if self.SMING_ARCH == 'Esp8266': - vars += ['ESP_HOME'] - if self.SMING_ARCH == 'Esp32': - vars += ['IDF_PATH', 'IDF_TOOLS_PATH'] - for name in vars: - value = os.environ.get(name, None) - if not value: - print("Warning: env['%s'] not found" % name) - setattr(self, name, value) + self.update(os.environ) + self.updateFromConfig('config.mk') + self.updateFromConfig('debug.mk') + + def updateFromConfig(self, filename): + path = self['OUT_BASE'] + '/' + filename + if not os.path.exists(path): + return + parser = configparser.ConfigParser() + parser.optionxform = str # preserve case + with open(path) as f: + data = "[config]\n" + f.read() + parser.read_string(data) + self.update(parser['config']) def replace(self, path, name, prefix): - value = getattr(self, name, None) + value = self.get(name) if value is not None: s = fix_path(path) value = fix_path(value) @@ -35,7 +36,14 @@ def replace(self, path, name, prefix): def resolve(self, path): """Convert any embedded environment variables into real paths """ - return os.path.expandvars(path) + tmp = str(path) + while True: + tmp = tmp.replace('(', '{') + tmp = tmp.replace(')', '}') + new_path = string.Template(tmp).safe_substitute(self) + if new_path == tmp: + return new_path + tmp = new_path def subst_path(self, path, prefix=''): path = self.replace(path, 'SMING_HOME', prefix) @@ -45,15 +53,7 @@ def subst_path(self, path, prefix=''): return path def isWsl(self): - return self.WSL_ROOT != '' - - def update(self, env): - env['SMING_HOME'] = self.SMING_HOME - if self.SMING_ARCH == 'Esp8266': - env['ESP_HOME'] = self.ESP_HOME - if self.SMING_ARCH == 'Esp32': - env['IDF_PATH'] = self.IDF_PATH - env['IDF_TOOLS_PATH'] = self.IDF_PATH + return self.get('WSL_ROOT', '') != '' env = Env() @@ -119,7 +119,7 @@ def get_property(data, name, default): def update_intellisense(): dirs = [] - for d in os.environ['COMPONENTS_EXTRA_INCDIR'].split(): + for d in env['COMPONENTS_EXTRA_INCDIR'].split(): if os.path.exists(d): dirs += [check_path(env.subst_path(d))] @@ -132,15 +132,15 @@ def update_intellisense(): env.update(get_property(properties, 'env', {})) configurations = get_property(properties, 'configurations', []) - config = find_object(configurations, env.SMING_ARCH) + config = find_object(configurations, env['SMING_ARCH']) if config is None: config = load_template('intellisense/configuration.json') - config['name'] = env.SMING_ARCH - config['defines'].append('ARCH_%s=1' % env.SMING_ARCH.upper()) + config['name'] = env['SMING_ARCH'] + config['defines'].append('ARCH_%s=1' % env['SMING_ARCH'].upper()) configurations.append(config) - config['compilerPath'] = find_tool(os.environ['CXX']) + config['compilerPath'] = find_tool(env['CXX']) config['includePath'] = dirs save_json(properties, propertiesFile) @@ -162,29 +162,33 @@ def update_launch(): if launch is None: launch = template.copy() configurations = get_property(launch, 'configurations', []) - for template_config in template['configurations']: - config = find_object(configurations, template_config['name']) - if not config is None: - configurations.remove(config) + config_name = "%s GDB" % env['SMING_ARCH'] + config = find_object(configurations, config_name) + template_config = find_object(template['configurations'], config_name) + if template_config is None: + print("Warning: Template launch configuration '%s' not found" % config_name) + elif not config is None: + configurations.remove(config) config = template_config.copy() configurations.append(config) - config = find_object(configurations, "%s GDB" % env.SMING_ARCH) - if not config is None: - config['miDebuggerPath'] = find_tool(os.environ['GDB']) - dbgargs = "-x ${env:SMING_HOME}/Arch/%s/Components/gdbstub/gdbcmds" % env.SMING_ARCH - if env.SMING_ARCH == 'Esp8266': - if not env.isWsl(): - dbgargs += " -b %s" % os.environ['COM_SPEED_GDB'] - config['miDebuggerServerAddress'] = os.environ['COM_PORT_GDB'] - elif env.SMING_ARCH == 'Host': - args = [] - args += os.environ['CLI_TARGET_OPTIONS'].split() - args += ["--pause"] - args += ["--"] - args += os.environ['HOST_PARAMETERS'].split() - config['args'] = args - config['miDebuggerArgs'] = dbgargs + if config is None: + return + + config['miDebuggerPath'] = find_tool(env['GDB']) + dbgargs = "-x ${env:SMING_HOME}/Arch/%s/Components/gdbstub/gdbcmds" % env['SMING_ARCH'] + if env['SMING_ARCH'] == 'Esp8266': + if not env.isWsl(): + dbgargs += " -b %s" % env['COM_SPEED_GDB'] + config['miDebuggerServerAddress'] = env['COM_PORT_GDB'] + elif env['SMING_ARCH'] == 'Host': + args = [] + args += env['CLI_TARGET_OPTIONS'].split() + args += ["--"] + args += env['HOST_PARAMETERS'].split() + config['args'] = args + config['miDebuggerArgs'] = dbgargs + config['program'] = "${workspaceFolder}/" + env.resolve('${TARGET_OUT_0}') save_json(launch, filename) @@ -202,11 +206,11 @@ def update_workspace(): save_json(ws, filename) def main(): - if not env.SMING_HOME or not env.SMING_ARCH: + if not env['SMING_HOME'] or not env['SMING_ARCH']: sys.exit(1) # So we can find rjsmin.py - sys.path.append(os.path.join(env.SMING_HOME, 'Components/Storage/Tools/hwconfig')) + sys.path.append(os.path.join(env['SMING_HOME'], 'Components/Storage/Tools/hwconfig')) update_intellisense() update_tasks() From 1437b231aeafcf788193946a8374bbe94af54515 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 29 Apr 2021 10:13:21 +0100 Subject: [PATCH 017/130] IFS attribute enumeration support (#2318) This PR implements a mechanism for efficiently enumerating file attributes. This is necessary for file copying, filtering, etc. **IFS** Add IFileSystem `fenumxattr` and `enumxattr` methods Add `OpenFlag::NoFollow` to support enumeration of mount point objects Add creationTime field to `IFileSystem::Info` Fix issue where `Directory` class size/count keeps increasing if `rewind()` used Revise test application to use LittleFS for Hybrid tests **LittleFS** Re-mount after format if appropriate Use ACL for root directory as default (consistent with FWFS behaviour) ACL stored in separate read/write entries; existing LittleFS partitions must be rebuilt. **Hybrid file system** Add check in mount() to ensure provided filesystem is writable In mount() copy root ACL from FWFS to use as default Ensure parent directories are created in open() method Repeat tests for both SPIFFS and LittleFS variants **Basic_IFS sample** Update to demonstrate enumeration --- Sming/Components/IFS | 2 +- .../Network/src/Network/Http/HttpResponse.cpp | 2 +- Sming/Libraries/LittleFS | 2 +- Sming/Libraries/Spiffs/src/FileMeta.cpp | 224 ++++++++++++++++++ Sming/Libraries/Spiffs/src/FileSystem.cpp | 30 ++- .../Spiffs/src/include/IFS/SPIFFS/FileMeta.h | 182 ++------------ .../src/include/IFS/SPIFFS/FileSystem.h | 1 + samples/Basic_IFS/app/application.cpp | 48 +++- 8 files changed, 306 insertions(+), 185 deletions(-) create mode 100644 Sming/Libraries/Spiffs/src/FileMeta.cpp diff --git a/Sming/Components/IFS b/Sming/Components/IFS index 0b3588a4db..b59301c5aa 160000 --- a/Sming/Components/IFS +++ b/Sming/Components/IFS @@ -1 +1 @@ -Subproject commit 0b3588a4db2f0650bc9f57cf0edcfb0cb12272fc +Subproject commit b59301c5aafae4d3846525bb0d24192b6d3b7fd4 diff --git a/Sming/Components/Network/src/Network/Http/HttpResponse.cpp b/Sming/Components/Network/src/Network/Http/HttpResponse.cpp index 95b5a3ab9d..73222e8c51 100644 --- a/Sming/Components/Network/src/Network/Http/HttpResponse.cpp +++ b/Sming/Components/Network/src/Network/Http/HttpResponse.cpp @@ -81,7 +81,7 @@ bool HttpResponse::sendFile(const String& fileName, bool allowGzipFileCheck) if(stat.compression.type == IFS::Compression::Type::GZip) { headers[HTTP_HEADER_CONTENT_ENCODING] = F("gzip"); } else if(stat.compression.type != IFS::Compression::Type::None) { - debug_e("Unsupported compression type: %u", stat.compression); + debug_e("Unsupported compression type: %s", ::toString(stat.compression.type).c_str()); } return sendDataStream(fs, ContentType::fromFullFileName(fileName)); } diff --git a/Sming/Libraries/LittleFS b/Sming/Libraries/LittleFS index 1ff7443af7..0514211e5f 160000 --- a/Sming/Libraries/LittleFS +++ b/Sming/Libraries/LittleFS @@ -1 +1 @@ -Subproject commit 1ff7443af7781518fcd3bb966de1a97a848441aa +Subproject commit 0514211e5f30b12d2f69ab4b61e8a59b9dc2f963 diff --git a/Sming/Libraries/Spiffs/src/FileMeta.cpp b/Sming/Libraries/Spiffs/src/FileMeta.cpp new file mode 100644 index 0000000000..f603a8f27d --- /dev/null +++ b/Sming/Libraries/Spiffs/src/FileMeta.cpp @@ -0,0 +1,224 @@ +/** + * FileMeta.cpp + * + * Created on: 21 Jul 2018 + * + * Copyright 2019 mikee47 + * + * This file is part of the SPIFFS IFS Library + * + * This library is free software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, version 3 or later. + * + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this library. + * If not, see . + * + ****/ + +#include "include/IFS/SPIFFS/FileMeta.h" +#include + +namespace IFS +{ +namespace SPIFFS +{ +void* FileMeta::getAttributePtr(AttributeTag tag) +{ + switch(tag) { + case AttributeTag::ModifiedTime: + return &mtime; + case AttributeTag::ReadAce: + return &acl.readAccess; + case AttributeTag::WriteAce: + return &acl.writeAccess; + case AttributeTag::Compression: + return &compression; + case AttributeTag::FileAttributes: + return &attr; + default: + return nullptr; + } +} + +int SpiffsMetaBuffer::enumxattr(AttributeEnumCallback callback, void* buffer, size_t bufsize) +{ + size_t count{0}; + AttributeEnum e{buffer, bufsize}; + + for(unsigned i = 0; i < unsigned(AttributeTag::User); ++i) { + auto tag = AttributeTag(i); + if(isDefaultValue(tag)) { + continue; + } + auto value = meta.getAttributePtr(tag); + if(value == nullptr) { + continue; + } + e.set(tag, value, getAttributeSize(tag)); + if(!callback(e)) { + return count; + } + } + + for(unsigned i = 0; i < SPIFFS_USER_METALEN;) { + uint8_t tagIndex = user[i++]; + uint8_t tagSize = user[i++]; + if(tagIndex == 0xff && tagSize == 0xff) { + break; + } + e.set(AttributeTag(unsigned(AttributeTag::User) + tagIndex), &user[i], tagSize); + if(!callback(e)) { + break; + } + i += tagSize; + } + + return count; +} + +int SpiffsMetaBuffer::getxattr(AttributeTag tag, void* buffer, size_t size) +{ + if(tag >= AttributeTag::User) { + return getUserAttribute(unsigned(tag) - unsigned(AttributeTag::User), buffer, size); + } + + auto value = meta.getAttributePtr(tag); + if(value == nullptr || isDefaultValue(tag)) { + return Error::NotFound; + } + + auto attrSize = getAttributeSize(tag); + memcpy(buffer, value, std::min(size, attrSize)); + return attrSize; +} + +int SpiffsMetaBuffer::setxattr(AttributeTag tag, const void* data, size_t size) +{ + if(tag >= AttributeTag::User) { + return setUserAttribute(unsigned(tag) - unsigned(AttributeTag::User), data, size); + } + + // Cannot delete standard attributes + if(data == nullptr) { + return Error::NotSupported; + } + auto value = meta.getAttributePtr(tag); + if(value == nullptr) { + return Error::NotSupported; + } + if(size != getAttributeSize(tag)) { + return Error::BadParam; + } + if(memcmp(value, data, size) == 0) { + // No change + return FS_OK; + } + memcpy(value, data, size); + flags += Flag::dirty; + return FS_OK; +} + +/* + * User tags are laid out in spare space as follows: + * + * uint8_t tag; + * uint8_t len; + * uint8_t data[]; + * + * Unused space is set to 0xFF. + */ +int SpiffsMetaBuffer::getUserAttribute(unsigned userTag, void* buffer, size_t size) +{ +#if SPIFFS_USER_METALEN + if(userTag > 255) { + return Error::BadParam; + } + + for(unsigned i = 0; i < SPIFFS_USER_METALEN;) { + uint8_t tagIndex = user[i++]; + uint8_t tagSize = user[i++]; + if(tagIndex != userTag) { + i += tagSize; + continue; + } + if(size > tagSize) { + size = tagSize; + } + if(buffer != nullptr) { + memcpy(buffer, &user[i], std::min(size_t(tagSize), size)); + } + return tagSize; + } +#else + (void)userTag; + (void)buffer; + (void)size; +#endif + + return Error::NotFound; +} + +int SpiffsMetaBuffer::setUserAttribute(unsigned userTag, const void* data, size_t size) +{ +#if SPIFFS_USER_METALEN + if(userTag > 255) { + return Error::BadParam; + } + + bool deleteFlag = (data == nullptr); + + for(unsigned i = 0; i < SPIFFS_USER_METALEN;) { + uint8_t tagIndex = user[i++]; + uint8_t tagSize = user[i++]; + if(tagIndex == userTag) { + // Found the tag - compare with new data + if(!deleteFlag && tagSize == size && memcmp(data, &user[i], tagSize) == 0) { + // No change + return FS_OK; + } + // Remove the tag + i -= 2; + tagSize += 2; + // Shift items above down + memmove(&user[i], &user[i + tagSize], SPIFFS_USER_METALEN - i - tagSize); + // Clear unused space to 0xFF + memset(&user[SPIFFS_USER_METALEN - tagSize], 0xff, tagSize); + flags += Flag::dirty; + + if(deleteFlag) { + return FS_OK; + } + continue; + } + + // End of list? + if(tagIndex == 0xff && tagSize == 0xff) { + // Room for new tag? + if(i + size > SPIFFS_USER_METALEN) { + break; + } + user[i - 2] = userTag; + user[i - 1] = size; + memcpy(&user[i], data, size); + flags += Flag::dirty; + return size; + } + + i += tagSize; + } +#else + (void)userTag; + (void)data; + (void)size; +#endif + + // No room for attribute + return Error::BufferTooSmall; +} + +} // namespace SPIFFS +} // namespace IFS diff --git a/Sming/Libraries/Spiffs/src/FileSystem.cpp b/Sming/Libraries/Spiffs/src/FileSystem.cpp index a698b4468a..710a3adab6 100644 --- a/Sming/Libraries/Spiffs/src/FileSystem.cpp +++ b/Sming/Libraries/Spiffs/src/FileSystem.cpp @@ -67,6 +67,8 @@ OpenFlags mapFileOpenFlags(OpenFlags flags, spiffs_flags& sflags) map(OpenFlag::Read, SPIFFS_O_RDONLY); map(OpenFlag::Write, SPIFFS_O_WRONLY); + flags -= OpenFlag::NoFollow; + if(flags.any()) { debug_w("Unknown OpenFlags: 0x%02X", flags.value()); } @@ -74,6 +76,14 @@ OpenFlags mapFileOpenFlags(OpenFlags flags, spiffs_flags& sflags) return flags; } +void fillStat(Stat& stat, const SpiffsMetaBuffer& smb) +{ + stat.acl = smb.meta.acl; + stat.attr = smb.meta.attr; + stat.mtime = smb.meta.mtime; + stat.compression = smb.meta.compression; +} + } // namespace s32_t FileSystem::f_read(struct spiffs_t* spiffs, u32_t addr, u32_t size, u8_t* dst) @@ -463,7 +473,8 @@ int FileSystem::stat(const char* path, Stat* stat) #else smb.init(); #endif - smb.copyTo(*stat); + fillStat(*stat, smb); + checkStat(*stat); } return FS_OK; @@ -492,7 +503,8 @@ int FileSystem::fstat(FileHandle file, Stat* stat) stat->name.copy(reinterpret_cast(ss.name)); stat->size = ss.size; stat->id = ss.obj_id; - smb->copyTo(*stat); + fillStat(*stat, *smb); + checkStat(*stat); } return FS_OK; @@ -520,6 +532,18 @@ int FileSystem::fgetxattr(FileHandle file, AttributeTag tag, void* buffer, size_ return smb->getxattr(tag, buffer, size); } +int FileSystem::fenumxattr(FileHandle file, AttributeEnumCallback callback, void* buffer, size_t bufsize) +{ + CHECK_MOUNTED() + + auto smb = getMetaBuffer(file); + if(smb == nullptr) { + return Error::InvalidHandle; + } + + return smb->enumxattr(callback, buffer, bufsize); +} + int FileSystem::setxattr(const char* path, AttributeTag tag, const void* data, size_t size) { #ifdef SPIFFS_STORE_META @@ -692,7 +716,7 @@ int FileSystem::readdir(DirHandle dir, Stat& stat) #else smb.init(); #endif - smb.copyTo(stat); + fillStat(stat, smb); } return FS_OK; diff --git a/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileMeta.h b/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileMeta.h index 4aaf74bc5e..5a4f846fd1 100644 --- a/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileMeta.h +++ b/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileMeta.h @@ -21,9 +21,7 @@ #pragma once -#include -#include -#include +#include namespace IFS { @@ -56,21 +54,7 @@ struct FileMeta { acl.writeAccess = UserRole::Admin; } - void* getAttributePtr(AttributeTag tag) - { - switch(tag) { - case AttributeTag::ModifiedTime: - return &mtime; - case AttributeTag::Acl: - return &acl; - case AttributeTag::Compression: - return &compression; - case AttributeTag::FileAttributes: - return &attr; - default: - return nullptr; - } - } + void* getAttributePtr(AttributeTag tag); }; #define FILEMETA_SIZE 16 @@ -110,14 +94,6 @@ struct SpiffsMetaBuffer { } } - void copyTo(Stat& stat) - { - stat.acl = meta.acl; - stat.attr = meta.attr; - stat.mtime = meta.mtime; - stat.compression = meta.compression; - } - void setFileTime(time_t t) { if(meta.mtime != t) { @@ -126,151 +102,23 @@ struct SpiffsMetaBuffer { } } - int getxattr(AttributeTag tag, void* buffer, size_t size) - { - if(tag >= AttributeTag::User) { - return getUserAttribute(unsigned(tag) - unsigned(AttributeTag::User), buffer, size); - } - - auto attrSize = getAttributeSize(tag); - if(attrSize == 0) { - return Error::BadParam; - } - if(size >= attrSize) { - auto value = meta.getAttributePtr(tag); - if(value != nullptr) { - memcpy(buffer, value, attrSize); - } - } - return attrSize; - } - - int setxattr(AttributeTag tag, const void* data, size_t size) - { - if(tag >= AttributeTag::User) { - return setUserAttribute(unsigned(tag) - unsigned(AttributeTag::User), data, size); - } - - // Cannot delete standard attributes - if(data == nullptr) { - return Error::NotSupported; - } - if(size != getAttributeSize(tag)) { - return Error::BadParam; - } - auto value = meta.getAttributePtr(tag); - if(value == nullptr) { - return Error::BadParam; - } - if(memcmp(value, data, size) == 0) { - // No change - return FS_OK; - } - memcpy(value, data, size); - if(tag == AttributeTag::Compression) { - meta.attr[FileAttribute::Compressed] = (meta.compression.type != Compression::Type::None); - } - flags += Flag::dirty; - return FS_OK; - } - - /* - * User tags are laid out in spare space as follows: - * - * uint8_t tag; - * uint8_t len; - * uint8_t data[]; - * - * Unused space is set to 0xFF. - */ - int getUserAttribute(unsigned userTag, void* buffer, size_t size) + bool isDefaultValue(AttributeTag tag) { -#if SPIFFS_USER_METALEN - if(userTag > 255) { - return Error::BadParam; - } - - for(unsigned i = 0; i < SPIFFS_USER_METALEN;) { - uint8_t tagIndex = user[i++]; - uint8_t tagSize = user[i++]; - if(tagIndex != userTag) { - i += tagSize; - continue; - } - if(size > tagSize) { - size = tagSize; - } - if(buffer != nullptr) { - memcpy(buffer, &user[i], std::min(size_t(tagSize), size)); - } - return tagSize; + switch(tag) { + case AttributeTag::FileAttributes: + return meta.attr.none(); + case AttributeTag::Compression: + return meta.compression.type == Compression::Type::None; + default: + return false; } -#else - (void)userTag; - (void)buffer; - (void)size; -#endif - - return Error::NotFound; } - int setUserAttribute(unsigned userTag, const void* data, size_t size) - { -#if SPIFFS_USER_METALEN - if(userTag > 255) { - return Error::BadParam; - } - - bool deleteFlag = (data == nullptr); - - for(unsigned i = 0; i < SPIFFS_USER_METALEN;) { - uint8_t tagIndex = user[i++]; - uint8_t tagSize = user[i++]; - if(tagIndex == userTag) { - // Found the tag - compare with new data - if(!deleteFlag && tagSize == size && memcmp(data, &user[i], tagSize) == 0) { - // No change - return FS_OK; - } - // Remove the tag - i -= 2; - tagSize += 2; - // Shift items above down - memmove(&user[i], &user[i + tagSize], SPIFFS_USER_METALEN - i - tagSize); - // Clear unused space to 0xFF - memset(&user[SPIFFS_USER_METALEN - tagSize], 0xff, tagSize); - flags += Flag::dirty; - - if(deleteFlag) { - return FS_OK; - } - continue; - } - - // End of list? - if(tagIndex == 0xff && tagSize == 0xff) { - // Room for new tag? - if(i + size > SPIFFS_USER_METALEN) { - break; - } - user[i - 2] = userTag; - user[i - 1] = size; - memcpy(&user[i], data, size); - flags += Flag::dirty; - return size; - } - - i += tagSize; - } -#else - (void)userTag; - (void)data; - (void)size; -#endif - - // No room for attribute - return Error::BufferTooSmall; - } + int enumxattr(AttributeEnumCallback callback, void* buffer, size_t bufsize); + int getxattr(AttributeTag tag, void* buffer, size_t size); + int setxattr(AttributeTag tag, const void* data, size_t size); + int getUserAttribute(unsigned userTag, void* buffer, size_t size); + int setUserAttribute(unsigned userTag, const void* data, size_t size); }; } // namespace SPIFFS diff --git a/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileSystem.h b/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileSystem.h index 0bb06246c2..be59da5e25 100644 --- a/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileSystem.h +++ b/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileSystem.h @@ -82,6 +82,7 @@ class FileSystem : public IFileSystem int fstat(FileHandle file, Stat* stat) override; int fsetxattr(FileHandle file, AttributeTag tag, const void* data, size_t size) override; int fgetxattr(FileHandle file, AttributeTag tag, void* buffer, size_t size) override; + int fenumxattr(FileHandle file, AttributeEnumCallback callback, void* buffer, size_t bufsize) override; int setxattr(const char* path, AttributeTag tag, const void* data, size_t size) override; int getxattr(const char* path, AttributeTag tag, void* buffer, size_t size) override; FileHandle open(const char* path, OpenFlags flags) override; diff --git a/samples/Basic_IFS/app/application.cpp b/samples/Basic_IFS/app/application.cpp index e1a9223c1c..a68d019ac7 100644 --- a/samples/Basic_IFS/app/application.cpp +++ b/samples/Basic_IFS/app/application.cpp @@ -241,18 +241,17 @@ void copySomeFiles() src.readContent([&dst](const char* buffer, size_t size) -> int { return dst.write(buffer, size); }); (void)len; debug_w("Wrote '%s', %d bytes", filename, len); - if(!dst.settime(stat.mtime)) { - Serial.print(F("settime() failed: ")); - Serial.println(dst.getLastErrorString()); - } - if(!dst.setcompression(stat.compression)) { - Serial.print(F("setcompression() failed: ")); - Serial.println(dst.getLastErrorString()); - } - if(!dst.setacl(stat.acl)) { - Serial.print(F("setacl() failed: ")); - Serial.println(dst.getLastErrorString()); - } + + // Copy metadata + auto callback = [&](IFS::AttributeEnum& e) -> bool { + if(!dst.setAttribute(e.tag, e.buffer, e.size)) { + m_printf(_F("setAttribute(%s) failed: %s"), toString(e.tag).c_str(), + dst.getLastErrorString().c_str()); + } + return true; + }; + char buffer[1024]; + src.enumAttributes(callback, buffer, sizeof(buffer)); } else { debug_w("%s", dst.getLastErrorString().c_str()); } @@ -267,6 +266,29 @@ bool isVolumeEmpty() return !dir.next(); } +void listAttributes() +{ + Directory dir; + if(dir.open()) { + while(dir.next()) { + auto filename = dir.stat().name.c_str(); + File f; + if(!f.open(filename)) { + continue; + } + m_printf("%s:\r\n", filename); + auto callback = [](IFS::AttributeEnum& e) -> bool { + m_printf(" attr 0x%04x %s, %u bytes\r\n", unsigned(e.tag), toString(e.tag).c_str(), e.attrsize); + m_printHex(" ATTR", e.buffer, e.size); + return true; + }; + char buffer[64]; + int res = f.enumAttributes(callback, buffer, sizeof(buffer)); + debug_i("res: %d", res); + } + } +} + void fstest() { // Various ways to initialise a filesystem @@ -302,6 +324,8 @@ void fstest() } printDirectory(nullptr); + + listAttributes(); } } // namespace From 9ec2f9dd2e8af34c159b1fd8f5d09fe7f5a6e4e6 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 29 Apr 2021 21:07:21 +0100 Subject: [PATCH 018/130] FWFS stores objectIDs directly (breaking change) (#2321) This PR simplifies FWFS further by storing object identifiers as image offsets. Advantages: - No cache required - Faster because we can jump directly to the referenced record - Debugging and introspection is much easier The references are stored as packed integers (1 - 4 bytes). Note: Existing images need to be rebuilt. This change should have been included in #2315 as the use of numeric object IDs was part of the abstraction for object stores. --- Sming/Components/IFS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sming/Components/IFS b/Sming/Components/IFS index b59301c5aa..86e39728b5 160000 --- a/Sming/Components/IFS +++ b/Sming/Components/IFS @@ -1 +1 @@ -Subproject commit b59301c5aafae4d3846525bb0d24192b6d3b7fd4 +Subproject commit 86e39728b5a81cf0147b9f8dd3b67901c85a2a4c From fcb91862d5155c7ffc6544e418245153718ffae9 Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 30 Apr 2021 09:09:21 +0100 Subject: [PATCH 019/130] Add streaming filesystem backup support (#2323) This PR adds the `ArchiveStream` class to support backing up filesystems. The archive files are in FWFS format. - Add FWFS for user attribute support User attributes can be set (via callback) during the backup operation and read backup using the standard IFS API. - Add Encrypted file attribute Files may be encrypted via `IBlockEncoder` plugin, which would set this attribute. - Add `Comment` standard attribute Standard comment of up to 255 characters (FWFS and LittleFS). - Add check in fsbuild for duplicate directories/files, and allow paths to be specified in source Tests have been updated accordingly. --- Sming/Components/IFS | 2 +- Sming/Core/Data/Stream/IFS/ArchiveStream.h | 28 ++++++++++ Sming/Core/Data/Stream/MemoryDataStream.h | 10 ++++ Sming/Libraries/LittleFS | 2 +- samples/Basic_IFS/README.rst | 9 +++- samples/Basic_IFS/app/application.cpp | 59 +++++++++++++++------- 6 files changed, 89 insertions(+), 21 deletions(-) create mode 100644 Sming/Core/Data/Stream/IFS/ArchiveStream.h diff --git a/Sming/Components/IFS b/Sming/Components/IFS index 86e39728b5..8792db1144 160000 --- a/Sming/Components/IFS +++ b/Sming/Components/IFS @@ -1 +1 @@ -Subproject commit 86e39728b5a81cf0147b9f8dd3b67901c85a2a4c +Subproject commit 8792db1144588a4732e44a561810730a37b8a48a diff --git a/Sming/Core/Data/Stream/IFS/ArchiveStream.h b/Sming/Core/Data/Stream/IFS/ArchiveStream.h new file mode 100644 index 0000000000..02115b1ff1 --- /dev/null +++ b/Sming/Core/Data/Stream/IFS/ArchiveStream.h @@ -0,0 +1,28 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/anakod/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * ArchiveStream.h + * + * @author mikee47 April 2021 + * + * + ****/ + +#pragma once + +#include +#include + +class ArchiveStream : public IFS::FWFS::ArchiveStream +{ +public: + using IFS::FWFS::ArchiveStream::ArchiveStream; + + ArchiveStream(VolumeInfo volumeInfo, String rootPath = nullptr, Flags flags = 0) + : ArchiveStream(::getFileSystem(), volumeInfo, rootPath, flags) + { + } +}; diff --git a/Sming/Core/Data/Stream/MemoryDataStream.h b/Sming/Core/Data/Stream/MemoryDataStream.h index 75a1f06607..3e03f8821b 100644 --- a/Sming/Core/Data/Stream/MemoryDataStream.h +++ b/Sming/Core/Data/Stream/MemoryDataStream.h @@ -104,6 +104,16 @@ class MemoryDataStream : public ReadWriteStream readPos = 0; } + size_t getSize() const + { + return size; + } + + size_t getCapacity() const + { + return capacity; + } + private: char* buffer = nullptr; ///< Stream content stored here size_t maxCapacity{UINT16_MAX}; ///< Limit size of stream diff --git a/Sming/Libraries/LittleFS b/Sming/Libraries/LittleFS index 0514211e5f..21f3969a64 160000 --- a/Sming/Libraries/LittleFS +++ b/Sming/Libraries/LittleFS @@ -1 +1 @@ -Subproject commit 0514211e5f30b12d2f69ab4b61e8a59b9dc2f963 +Subproject commit 21f3969a6407771d8823e4eac3a3b266d1772ba6 diff --git a/samples/Basic_IFS/README.rst b/samples/Basic_IFS/README.rst index 70867537f0..ec0ddd2b0d 100644 --- a/samples/Basic_IFS/README.rst +++ b/samples/Basic_IFS/README.rst @@ -7,7 +7,14 @@ Simple Webserver demonstration using IFS. View the filesystem using a web browser. -To see the data in a different format, add ``?format=XX``. Current supported formats are ``json``, ``text`` and ``html``. +To see directory content in a different format, append ``?format=XX``, one of: + +- ``json`` +- ``text`` +- ``html`` + +Use the format ``archive`` to retrieve an archive/backup of the directory tree as an FWFS image. + Building -------- diff --git a/samples/Basic_IFS/app/application.cpp b/samples/Basic_IFS/app/application.cpp index a68d019ac7..6c60f80184 100644 --- a/samples/Basic_IFS/app/application.cpp +++ b/samples/Basic_IFS/app/application.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -52,11 +53,25 @@ void onFile(HttpRequest& request, HttpResponse& response) ++requestCount; String file = request.uri.getRelativePath(); + String fmt = request.uri.Query["format"]; if(dirExist(file)) { + if(fmt.equalsIgnoreCase("archive")) { + debug_i("Sending streaming archive"); + IFS::FileSystem::NameInfo fsinfo; + fileGetSystemInfo(fsinfo); + ArchiveStream::VolumeInfo volumeInfo; + volumeInfo.name = F("Backup of '") + fsinfo.name + "'"; + if(file.length() != 0) { + volumeInfo.name += F("; root = '") + file + "'"; + } + auto archive = new ArchiveStream(volumeInfo, file, ArchiveStream::Flag::IncludeMountPoints); + response.sendDataStream(archive, archive->getMimeType()); + return; + } + auto dir = new Directory; IFS::DirectoryTemplate* tmpl; - String fmt = request.uri.Query["format"]; if(fmt.equalsIgnoreCase("json")) { auto source = new FlashMemoryStream(listing_json); tmpl = new IFS::JsonDirectoryTemplate(source, dir); @@ -71,24 +86,32 @@ void onFile(HttpRequest& request, HttpResponse& response) dir->open(file); tmpl->gotoSection(0); response.sendDataStream(tmpl, tmpl->getMimeType()); - } else { - // response.setCache(86400, true); // It's important to use cache for better performance. - auto stream = new FileStream; - if(!stream->open(file)) { - response.code = HTTP_STATUS_INTERNAL_SERVER_ERROR; - delete stream; - return; - } - FileStat stat; - stream->stat(stat); - if(stat.compression.type == IFS::Compression::Type::GZip) { - response.headers[HTTP_HEADER_CONTENT_ENCODING] = F("gzip"); - } else if(stat.compression.type != IFS::Compression::Type::None) { - debug_e("Unsupported compression type: %u", stat.compression.type); - } - auto mimeType = ContentType::fromFullFileName(file, MIME_TEXT); - response.sendDataStream(stream, mimeType); + return; + } + + if(fmt) { + debug_e("'format' option only supported for directories"); + response.code = HTTP_STATUS_BAD_REQUEST; + return; + } + + // response.setCache(86400, true); // It's important to use cache for better performance. + auto stream = new FileStream; + if(!stream->open(file)) { + int err = stream->getLastError(); + response.code = (err == IFS::Error::NotFound) ? HTTP_STATUS_NOT_FOUND : HTTP_STATUS_INTERNAL_SERVER_ERROR; + delete stream; + return; + } + FileStat stat; + stream->stat(stat); + if(stat.compression.type == IFS::Compression::Type::GZip) { + response.headers[HTTP_HEADER_CONTENT_ENCODING] = F("gzip"); + } else if(stat.compression.type != IFS::Compression::Type::None) { + debug_e("Unsupported compression type: %u", stat.compression.type); } + auto mimeType = ContentType::fromFullFileName(file, MIME_TEXT); + response.sendDataStream(stream, mimeType); } void startWebServer() From b879d295c9563ff2443ab9e21f333bf7cdf66194 Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 1 May 2021 18:54:14 +0100 Subject: [PATCH 020/130] Fix Basic_IFS sample and TemplateStream (#2324) * Use per-arch hardware configs, and reduce ESP32 app. partition size * Fix bug in ReadWriteStream::copyFrom() * Fix TemplateStream Tested against large (1MB) JSON listing generated from templates, issues arising. Need to check for remaining plain data before continuing with next tag search Move check for missing end tag after `evaluate` * Add test for fragmented read of variable values Verified that debug statements #1, #3 and #4 are triggered by the test. Use smaller read buffers for testing to stress code more --- Sming/Core/Data/Stream/ReadWriteStream.cpp | 4 +- Sming/Core/Data/Stream/TemplateStream.cpp | 41 ++++++++++------- samples/Basic_IFS/basic_ifs_Esp32.hw | 9 ++++ samples/Basic_IFS/basic_ifs_Esp8266.hw | 9 ++++ samples/Basic_IFS/basic_ifs_Host.hw | 9 ++++ samples/Basic_IFS/component.mk | 2 +- tests/HostTests/modules/TemplateStream.cpp | 53 +++++++++++++++++++++- 7 files changed, 106 insertions(+), 21 deletions(-) create mode 100644 samples/Basic_IFS/basic_ifs_Esp32.hw create mode 100644 samples/Basic_IFS/basic_ifs_Esp8266.hw create mode 100644 samples/Basic_IFS/basic_ifs_Host.hw diff --git a/Sming/Core/Data/Stream/ReadWriteStream.cpp b/Sming/Core/Data/Stream/ReadWriteStream.cpp index 6c26e94bc9..af972a11a8 100644 --- a/Sming/Core/Data/Stream/ReadWriteStream.cpp +++ b/Sming/Core/Data/Stream/ReadWriteStream.cpp @@ -23,13 +23,13 @@ size_t ReadWriteStream::copyFrom(IDataSourceStream* source, size_t size) size_t total = 0; size_t count; while(!source->isFinished()) { - count = source->readMemoryBlock(buffer, sizeof(buffer)); + count = source->readMemoryBlock(buffer, bufSize); if(count == 0) { continue; } auto written = write(reinterpret_cast(buffer), count); source->seek(written); - total += count; + total += written; if(written != count) { break; } diff --git a/Sming/Core/Data/Stream/TemplateStream.cpp b/Sming/Core/Data/Stream/TemplateStream.cpp index 97de0177b9..a3fe30d7a1 100644 --- a/Sming/Core/Data/Stream/TemplateStream.cpp +++ b/Sming/Core/Data/Stream/TemplateStream.cpp @@ -14,6 +14,7 @@ String TemplateStream::evaluate(char*& expr) { auto end = strchr(expr, '}'); if(end == nullptr) { + expr += strlen(expr); return nullptr; } @@ -30,7 +31,7 @@ String TemplateStream::getValue(const char* name) if(!s && getValueCallback) { s = getValueCallback(name); } - debug_d("TemplateStream: value '%s' %sfound: \"%s\"", name, s ? "" : "NOT ", s.c_str()); + debug_d("[TMPL] value '%s' %sfound: \"%s\"", name, s ? "" : "NOT ", s.c_str()); return s; } @@ -52,9 +53,16 @@ uint16_t TemplateStream::readMemoryBlock(char* data, int bufSize) return sendValue(); } + if(valueWaitSize != 0) { + debug_d("[TMPL] #1"); + size_t res = std::min(uint16_t(bufSize), valueWaitSize); + return stream->readMemoryBlock(data, res); + } + const size_t tagDelimiterLength = 1 + doubleBraces; if(size_t(bufSize) <= TEMPLATE_MAX_VAR_NAME_LEN + (2 * tagDelimiterLength)) { + debug_d("[TMPL] #2"); return 0; } @@ -68,12 +76,23 @@ uint16_t TemplateStream::readMemoryBlock(char* data, int bufSize) if(datalen != 0) { data[datalen] = '\0'; // Terminate buffer to mitigate overflow risk auto tagStart = findStartTag(data); - auto lastTagFound = tagStart; while(tagStart != nullptr) { - lastTagFound = tagStart; - char* curPos = tagStart + tagDelimiterLength; value = evaluate(curPos); + size_t tailpos = curPos - data; + if(tailpos >= datalen) { + debug_d("[TMPL #3]"); + // Incomplete variable name, end tag not found in buffer + unsigned newlen = tagStart - data; + if(newlen + TEMPLATE_MAX_VAR_NAME_LEN > datalen) { + // Return what we have so far, unless we're at the end of the input stream + if(datalen == size_t(bufSize - 1)) { + debug_d("[TMPL #4]"); + datalen = newlen; + break; + } + } + } if(doubleBraces) { // Double end brace isn't necessary, but if present skip it if(*curPos == '}') { @@ -113,18 +132,6 @@ uint16_t TemplateStream::readMemoryBlock(char* data, int bufSize) memmove(data, start, valueWaitSize); return valueWaitSize; } - - if(lastTagFound != nullptr) { - unsigned newlen = lastTagFound - data; - if(newlen + TEMPLATE_MAX_VAR_NAME_LEN > datalen) { - debug_d("TemplateStream: trim end to %u from %u", newlen, datalen); - // It can be a incomplete variable name - don't split it - // provided we're not at end of input stream - if(datalen == size_t(bufSize)) { - datalen = newlen; - } - } - } } datalen -= (start - data); @@ -167,7 +174,7 @@ int TemplateStream::seekFrom(int offset, SeekOrigin origin) if(valueWaitSize != 0) { if(size_t(offset) > valueWaitSize) { - debug_e("TemplateStream: offset > valueWaitSize"); + debug_e("[TMPL] offset > valueWaitSize"); return -1; } valueWaitSize -= offset; diff --git a/samples/Basic_IFS/basic_ifs_Esp32.hw b/samples/Basic_IFS/basic_ifs_Esp32.hw new file mode 100644 index 0000000000..e5a80b47b0 --- /dev/null +++ b/samples/Basic_IFS/basic_ifs_Esp32.hw @@ -0,0 +1,9 @@ +{ + "base_config": "basic_ifs", + "arch": "Esp32", + "partitions": { + "rom0": { + "size": "960K" + } + } +} \ No newline at end of file diff --git a/samples/Basic_IFS/basic_ifs_Esp8266.hw b/samples/Basic_IFS/basic_ifs_Esp8266.hw new file mode 100644 index 0000000000..a174c30a45 --- /dev/null +++ b/samples/Basic_IFS/basic_ifs_Esp8266.hw @@ -0,0 +1,9 @@ +{ + "base_config": "basic_ifs", + "arch": "Esp8266", + "partitions": { + "rom0": { + "size": "480K" + } + } +} \ No newline at end of file diff --git a/samples/Basic_IFS/basic_ifs_Host.hw b/samples/Basic_IFS/basic_ifs_Host.hw new file mode 100644 index 0000000000..7b6468d01a --- /dev/null +++ b/samples/Basic_IFS/basic_ifs_Host.hw @@ -0,0 +1,9 @@ +{ + "base_config": "basic_ifs", + "arch": "Host", + "partitions": { + "rom0": { + "size": "480K" + } + } +} \ No newline at end of file diff --git a/samples/Basic_IFS/component.mk b/samples/Basic_IFS/component.mk index 0b43df10ed..feec6c667e 100644 --- a/samples/Basic_IFS/component.mk +++ b/samples/Basic_IFS/component.mk @@ -10,5 +10,5 @@ ifeq ($(ENABLE_FLASHSTRING_IMAGE),1) COMPONENT_CXXFLAGS += -DENABLE_FLASHSTRING_IMAGE=1 HWCONFIG := spiffs else -HWCONFIG := basic_ifs +HWCONFIG := basic_ifs_$(SMING_ARCH) endif diff --git a/tests/HostTests/modules/TemplateStream.cpp b/tests/HostTests/modules/TemplateStream.cpp index 1805d54c02..f98d145ccc 100644 --- a/tests/HostTests/modules/TemplateStream.cpp +++ b/tests/HostTests/modules/TemplateStream.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #ifdef ARCH_HOST @@ -112,12 +113,62 @@ class TemplateStreamTest : public TestGroup check(tmpl, Resource::ut_template1_out1_rst); } + + auto addChar = [](String& s, char c, size_t count) { + auto len = s.length(); + s.setLength(len + count); + memset(&s[len], c, count); + }; + + TEST_CASE("Fragmented read of variable [TMPL #1, #3, #4]") + { + constexpr size_t TEMPLATE_BUFFER_SIZE{100}; + String input; + addChar(input, 'a', TEMPLATE_BUFFER_SIZE - 4); + input += _F("{varname}"); + addChar(input, 'a', TEMPLATE_BUFFER_SIZE); + auto source = new LimitedMemoryStream(input.begin(), input.length(), input.length(), false); + TemplateStream tmpl(source); + PSTR_ARRAY(someValue, "Some value or other"); + tmpl.setVar(F("varname"), someValue); + + size_t outlen{0}; + char output[TEMPLATE_BUFFER_SIZE * 3]{}; + while(!tmpl.isFinished()) { + auto ptr = output + outlen; + size_t read1 = tmpl.readMemoryBlock(ptr, TEMPLATE_BUFFER_SIZE); + char tmp[read1]; + memcpy(tmp, ptr, read1); + size_t read = tmpl.readMemoryBlock(ptr, TEMPLATE_BUFFER_SIZE); + CHECK_EQ(read, read1); + CHECK(memcmp(tmp, ptr, read) == 0); + if(read > 10) { + read -= 5; + } + tmpl.seek(read); + outlen += read; + ptr += read; + } + + String expected; + addChar(expected, 'a', TEMPLATE_BUFFER_SIZE - 4); + expected += someValue; + addChar(expected, 'a', TEMPLATE_BUFFER_SIZE); + + if(!expected.equals(output, outlen)) { + m_nputs(output, outlen); + m_puts("\r\n"); + m_nputs(expected.c_str(), expected.length()); + m_puts("\r\n"); + } + REQUIRE(expected.equals(output, outlen)); + } } private: void check(TemplateStream& tmpl, const FlashString& ref) { - static constexpr size_t bufSize{512}; + constexpr size_t bufSize{256}; char buf1[bufSize]; char buf2[bufSize]; FSTR::Stream refStream(ref); From 10a25d62fb5d92ff594b139bf12a2242709339f9 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 3 May 2021 17:08:32 +0100 Subject: [PATCH 021/130] Fixes to Travis and AppVeyor CI systems (#2322) Co-authored-by: Slavey Karadzhov --- .travis.yml | 9 +-------- Tools/install.sh | 8 ++++---- Tools/travis/install.sh | 5 +++++ 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4b5249e996..b470c2bb35 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: python -python: 3.8.6 +python: 3.9 os: linux dist: focal env: @@ -16,8 +16,6 @@ jobs: apt: packages: - clang-format-8 - - linux-libc-dev:i386 - - g++-9-multilib - doxygen - python3-sphinx - python3-pip @@ -30,11 +28,6 @@ jobs: - SMING_ARCH=Host - stage: build name: C++17 - addons: - apt: - packages: - - linux-libc-dev:i386 - - g++-9-multilib env: - SMING_ARCH=Esp8266 - SDK_VERSION=3.0.1 diff --git a/Tools/install.sh b/Tools/install.sh index d3d096f282..562035069a 100755 --- a/Tools/install.sh +++ b/Tools/install.sh @@ -1,12 +1,10 @@ -#!/bin/bash -e +#!/bin/bash # # This file should be sourced after pulling in Sming repo, i.e: # # . /opt/sming/Tools/install.sh # -set -e - [ "$0" = "$BASH_SOURCE" ]; sourced=$? inst_host=0 @@ -103,7 +101,7 @@ else case $DIST in debian) - sudo apt-get -y update + sudo apt-get -y update || echo "Update failed... Try to install anyway..." $PKG_INSTALL \ clang-format-8 \ cmake \ @@ -143,6 +141,8 @@ else fi +set -e + sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-8 100 python3 -m pip install --upgrade pip -r $SMING_HOME/../Tools/requirements.txt diff --git a/Tools/travis/install.sh b/Tools/travis/install.sh index 1c3a0f9308..099ee34e94 100755 --- a/Tools/travis/install.sh +++ b/Tools/travis/install.sh @@ -1,6 +1,11 @@ #!/bin/bash set -ex # exit with nonzero exit code if anything fails +sudo dpkg --add-architecture i386 +sudo apt update +sudo apt install software-properties-common +sudo aptitude install -y libc6-dev=2.31-0ubuntu9.2 libc6=2.31-0ubuntu9.2 g++-9-multilib linux-libc-dev:i386 + if [ -f "$TRAVIS_BUILD_DIR/Sming/Arch/$SMING_ARCH/Tools/travis/install.sh" ]; then source "$TRAVIS_BUILD_DIR/Sming/Arch/$SMING_ARCH/Tools/travis/install.sh" fi From dc0747c4ebe1a4e54c6541e9384268af29913aad Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 5 May 2021 07:57:22 +0100 Subject: [PATCH 022/130] ESP32 network fixes (#2325) Don't start webserver until onReady Don't use ESP_ERROR_CHECK so much... Fix Ssl::Options::toString() Change Timer to SimpleTimer for HttpClient. Uses a little less RAM. Increase ESP32 timer task stack size Bugchecks during HostTests. --- .../esp32/project/sdkconfig.defaults.debug | 2 +- .../esp32/project/sdkconfig.defaults.release | 2 +- .../Arch/Esp32/Platform/AccessPointImpl.cpp | 29 +++++++++++++------ .../Arch/Esp32/Platform/StationImpl.cpp | 21 ++++++++++---- .../Network/src/Network/HttpClient.cpp | 4 +-- .../Network/src/Network/HttpClient.h | 4 +-- Sming/Components/ssl/src/Session.cpp | 2 +- samples/Basic_IFS/basic_ifs_Esp32.hw | 2 +- .../Basic_WebSkeletonApp/app/application.cpp | 2 +- .../app/application.cpp | 2 +- 10 files changed, 45 insertions(+), 25 deletions(-) diff --git a/Sming/Arch/Esp32/Components/esp32/project/sdkconfig.defaults.debug b/Sming/Arch/Esp32/Components/esp32/project/sdkconfig.defaults.debug index 758d125f83..cb3c9cab5c 100644 --- a/Sming/Arch/Esp32/Components/esp32/project/sdkconfig.defaults.debug +++ b/Sming/Arch/Esp32/Components/esp32/project/sdkconfig.defaults.debug @@ -16,7 +16,7 @@ CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 # Mandatory Sming framework changes -CONFIG_ESP_TIMER_TASK_STACK_SIZE=8192 +CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 # The bootloader logs all type of messages diff --git a/Sming/Arch/Esp32/Components/esp32/project/sdkconfig.defaults.release b/Sming/Arch/Esp32/Components/esp32/project/sdkconfig.defaults.release index d1c7ab696f..255cb2c690 100644 --- a/Sming/Arch/Esp32/Components/esp32/project/sdkconfig.defaults.release +++ b/Sming/Arch/Esp32/Components/esp32/project/sdkconfig.defaults.release @@ -16,7 +16,7 @@ CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 # Mandatory Sming framework changes -CONFIG_ESP_TIMER_TASK_STACK_SIZE=8192 +CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 # The bootloader logs only errors diff --git a/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.cpp b/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.cpp index 33fe58ff40..a6cda7928a 100644 --- a/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.cpp +++ b/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.cpp @@ -95,31 +95,42 @@ bool AccessPointImpl::config(const String& ssid, String password, WifiAuthMode m IpAddress AccessPointImpl::getIP() const { + IpAddress addr; esp_netif_ip_info_t info; - ESP_ERROR_CHECK(esp_netif_get_ip_info(apNetworkInterface, &info)); - return info.ip.addr; + if(esp_netif_get_ip_info(apNetworkInterface, &info) == ESP_OK) { + addr = info.ip.addr; + } + return addr; } IpAddress AccessPointImpl::getNetworkBroadcast() const { + IpAddress addr; esp_netif_ip_info_t info; - ESP_ERROR_CHECK(esp_netif_get_ip_info(apNetworkInterface, &info)); - return info.ip.addr | ~info.netmask.addr; + if(esp_netif_get_ip_info(apNetworkInterface, &info) == ESP_OK) { + addr = info.ip.addr | ~info.netmask.addr; + } + return addr; } IpAddress AccessPointImpl::getNetworkMask() const { + IpAddress addr; esp_netif_ip_info_t info; - ESP_ERROR_CHECK(esp_netif_get_ip_info(apNetworkInterface, &info)); - return info.netmask.addr; + if(esp_netif_get_ip_info(apNetworkInterface, &info) == ESP_OK) { + addr = info.netmask.addr; + } + return addr; } IpAddress AccessPointImpl::getNetworkGateway() const { + IpAddress addr; esp_netif_ip_info_t info; - ESP_ERROR_CHECK(esp_netif_get_ip_info(apNetworkInterface, &info)); - - return info.gw.addr; + if(esp_netif_get_ip_info(apNetworkInterface, &info) == ESP_OK) { + addr = info.gw.addr; + } + return addr; } bool AccessPointImpl::setIP(IpAddress address) diff --git a/Sming/Components/Network/Arch/Esp32/Platform/StationImpl.cpp b/Sming/Components/Network/Arch/Esp32/Platform/StationImpl.cpp index 6780762c58..fed72f220d 100644 --- a/Sming/Components/Network/Arch/Esp32/Platform/StationImpl.cpp +++ b/Sming/Components/Network/Arch/Esp32/Platform/StationImpl.cpp @@ -193,23 +193,32 @@ bool StationImpl::setMacAddress(const MacAddress& addr) const IpAddress StationImpl::getNetworkBroadcast() const { + IpAddress addr; esp_netif_ip_info_t info; - ESP_ERROR_CHECK(esp_netif_get_ip_info(stationNetworkInterface, &info)); - return info.ip.addr | ~info.netmask.addr; + if(esp_netif_get_ip_info(stationNetworkInterface, &info) == ESP_OK) { + addr = info.ip.addr | ~info.netmask.addr; + } + return addr; } IpAddress StationImpl::getNetworkMask() const { + IpAddress addr; esp_netif_ip_info_t info; - ESP_ERROR_CHECK(esp_netif_get_ip_info(stationNetworkInterface, &info)); - return info.netmask.addr; + if(esp_netif_get_ip_info(stationNetworkInterface, &info) == ESP_OK) { + addr = info.netmask.addr; + } + return addr; } IpAddress StationImpl::getNetworkGateway() const { + IpAddress addr; esp_netif_ip_info_t info; - ESP_ERROR_CHECK(esp_netif_get_ip_info(stationNetworkInterface, &info)); - return info.gw.addr; + if(esp_netif_get_ip_info(stationNetworkInterface, &info) == ESP_OK) { + addr = info.gw.addr; + } + return addr; } bool StationImpl::setIP(IpAddress address, IpAddress netmask, IpAddress gateway) diff --git a/Sming/Components/Network/src/Network/HttpClient.cpp b/Sming/Components/Network/src/Network/HttpClient.cpp index 723788e3fe..0d54aeff5f 100644 --- a/Sming/Components/Network/src/Network/HttpClient.cpp +++ b/Sming/Components/Network/src/Network/HttpClient.cpp @@ -14,7 +14,7 @@ #include "Data/Stream/FileStream.h" HttpClient::HttpConnectionPool HttpClient::httpConnectionPool; -Timer HttpClient::cleanUpTimer; +SimpleTimer HttpClient::cleanUpTimer; bool HttpClient::send(HttpRequest* request) { @@ -41,7 +41,7 @@ bool HttpClient::send(HttpRequest* request) } if(!cleanUpTimer.isStarted()) { - cleanUpTimer.initializeMs<60000>(&HttpClient::cleanInactive).start(); // run every minute + cleanUpTimer.initializeMs<60000>(HttpClient::cleanInactive).start(); // run every minute } return connection->send(request); } diff --git a/Sming/Components/Network/src/Network/HttpClient.h b/Sming/Components/Network/src/Network/HttpClient.h index e2d332e1a1..80a40fa6f8 100644 --- a/Sming/Components/Network/src/Network/HttpClient.h +++ b/Sming/Components/Network/src/Network/HttpClient.h @@ -23,7 +23,7 @@ #include "Http/HttpRequest.h" #include "Http/HttpClientConnection.h" #include "Data/Stream/LimitedMemoryStream.h" -#include +#include class HttpClient { @@ -145,7 +145,7 @@ class HttpClient static HttpConnectionPool httpConnectionPool; private: - static Timer cleanUpTimer; + static SimpleTimer cleanUpTimer; static void cleanInactive(); }; diff --git a/Sming/Components/ssl/src/Session.cpp b/Sming/Components/ssl/src/Session.cpp index 208fee87c0..6f0143a5de 100644 --- a/Sming/Components/ssl/src/Session.cpp +++ b/Sming/Components/ssl/src/Session.cpp @@ -24,8 +24,8 @@ String Options::toString() const if(field) { \ if(s) { \ s += ", "; \ - s += _F(#field); \ } \ + s += _F(#field); \ } ADD(sessionResume); diff --git a/samples/Basic_IFS/basic_ifs_Esp32.hw b/samples/Basic_IFS/basic_ifs_Esp32.hw index e5a80b47b0..3f3f6e9c17 100644 --- a/samples/Basic_IFS/basic_ifs_Esp32.hw +++ b/samples/Basic_IFS/basic_ifs_Esp32.hw @@ -2,7 +2,7 @@ "base_config": "basic_ifs", "arch": "Esp32", "partitions": { - "rom0": { + "factory": { "size": "960K" } } diff --git a/samples/Basic_WebSkeletonApp/app/application.cpp b/samples/Basic_WebSkeletonApp/app/application.cpp index 187bd579bf..72112247aa 100644 --- a/samples/Basic_WebSkeletonApp/app/application.cpp +++ b/samples/Basic_WebSkeletonApp/app/application.cpp @@ -56,7 +56,7 @@ void init() WifiEvents.onStationDisconnect(STADisconnect); WifiEvents.onStationGotIP(STAGotIP); - startWebServer(); + System.onReady(startWebServer); counterTimer.initializeMs(1000, counterLoop).start(); } diff --git a/samples/Basic_WebSkeletonApp_LTS/app/application.cpp b/samples/Basic_WebSkeletonApp_LTS/app/application.cpp index edd109110b..7c7d00e2b8 100644 --- a/samples/Basic_WebSkeletonApp_LTS/app/application.cpp +++ b/samples/Basic_WebSkeletonApp_LTS/app/application.cpp @@ -24,7 +24,7 @@ void init() WifiEvents.onStationDisconnect(STADisconnect); WifiEvents.onStationGotIP(STAGotIP); - startWebServer(); + System.onReady(startWebServer); counterTimer.initializeMs(1000, counterLoop).start(); } From 1123cdf23ff0d53c4a935f17d020eb8c1effbca0 Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 5 May 2021 13:25:16 +0100 Subject: [PATCH 023/130] Enable Host access to physical COM ports (#2326) This PR extends the Host serial port emulation to include local physical devices using the `--device` command-line option. For example: ``` make run HOST_UART_OPTIONS="--uart=0 --device=/dev/ttyUSB0" ``` The `--device` option must follow the `--uart` option. Another example: ``` make run HOST_UART_OPTIONS="--uart=0 --device=/dev/ttyUSB0 --uart=1 --device=/dev/ttyUSB1" ``` The port is opened when `uart_init` gets called. The default baud rate is whatever the application has requested. This can be overridden as follows: ``` make run HOST_UART_OPTIONS="--uart=0 --device=/dev/ttyUSB0 --baud=921600" ``` --- .gitmodules | 8 +- .../Host/Components/SerialLib/SerialLib.h | 7 + .../Host/Components/SerialLib/component.mk | 4 + .../Host/Components/SerialLib}/seriallib | 0 .../Host/Components/SerialLib/seriallib.patch | 22 ++ .../Arch/Host/Components/driver/component.mk | 4 +- Sming/Arch/Host/Components/driver/uart.rst | 32 ++- .../Host/Components/driver/uart_server.cpp | 196 +++++++++++++++--- .../Arch/Host/Components/driver/uart_server.h | 113 +++++++--- Sming/Arch/Host/Components/hostlib/options.h | 4 + .../Arch/Host/Components/hostlib/startup.cpp | 27 ++- Sming/Components/Hosted/component.mk | 2 +- .../Components/Hosted/include/Hosted/Serial.h | 2 +- Sming/Libraries/seriallib/component.mk | 6 - 14 files changed, 346 insertions(+), 81 deletions(-) create mode 100644 Sming/Arch/Host/Components/SerialLib/SerialLib.h create mode 100644 Sming/Arch/Host/Components/SerialLib/component.mk rename Sming/{Libraries/seriallib => Arch/Host/Components/SerialLib}/seriallib (100%) create mode 100644 Sming/Arch/Host/Components/SerialLib/seriallib.patch delete mode 100644 Sming/Libraries/seriallib/component.mk diff --git a/.gitmodules b/.gitmodules index 6432564096..24fe182d73 100644 --- a/.gitmodules +++ b/.gitmodules @@ -120,6 +120,10 @@ path = Sming/Arch/Host/Components/lwip/lwip url = https://github.com/lwip-tcpip/lwip ignore = dirty +[submodule "seriallib"] + path = Sming/Arch/Host/Components/SerialLib/seriallib + url = https://github.com/imabot2/serialib.git + ignore = dirty @@ -255,10 +259,6 @@ path = Sming/Libraries/RingTone url = https://github.com/mikee47/RingTone ignore = dirty -[submodule "Libraries.seriallib"] - path = Sming/Libraries/seriallib/seriallib - url = https://github.com/imabot2/serialib.git - ignore = dirty [submodule "Libraries.SignalGenerator"] path = Sming/Libraries/SignalGenerator url = https://github.com/mikee47/SignalGenerator diff --git a/Sming/Arch/Host/Components/SerialLib/SerialLib.h b/Sming/Arch/Host/Components/SerialLib/SerialLib.h new file mode 100644 index 0000000000..6f57aed795 --- /dev/null +++ b/Sming/Arch/Host/Components/SerialLib/SerialLib.h @@ -0,0 +1,7 @@ +#pragma once + +#include "seriallib/lib/serialib.h" + +class SerialDevice : public serialib +{ +}; diff --git a/Sming/Arch/Host/Components/SerialLib/component.mk b/Sming/Arch/Host/Components/SerialLib/component.mk new file mode 100644 index 0000000000..ba74706ec1 --- /dev/null +++ b/Sming/Arch/Host/Components/SerialLib/component.mk @@ -0,0 +1,4 @@ +COMPONENT_SUBMODULES += seriallib + +COMPONENT_SRCDIRS := seriallib/lib +COMPONENT_INCDIRS := . diff --git a/Sming/Libraries/seriallib/seriallib b/Sming/Arch/Host/Components/SerialLib/seriallib similarity index 100% rename from Sming/Libraries/seriallib/seriallib rename to Sming/Arch/Host/Components/SerialLib/seriallib diff --git a/Sming/Arch/Host/Components/SerialLib/seriallib.patch b/Sming/Arch/Host/Components/SerialLib/seriallib.patch new file mode 100644 index 0000000000..269d0a3cda --- /dev/null +++ b/Sming/Arch/Host/Components/SerialLib/seriallib.patch @@ -0,0 +1,22 @@ +diff --git a/lib/serialib.cpp b/lib/serialib.cpp +index 8dbe52f..dc25811 100644 +--- a/lib/serialib.cpp ++++ b/lib/serialib.cpp +@@ -135,7 +135,7 @@ char serialib::openDevice(const char *Device,const unsigned int Bauds) + case 115200 : dcbSerialParams.BaudRate=CBR_115200; break; + case 128000 : dcbSerialParams.BaudRate=CBR_128000; break; + case 256000 : dcbSerialParams.BaudRate=CBR_256000; break; +- default : return -4; ++ default : dcbSerialParams.BaudRate=Bauds; + } + // 8 bit data + dcbSerialParams.ByteSize=8; +@@ -609,7 +609,7 @@ int serialib::available() + // Device errors + DWORD commErrors; + // Device status +- COMSTAT commStatus; ++ COMSTAT commStatus{}; + // Read status + ClearCommError(hSerial, &commErrors, &commStatus); + // Return the number of pending bytes diff --git a/Sming/Arch/Host/Components/driver/component.mk b/Sming/Arch/Host/Components/driver/component.mk index 27b5e36d23..334ca3db26 100644 --- a/Sming/Arch/Host/Components/driver/component.mk +++ b/Sming/Arch/Host/Components/driver/component.mk @@ -1,6 +1,8 @@ COMPONENT_INCDIRS := include -COMPONENT_DEPENDS := arch_driver +COMPONENT_DEPENDS := \ + arch_driver \ + SerialLib COMPONENT_VARS += USE_US_TIMER USE_US_TIMER ?= 1 diff --git a/Sming/Arch/Host/Components/driver/uart.rst b/Sming/Arch/Host/Components/driver/uart.rst index 1d03f556d9..8e8a6e19c8 100644 --- a/Sming/Arch/Host/Components/driver/uart.rst +++ b/Sming/Arch/Host/Components/driver/uart.rst @@ -6,7 +6,8 @@ Host UART driver Introduction ------------ -Implements a UART driver to connect via TCP sockets, allowing terminal emulation using telnet. +Implements a UART driver to connect via TCP socket, allowing terminal emulation using telnet, +or directly to local host serial device (e.g. /dev/ttyUSB0, COM4, etc.) By default, output to UART0 is sent to the console and keyboard input is written to the UART0 receive queue. If emulation is enabled on any ports then this behaviour is disabled. @@ -44,11 +45,9 @@ Build variables Allows full customisation of UART command-line options for ``make run``. - You should not need to change this for normal use. - -Usage ------ +TCP port emulation +------------------ Set required ports for emulation using the :envvar:`ENABLE_HOST_UARTID`, then execute ``make run``. @@ -79,3 +78,26 @@ output at startup.) Port numbers are allocated sequentially from 10000. If you want to use different port numbers, set :envvar:`HOST_UART_PORTBASE`. + +Physical device connection +-------------------------- + +Override :envvar:`HOST_UART_OPTIONS` adding the `--device` option. For example:: + + make run HOST_UART_OPTIONS="--uart=0 --device=/dev/ttyUSB0" + +The ``--device`` option must follow the ``--uart`` option. Another example:: + + make run HOST_UART_OPTIONS="--uart=0 --device=/dev/ttyUSB0 --uart=1 --device=/dev/ttyUSB1" + +The port is opened when ``uart_init()`` gets called. + +The default baud rate is whatever the application has requested. This can be overridden as follows:: + + make run HOST_UART_OPTIONS="--uart=0 --device=/dev/ttyUSB0 --baud=921600" + +For Windows, substitute the appropriate device name, e.g. ``COM4`` instead of ``/dev/ttyUSB0``. + +.. note:: + + If necessary, add ``ENABLE_HOST_UARTID=`` to prevent telnet windows from being created. diff --git a/Sming/Arch/Host/Components/driver/uart_server.cpp b/Sming/Arch/Host/Components/driver/uart_server.cpp index 08e9d4f74b..2b0e5ad0a5 100644 --- a/Sming/Arch/Host/Components/driver/uart_server.cpp +++ b/Sming/Arch/Host/Components/driver/uart_server.cpp @@ -22,12 +22,15 @@ #include #include #include +#include -const unsigned IDLE_SLEEP_MS = 100; - -unsigned CUartServer::portBase = 10000; - -static CUartServer* uartServers[UART_COUNT]; +namespace UartServer +{ +namespace +{ +unsigned portBase{10000}; +std::unique_ptr servers[UART_COUNT]; +} // namespace class KeyboardThread : public CThread { @@ -131,14 +134,14 @@ static void onUart0Notify(smg_uart_t* uart, smg_uart_notify_code_t code) } } -void CUartServer::startup(const UartServerConfig& config) +void startup(const Config& config) { if(config.portBase != 0) { portBase = config.portBase; } auto notify = [](smg_uart_t* uart, smg_uart_notify_code_t code) { - auto server = uartServers[uart->uart_nr]; + auto& server = servers[uart->uart_nr]; if(server) { server->onNotify(uart, code); } else if(code == UART_NOTIFY_AFTER_WRITE) { @@ -151,8 +154,12 @@ void CUartServer::startup(const UartServerConfig& config) if(!bitRead(config.enableMask, i)) { continue; } - auto& server = uartServers[i]; - server = new CUartServer(i); + auto& server = servers[i]; + if(config.deviceNames[i] == nullptr) { + server.reset(new CUartPort(i)); + } else { + server.reset(new CUartDevice(i, config.deviceNames[i], config.baud[i])); + } server->execute(); } @@ -163,30 +170,34 @@ void CUartServer::startup(const UartServerConfig& config) } } -void CUartServer::shutdown() +void shutdown() { destroyKeyboardThread(); for(unsigned i = 0; i < UART_COUNT; ++i) { - auto& server = uartServers[i]; - if(server == nullptr) { + auto& server = servers[i]; + if(!server) { continue; } server->terminate(); - delete server; - server = nullptr; + server.reset(); } } -void CUartServer::terminate() +/* CUart */ + +CUart::CUart(unsigned uart_nr) : CThread("uart", 1), uart_nr(uart_nr) +{ +} + +void CUart::terminate() { - close(); join(); host_debug_i("UART%u server destroyed", uart_nr); } -void CUartServer::onNotify(smg_uart_t* uart, smg_uart_notify_code_t code) +void CUart::onNotify(smg_uart_t* uart, smg_uart_notify_code_t code) { switch(code) { case UART_NOTIFY_AFTER_OPEN: @@ -198,31 +209,29 @@ void CUartServer::onNotify(smg_uart_t* uart, smg_uart_notify_code_t code) break; case UART_NOTIFY_AFTER_WRITE: { - if(socket == nullptr) { - // Not connected, discard data - uart->tx_buffer->clear(); - } else { + if(uart != nullptr) { // Kick the thread to send now txsem.post(); + } else { + // Not connected, discard data + uart->tx_buffer->clear(); } break; } case UART_NOTIFY_WAIT_TX: - break; - case UART_NOTIFY_BEFORE_READ: break; } } -int CUartServer::serviceRead() +int CUart::serviceRead() { if(!smg_uart_rx_enabled(uart)) { return 0; } - int avail = socket->available(); + int avail = available(); if(avail <= 0) { return avail; } @@ -230,10 +239,13 @@ int CUartServer::serviceRead() interrupt_begin(); int space = uart->rx_buffer->getFreeSpace(); + if(space < avail) { + uart->status |= UART_RXFIFO_OVF_INT_ST; + } int read = std::min(space, avail); if(read != 0) { char buffer[read]; - read = socket->recv(buffer, read); + read = readBytes(buffer, read); if(read > 0) { for(int i = 0; i < read; ++i) { uart->rx_buffer->writeChar(buffer[i]); @@ -252,7 +264,7 @@ int CUartServer::serviceRead() return read; } -int CUartServer::serviceWrite() +int CUart::serviceWrite() { if(!smg_uart_tx_enabled(uart)) { return 0; @@ -269,7 +281,7 @@ int CUartServer::serviceWrite() interrupt_begin(); do { - int sent = socket->send(data, avail); + int sent = writeBytes(data, avail); if(sent < 0) { host_debug_w("Uart send returned %d", sent); result = sent; @@ -290,7 +302,34 @@ int CUartServer::serviceWrite() return result; } -void* CUartServer::thread_routine() +/* CUartPort */ + +CUartPort::CUartPort(unsigned uart_nr) : CUart(uart_nr) +{ +} + +void CUartPort::terminate() +{ + close(); + CUart::terminate(); +} + +int CUartPort::available() +{ + return socket ? socket->available() : 0; +} + +int CUartPort::readBytes(void* buffer, size_t size) +{ + return socket ? socket->recv(buffer, size) : 0; +} + +int CUartPort::writeBytes(const void* data, size_t size) +{ + return socket ? socket->send(data, size) : 0; +} + +void* CUartPort::thread_routine() { auto port = portBase + uart_nr; CSockAddr addr(nullptr, port); @@ -340,3 +379,102 @@ void* CUartServer::thread_routine() return nullptr; } + +/* CUartDevice */ + +CUartDevice::CUartDevice(unsigned uart_nr, const char* deviceName, unsigned baud_rate) + : CUart(uart_nr), deviceName(deviceName), baud_rate(baud_rate) +{ +} + +void CUartDevice::terminate() +{ + done = true; + txsem.post(); + CUart::terminate(); +} + +void CUartDevice::onNotify(smg_uart_t* uart, smg_uart_notify_code_t code) +{ + CUart::onNotify(uart, code); + switch(code) { + case UART_NOTIFY_AFTER_OPEN: + if(baud_rate != 0) { + uart->baud_rate = baud_rate; + } + txsem.post(); + break; + + case UART_NOTIFY_BEFORE_CLOSE: + txsem.post(); + break; + + case UART_NOTIFY_AFTER_WRITE: + case UART_NOTIFY_WAIT_TX: + case UART_NOTIFY_BEFORE_READ: + break; + } +} + +int CUartDevice::available() +{ + return device ? device->available() : 0; +} + +int CUartDevice::readBytes(void* buffer, size_t size) +{ + return device ? device->readBytes(buffer, size) : 0; +} + +int CUartDevice::writeBytes(const void* data, size_t size) +{ + return device ? device->writeBytes(data, size) : 0; +} + +void* CUartDevice::thread_routine() +{ + while(!done) { + if(txsem.timedwait(IDLE_SLEEP_MS)) { + if(uart != nullptr && !device) { + device.reset(new SerialDevice); + char res = device->openDevice(deviceName, uart->baud_rate); + if(res != 1) { + host_debug_e("UART%u error %d opening serial device '%s'", uart_nr, res, deviceName); + device.reset(); + break; + } + device->DTR(false); + device->RTS(false); + host_debug_i("UART%u connected to '%s' @ %u baud", uart_nr, deviceName, uart->baud_rate); + } else if(uart == nullptr && device) { + device.reset(); + host_debug_i("Uart #%u device closed", uart_nr); + } + + if(serviceWrite() < 0) { + break; + } + } + + if(serviceRead() < 0) { + break; + } + + if(uart != nullptr) { + interrupt_begin(); + + auto status = uart->status; + uart->status = 0; + if(status != 0 && uart->callback != nullptr) { + uart->callback(uart, status); + } + + interrupt_end(); + } + } + + device.reset(); + return nullptr; +} + +} // namespace UartServer diff --git a/Sming/Arch/Host/Components/driver/uart_server.h b/Sming/Arch/Host/Components/driver/uart_server.h index 8b053408b1..dd84fef467 100644 --- a/Sming/Arch/Host/Components/driver/uart_server.h +++ b/Sming/Arch/Host/Components/driver/uart_server.h @@ -22,52 +22,105 @@ #include #include #include +#include -#define UART_SOCKET_PORT_BASE 10000 ///< Port for UART0 +class SerialDevice; -struct UartServerConfig { - unsigned enableMask; ///< Bit mask for required servers - unsigned portBase; ///< Base port address (optional) +/* + * Manages a set of virtualised UARTs + */ +namespace UartServer +{ +struct Config { + unsigned enableMask; ///< Bit mask for required servers + unsigned portBase; ///< Base port address (optional) + const char* deviceNames[UART_COUNT]; ///< Map uart to host port + unsigned baud[UART_COUNT]; ///< Speed for physical serial device }; +class CUart; + +/** + * @brief Start requested servers + * @param config + */ +void startup(const Config& config); + +void shutdown(); + /* - * Each server allocates a thread to listen for incoming connections. Only one client - * connection is permitted. - * - * For simplicity this implementation uses sockets for communication, so we can just use - * telnet as the terminal application. + * Base class for a UART * - * A socket is opened for each uart on the system at startup. If no client is connected - * any output is discarded. + * Each server allocates a thread to handle one device. + * If no client (i.e. application `uart`) is connected any output is discarded. * */ -class CUartServer : public CThread, public CServerSocket +class CUart : public CThread { public: - /** - * @brief Start requested servers - * @param config - */ - static void startup(const UartServerConfig& config); + CUart(unsigned uart_nr); - static void shutdown(); + virtual void terminate(); - CUartServer(unsigned uart_nr) : CThread("uart", 1), uart_nr(uart_nr) - { - } - - void terminate(); + virtual void onNotify(smg_uart_t* uart, smg_uart_notify_code_t code); protected: - void onNotify(smg_uart_t* uart, smg_uart_notify_code_t code); + static const unsigned IDLE_SLEEP_MS{100}; + + virtual int available() = 0; + virtual int readBytes(void* buffer, size_t size) = 0; + virtual int writeBytes(const void* data, size_t size) = 0; int serviceRead(); int serviceWrite(); + + CSemaphore txsem; ///< Signals when there's data to be sent out + unsigned uart_nr; ///< Which port we represent + smg_uart_t* uart = nullptr; ///< On set if port is open by application +}; + +/* + * UART implementation using TCP socket for communication, so we can use telnet as a terminal application. + */ +class CUartPort : public CUart, public CServerSocket +{ +public: + CUartPort(unsigned uart_nr); + + void terminate() override; + +protected: + int available() override; + int readBytes(void* buffer, size_t size) override; + int writeBytes(const void* data, size_t size) override; void* thread_routine() override; -private: - static unsigned portBase; - CSocket* socket = nullptr; ///< Connected client - CSemaphore txsem; ///< Signals when there's data to be sent out - unsigned uart_nr; ///< Which port we represent - smg_uart_t* uart = nullptr; ///< On set if port is open by application + CSocket* socket{nullptr}; ///< Connected client }; + +/* + * UART implementation using physical serial device on local machine + */ +class CUartDevice : public CUart +{ +public: + static constexpr unsigned DEFAULT_BAUD{115200}; + + CUartDevice(unsigned uart_nr, const char* deviceName, unsigned baud); + + void terminate() override; + + void onNotify(smg_uart_t* uart, smg_uart_notify_code_t code) override; + +protected: + int available() override; + int readBytes(void* buffer, size_t size) override; + int writeBytes(const void* data, size_t size) override; + void* thread_routine() override; + + std::unique_ptr device; ///< Physical device + const char* deviceName{nullptr}; ///< Physical device name + unsigned baud_rate{0}; ///< Command-line override for baud rate + bool done{false}; +}; + +}; // namespace UartServer diff --git a/Sming/Arch/Host/Components/hostlib/options.h b/Sming/Arch/Host/Components/hostlib/options.h index a7371993b6..9634e0b2fa 100644 --- a/Sming/Arch/Host/Components/hostlib/options.h +++ b/Sming/Arch/Host/Components/hostlib/options.h @@ -31,6 +31,10 @@ XX(help, no_argument, "Show help", nullptr, nullptr, nullptr) \ XX(uart, required_argument, "Enable UART server", "PORT", "Which UART number to enable", \ "e.g. --uart=0 --uart=1 enable servers for UART0, UART1\0") \ + XX(device, required_argument, "Set device for uart", "DEVICE", "Optionally map uart to device", \ + "e.g. --uart=0 --device=/dev/ttyUSB0") \ + XX(baud, required_argument, "Set baud rate for UART", "BAUD", "Requires --device argument", \ + "e.g. --uart=0 --device=/dev/ttyUSB0 --baud=115200") \ XX(portbase, required_argument, "Specify base port number for UART socket servers", "PORT", "IP port number", \ nullptr) \ XX(ifname, required_argument, "Specify network interface", "NAME", "Network interface to use (e.g. tap0)", \ diff --git a/Sming/Arch/Host/Components/hostlib/startup.cpp b/Sming/Arch/Host/Components/hostlib/startup.cpp index 0f650f0168..aad178fea9 100644 --- a/Sming/Arch/Host/Components/hostlib/startup.cpp +++ b/Sming/Arch/Host/Components/hostlib/startup.cpp @@ -55,7 +55,7 @@ static void cleanup() { hw_timer_cleanup(); host_flashmem_cleanup(); - CUartServer::shutdown(); + UartServer::shutdown(); sockets_finalise(); #ifndef DISABLE_WIFI host_lwip_shutdown(); @@ -135,7 +135,7 @@ int main(int argc, char* argv[]) int exitpause; bool initonly; bool enable_network; - UartServerConfig uart; + UartServer::Config uart; FlashmemConfig flash; #ifndef DISABLE_WIFI struct lwip_param lwip; @@ -165,6 +165,7 @@ int main(int argc, char* argv[]) #endif }; + int uart_num{-1}; option_tag_t opt; const char* arg; while((opt = get_option(argc, argv, arg)) != opt_none) { @@ -174,7 +175,25 @@ int main(int argc, char* argv[]) return 0; case opt_uart: - bitSet(config.uart.enableMask, atoi(arg)); + uart_num = atoi(arg); + if(uart_num < 0 || uart_num >= UART_COUNT) { + host_printf("UART %d number invalid\r\n", uart_num); + return 0; + } + bitSet(config.uart.enableMask, uart_num); + break; + + case opt_device: + case opt_baud: + if(uart_num < 0) { + host_printf("--uart option missing\r\n"); + return 0; + } + if(opt == opt_device) { + config.uart.deviceNames[uart_num] = arg; + } else if(opt == opt_baud) { + config.uart.baud[uart_num] = atoi(arg); + } break; case opt_portbase: @@ -263,7 +282,7 @@ int main(int argc, char* argv[]) host_init_tasks(); sockets_initialise(); - CUartServer::startup(config.uart); + UartServer::startup(config.uart); #ifndef DISABLE_WIFI if(config.enable_network) { diff --git a/Sming/Components/Hosted/component.mk b/Sming/Components/Hosted/component.mk index 49d6f81bd2..29dde196ff 100644 --- a/Sming/Components/Hosted/component.mk +++ b/Sming/Components/Hosted/component.mk @@ -13,7 +13,7 @@ ENABLE_HOSTED ?= ifneq ($(ENABLE_HOSTED),) COMPONENT_SRCDIRS += $(COMPONENT_PATH)/init/$(ENABLE_HOSTED) EXTRA_LDFLAGS := -Wl,-wrap,host_init - COMPONENT_DEPENDS += seriallib + COMPONENT_DEPENDS += SerialLib endif COMPONENT_VARS += HOSTED_SERVER_IP diff --git a/Sming/Components/Hosted/include/Hosted/Serial.h b/Sming/Components/Hosted/include/Hosted/Serial.h index 1d26b52bd4..8bdfc94b10 100644 --- a/Sming/Components/Hosted/include/Hosted/Serial.h +++ b/Sming/Components/Hosted/include/Hosted/Serial.h @@ -19,7 +19,7 @@ #include #include -#include +#include namespace Hosted { diff --git a/Sming/Libraries/seriallib/component.mk b/Sming/Libraries/seriallib/component.mk deleted file mode 100644 index a350642adf..0000000000 --- a/Sming/Libraries/seriallib/component.mk +++ /dev/null @@ -1,6 +0,0 @@ -COMPONENT_SUBMODULES += seriallib - -SERIALLIB_ROOT := $(COMPONENT_PATH)/seriallib - -COMPONENT_SRCDIRS := $(SERIALLIB_ROOT)/lib $(COMPONENT_PATH)/src -COMPONENT_INCDIRS := $(COMPONENT_SRCDIRS) $(COMPONENT_PATH)/include \ No newline at end of file From e37db564cd1c5b198972bed4b24332e3f8531a09 Mon Sep 17 00:00:00 2001 From: slaff Date: Thu, 6 May 2021 09:40:46 +0200 Subject: [PATCH 024/130] Store the actual size of the downloaded rBoot item. (#2327) --- Sming/Components/rboot/src/Network/RbootHttpUpdater.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Sming/Components/rboot/src/Network/RbootHttpUpdater.cpp b/Sming/Components/rboot/src/Network/RbootHttpUpdater.cpp index 78ed79e27a..883dd60dad 100644 --- a/Sming/Components/rboot/src/Network/RbootHttpUpdater.cpp +++ b/Sming/Components/rboot/src/Network/RbootHttpUpdater.cpp @@ -58,6 +58,7 @@ int RbootHttpUpdater::itemComplete(HttpConnection& client, bool success) debug_d("Finished: URL: %s, Offset: 0x%X, Length: %u", it.url.c_str(), it.stream->getStartAddress(), it.stream->available()); + it.size = it.stream->available(); it.stream = nullptr; // the actual deletion will happen outside of this class currentItem++; From 249dd6704949bb9b8ccf7dfe47e9c4fb17584933 Mon Sep 17 00:00:00 2001 From: slaff Date: Fri, 14 May 2021 12:00:54 +0200 Subject: [PATCH 025/130] CI: Fix problem with sphinx on AppVeyor for Windows (#2329) --- Sming/Arch/Host/Tools/ci/install.cmd | 4 ++++ appveyor.yml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Sming/Arch/Host/Tools/ci/install.cmd b/Sming/Arch/Host/Tools/ci/install.cmd index 556cb4dd2f..2e26df3ecd 100644 --- a/Sming/Arch/Host/Tools/ci/install.cmd +++ b/Sming/Arch/Host/Tools/ci/install.cmd @@ -3,8 +3,12 @@ REM Host install.cmd call :install "c:\tools\doxygen" doxygen-1.9.1.windows.bin.zip call :install "c:\tools" stable_windows_10_msbuild_Release_Win32_graphviz-2.46.1-win32.zip +python -m pip install --upgrade pip setuptools wheel + python -m pip install -r %SMING_HOME%/../docs/requirements.txt +python -m pip uninstall -y xcffib + goto :EOF :install diff --git a/appveyor.yml b/appveyor.yml index ddb897acc3..413d69e599 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -48,11 +48,11 @@ install: $env:SMING_HOME = Join-Path $env:CI_BUILD_DIR "Sming" - cmd: | - set PATH=C:\Python39;C:\MinGW\msys\1.0\bin;C:\MinGW\bin;%PATH% + set PATH=C:\Python39;C:\Python39\Scripts;C:\MinGW\msys\1.0\bin;C:\MinGW\bin;%PATH% set PYTHON=C:\Python39\python set ESP32_PYTHON_PATH=C:\Python39 Tools\ci\install.cmd - set PATH=C:\Python39\Scripts;C:\tools\doxygen;C:\tools\Graphviz\bin;%PATH% + set PATH=C:\tools\doxygen;C:\tools\Graphviz\bin;%PATH% - sh: | export PYTHON=$HOME/venv3.9/bin/python From cdc87068fc5463abec29ce620dae90f63f990301 Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 16 May 2021 09:41:08 +0100 Subject: [PATCH 026/130] Add ESP8266 implementations for all STL exception functions (#2331) --- .../Components/esp8266/esp_cplusplus.cpp | 10 +- .../Components/esp8266/functexcept.cpp | 93 +++++++++++++++++++ 2 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 Sming/Arch/Esp8266/Components/esp8266/functexcept.cpp diff --git a/Sming/Arch/Esp8266/Components/esp8266/esp_cplusplus.cpp b/Sming/Arch/Esp8266/Components/esp8266/esp_cplusplus.cpp index 62ccea5c32..965f7fd879 100644 --- a/Sming/Arch/Esp8266/Components/esp8266/esp_cplusplus.cpp +++ b/Sming/Arch/Esp8266/Components/esp8266/esp_cplusplus.cpp @@ -15,7 +15,8 @@ extern void (*__init_array_start)(); extern void (*__init_array_end)(); -namespace std { +namespace std +{ const nothrow_t nothrow; } @@ -42,10 +43,3 @@ extern "C" void __cxa_deleted_virtual(void) SYSTEM_ERROR("Bad deleted_virtual_call"); abort(); } - -namespace std { - void WEAK_ATTR __throw_bad_function_call() - { - while(1); - }; -} diff --git a/Sming/Arch/Esp8266/Components/esp8266/functexcept.cpp b/Sming/Arch/Esp8266/Components/esp8266/functexcept.cpp new file mode 100644 index 0000000000..7394848d03 --- /dev/null +++ b/Sming/Arch/Esp8266/Components/esp8266/functexcept.cpp @@ -0,0 +1,93 @@ +// Function-Based Exception Support -*- C++ -*- + +// Copyright (C) 2001-2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file bits/functexcept.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{exception} + * + * This header provides support for -fno-exceptions. + */ + +// +// ISO C++ 14882: 19.1 Exception classes +// + +#include + +#define THROW_HANDLER(name, ...) \ + void WEAK_ATTR __throw_##name(__VA_ARGS__) \ + { \ + while(1) { \ + } \ + } + +namespace std +{ +// Helper for exception objects in +THROW_HANDLER(bad_exception, void) + +// Helper for exception objects in +THROW_HANDLER(bad_alloc, void) + +// Helper for exception objects in +THROW_HANDLER(bad_cast, void) + +THROW_HANDLER(bad_typeid, void) + +// Helpers for exception objects in +THROW_HANDLER(logic_error, const char*) + +THROW_HANDLER(domain_error, const char*) + +THROW_HANDLER(invalid_argument, const char*) + +THROW_HANDLER(length_error, const char*) + +THROW_HANDLER(out_of_range, const char*) + +THROW_HANDLER(out_of_range_fmt, const char*, ...) + +THROW_HANDLER(runtime_error, const char*) + +THROW_HANDLER(range_error, const char*) + +THROW_HANDLER(overflow_error, const char*) + +THROW_HANDLER(underflow_error, const char*) + +// Helpers for exception objects in +THROW_HANDLER(ios_failure, const char*) + +THROW_HANDLER(ios_failure, const char*, int) + +// Helpers for exception objects in +THROW_HANDLER(system_error, int) + +// Helpers for exception objects in +THROW_HANDLER(future_error, int) + +// Helpers for exception objects in +THROW_HANDLER(bad_function_call) + +} // namespace std From 99bc5af2cc94aded19a39a77081a2431c1e4d206 Mon Sep 17 00:00:00 2001 From: slaff Date: Thu, 27 May 2021 10:07:04 +0200 Subject: [PATCH 027/130] Feature: unified Over-The-Air (OTA) upgrades (#2332) Allows the usage of over-the-air updates for Esp8266, Esp32 and can be test on Host. Co-authored-by: mikee47 --- README.md | 43 ++--- Sming/Arch/Esp32/spiffs-two-roms.hw | 33 ++++ Sming/Arch/Esp8266/spiffs-two-roms.hw | 4 +- Sming/Components/Ota/README.rst | 61 +++++++ Sming/Components/Ota/component.mk | 16 ++ Sming/Components/Ota/src/.cs | 0 .../Ota/src/Arch/Esp32/IdfUpgrader.cpp | 47 ++++++ .../src/Arch/Esp32/include/Ota/IdfUpgrader.h | 77 +++++++++ .../Ota/src/Arch/Esp32/include/Ota/Upgrader.h | 16 ++ .../Ota/src/Arch/Esp8266/RbootUpgrader.cpp | 75 +++++++++ .../Arch/Esp8266/include/Ota/RbootUpgrader.h | 67 ++++++++ .../src/Arch/Esp8266/include/Ota/Upgrader.h | 16 ++ Sming/Components/Ota/src/Manager.cpp | 4 + .../Ota/src/UpgradeOutputStream.cpp | 60 +++++++ .../Components/Ota/src/include/Ota/Manager.h | 7 + .../Ota/src/include/Ota/UpgradeOutputStream.h | 92 +++++++++++ .../Ota/src/include/Ota/UpgraderBase.h | 129 +++++++++++++++ Sming/Components/OtaNetwork/README.rst | 61 +++++++ Sming/Components/OtaNetwork/component.mk | 4 + Sming/Components/OtaNetwork/src/.cs | 0 .../OtaNetwork/src/HttpUpgrader.cpp | 120 ++++++++++++++ .../src/include/Ota/Network/HttpUpgrader.h | 156 ++++++++++++++++++ .../OtaUpgrade/OtaUpgrade/BasicStream.cpp | 29 ++-- .../OtaUpgrade/OtaUpgrade/BasicStream.h | 31 ++-- Sming/Libraries/OtaUpgrade/component.mk | 2 +- Sming/Libraries/OtaUpgradeMqtt/component.mk | 5 +- .../samples/Upgrade/app/application.cpp | 43 ++--- .../samples/Upgrade/component.mk | 4 +- .../OtaUpgradeMqtt/samples/Upgrade/ota.hw | 11 ++ .../OtaUpgradeMqtt/src/RbootPayloadParser.cpp | 40 ----- .../src/StandardPayloadParser.cpp | 43 +++++ .../OtaUpgrade/Mqtt/RbootPayloadParser.h | 24 +-- .../OtaUpgrade/Mqtt/StandardPayloadParser.h | 43 +++++ docs/source/getting-started/index.rst | 45 ++--- samples/{Basic_rBoot => Basic_Ota}/.cproject | 0 samples/{Basic_rBoot => Basic_Ota}/.project | 2 +- samples/{Basic_rBoot => Basic_Ota}/Makefile | 0 samples/Basic_Ota/README.rst | 99 +++++++++++ .../app/application.cpp | 106 ++++++------ samples/Basic_Ota/component.mk | 23 +++ .../files/testfile.txt | 0 samples/Basic_Ota/ota.hw | 11 ++ samples/Basic_rBoot/README.rst | 83 ---------- samples/Basic_rBoot/basic_rboot.hw | 22 --- samples/Basic_rBoot/component.mk | 7 - 45 files changed, 1402 insertions(+), 359 deletions(-) create mode 100644 Sming/Arch/Esp32/spiffs-two-roms.hw create mode 100644 Sming/Components/Ota/README.rst create mode 100644 Sming/Components/Ota/component.mk create mode 100644 Sming/Components/Ota/src/.cs create mode 100644 Sming/Components/Ota/src/Arch/Esp32/IdfUpgrader.cpp create mode 100644 Sming/Components/Ota/src/Arch/Esp32/include/Ota/IdfUpgrader.h create mode 100644 Sming/Components/Ota/src/Arch/Esp32/include/Ota/Upgrader.h create mode 100644 Sming/Components/Ota/src/Arch/Esp8266/RbootUpgrader.cpp create mode 100644 Sming/Components/Ota/src/Arch/Esp8266/include/Ota/RbootUpgrader.h create mode 100644 Sming/Components/Ota/src/Arch/Esp8266/include/Ota/Upgrader.h create mode 100644 Sming/Components/Ota/src/Manager.cpp create mode 100644 Sming/Components/Ota/src/UpgradeOutputStream.cpp create mode 100644 Sming/Components/Ota/src/include/Ota/Manager.h create mode 100644 Sming/Components/Ota/src/include/Ota/UpgradeOutputStream.h create mode 100644 Sming/Components/Ota/src/include/Ota/UpgraderBase.h create mode 100644 Sming/Components/OtaNetwork/README.rst create mode 100644 Sming/Components/OtaNetwork/component.mk create mode 100644 Sming/Components/OtaNetwork/src/.cs create mode 100644 Sming/Components/OtaNetwork/src/HttpUpgrader.cpp create mode 100644 Sming/Components/OtaNetwork/src/include/Ota/Network/HttpUpgrader.h create mode 100644 Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/ota.hw delete mode 100644 Sming/Libraries/OtaUpgradeMqtt/src/RbootPayloadParser.cpp create mode 100644 Sming/Libraries/OtaUpgradeMqtt/src/StandardPayloadParser.cpp create mode 100644 Sming/Libraries/OtaUpgradeMqtt/src/include/OtaUpgrade/Mqtt/StandardPayloadParser.h rename samples/{Basic_rBoot => Basic_Ota}/.cproject (100%) rename samples/{Basic_rBoot => Basic_Ota}/.project (96%) rename samples/{Basic_rBoot => Basic_Ota}/Makefile (100%) create mode 100644 samples/Basic_Ota/README.rst rename samples/{Basic_rBoot => Basic_Ota}/app/application.cpp (67%) create mode 100644 samples/Basic_Ota/component.mk rename samples/{Basic_rBoot => Basic_Ota}/files/testfile.txt (100%) create mode 100644 samples/Basic_Ota/ota.hw delete mode 100644 samples/Basic_rBoot/README.rst delete mode 100644 samples/Basic_rBoot/basic_rboot.hw delete mode 100644 samples/Basic_rBoot/component.mk diff --git a/README.md b/README.md index 102408e219..74afea59b8 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Table of Contents * [Connect to WiFi](#connect-to-wifi) * [Read DHT22 sensor](#read-dht22-sensor) * [HTTP Client](#http-client) - * [OTA Application Update Based on rBoot](#ota-application-update-based-on-rboot) + * [OTA Application Update](#ota-application-update) * [HTTP Server](#http-server) * [Email Client](#email-client) * [Live Debugging](#live-debugging) @@ -161,7 +161,7 @@ And check some of the examples. - [Connect to WiFi](#connect-to-wifi) - [Read DHT22 sensor](#read-dht22-sensor) - [HTTP Client](#http-client) -- [OTA Application Update Based on rBoot](#ota-application-update-based-on-rboot) +- [OTA Application Update](#ota-application-update) - [HTTP Server](#http-server) - [Email Client](#email-client) @@ -236,44 +236,31 @@ void onDataSent(HttpClient& client, bool successful) For more examples take a look at the [HttpClient](samples/HttpClient/app/application.cpp), [HttpClient_Instapush](samples/HttpClient_Instapush/app/application.cpp) and [HttpClient_ThingSpeak](samples/HttpClient_ThingSpeak/app/application.cpp) samples. -### OTA Application Update Based on rBoot +### OTA Application Update ```c++ -void OtaUpdate() +void doUpgrade() { -  uint8 slot; -  rboot_config bootconf; + // need a clean object, otherwise if run before and failed will not run again + if(otaUpdater) { + delete otaUpdater; + } + otaUpdater = new Ota::Network::HttpUpgrader(); -  Serial.println("Updating..."); + // select rom partition to flash + auto part = ota.getNextBootPartition(); -  // need a clean object, otherwise if run before and failed will not run again -  if (otaUpdater) { -    delete otaUpdater; -  } - -  otaUpdater = new RbootHttpUpdater(); - -  // select rom slot to flash -  bootconf = rboot_get_config(); -  slot = bootconf.current_rom; -  if (slot == 0) { -    slot = 1; -  } -  else { -    slot = 0; -  } - -  // flash rom to position indicated in the rBoot config rom table -  otaUpdater->addItem(bootconf.roms[slot], ROM_0_URL); +  // The content located on ROM_0_URL will be stored to the new partition +  otaUpdater->addItem(ROM_0_URL, part);   // and/or set a callback (called on failure or success without switching requested) -  otaUpdater->setCallback(OtaUpdate_CallBack); +  otaUpdater->setCallback(upgradeCallback);   // start update   otaUpdater->start(); } ``` -For a complete example take a look at the [Basic_rBoot](samples/Basic_rBoot/app/application.cpp) sample. +For a complete example take a look at the [Basic_Ota](samples/Basic_Ota/app/application.cpp) sample. ### HTTP Server ```c++ diff --git a/Sming/Arch/Esp32/spiffs-two-roms.hw b/Sming/Arch/Esp32/spiffs-two-roms.hw new file mode 100644 index 0000000000..0d7f3d35ea --- /dev/null +++ b/Sming/Arch/Esp32/spiffs-two-roms.hw @@ -0,0 +1,33 @@ +{ + "name": "Two ROM slots with single SPIFFS", + "base_config": "spiffs", + "partitions": { + "factory": { + "size": "1M" + }, + "rom0": { + "address": "0x110000", + "size": "960K", + "type": "app", + "subtype": "ota_0", + "filename": "$(TARGET_BIN)" + }, + "ota": { + "address": "0x00200000", + "size": "8K", + "type": "data", + "subtype": "ota" + }, + "rom1": { + "address": "0x210000", + "size": "960K", + "type": "app", + "subtype": "ota_1", + "filename": "" + }, + "spiffs0": { + "address": "0x300000", + "size": "1M" + } + } +} \ No newline at end of file diff --git a/Sming/Arch/Esp8266/spiffs-two-roms.hw b/Sming/Arch/Esp8266/spiffs-two-roms.hw index 93e4605b47..204546a8f7 100644 --- a/Sming/Arch/Esp8266/spiffs-two-roms.hw +++ b/Sming/Arch/Esp8266/spiffs-two-roms.hw @@ -6,11 +6,11 @@ "subtype": "ota_0" }, "rom1": { - "address": "0x108000", + "address": "0x102000", "size": "992K", "type": "app", "subtype": "ota_1", - "filename": "$(RBOOT_ROM_1_BIN)" + "filename": "$(RBOOT_ROM_0_BIN)" } } } diff --git a/Sming/Components/Ota/README.rst b/Sming/Components/Ota/README.rst new file mode 100644 index 0000000000..5b0c046e43 --- /dev/null +++ b/Sming/Components/Ota/README.rst @@ -0,0 +1,61 @@ +Over-The-Air(OTA) Upgrader +========================== + +.. highlight:: c++ + +Introduction +------------ + +This architecture-agnostic component adds support for Over-The-Air upgrades. + +Usage +----- +1. Add ``COMPONENT_DEPENDS += Ota`` to your application componenent.mk file. +2. Add these lines to your application:: + + #include + +After that you will have access to a global ``OtaManager`` instance that can be used to manage your OTA upgrade process. + +3. You can use ``OtaManager`` to get information about the bootable partitions and update them. + The code below will display the current bootable and running partition:: + + void init() + { + + // ... + auto partition = OtaManager.getRunningPartition(); + + Serial.printf("\r\nCurrently running %s @ 0x%08x.\r\n", partition.name().c_str(), partition.address()); + + } + +4. If needed you can also create your own instance of the of OtaUpgrader as shown below:: + + + // Call when IP address has been obtained + void onIp(IpAddress ip, IpAddress mask, IpAddress gateway) + { + // ... + + OtaUpgrader ota; + + auto part = ota.getNextBootPartition(); + + ota.begin(part); + + // ... write all the data to the partition + + ota.end(); + + // ... + } + +See the :sample:`Upgrade` sample application. + +API Documentation +----------------- + +.. doxygennamespace:: Ota + :members: + diff --git a/Sming/Components/Ota/component.mk b/Sming/Components/Ota/component.mk new file mode 100644 index 0000000000..1303f4fdc5 --- /dev/null +++ b/Sming/Components/Ota/component.mk @@ -0,0 +1,16 @@ +COMPONENT_ARCH := $(SMING_ARCH) +ifeq ($(COMPONENT_ARCH),Host) + COMPONENT_ARCH := Esp8266 +endif + +COMPONENT_SRCDIRS := \ + src \ + src/Arch/$(COMPONENT_ARCH) + +COMPONENT_INCDIRS := \ + src/include \ + src/Arch/$(COMPONENT_ARCH)/include + +ifeq ($(COMPONENT_ARCH),Esp8266) + COMPONENT_DEPENDS += rboot +endif \ No newline at end of file diff --git a/Sming/Components/Ota/src/.cs b/Sming/Components/Ota/src/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Components/Ota/src/Arch/Esp32/IdfUpgrader.cpp b/Sming/Components/Ota/src/Arch/Esp32/IdfUpgrader.cpp new file mode 100644 index 0000000000..39af014010 --- /dev/null +++ b/Sming/Components/Ota/src/Arch/Esp32/IdfUpgrader.cpp @@ -0,0 +1,47 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * IdfUpgrader.cpp + * + ****/ + +#include "include/Ota/IdfUpgrader.h" + +namespace Ota +{ +bool IdfUpgrader::begin(Partition partition, size_t size) +{ + if(partition.size() < size) { + return false; // the requested size is too big... + } + + writtenSoFar = 0; + maxSize = size ?: partition.size(); + + esp_err_t result = esp_ota_begin(convertToIdfPartition(partition), maxSize, &handle); + + return result == ESP_OK; +} + +size_t IdfUpgrader::write(const uint8_t* buffer, size_t size) +{ + if(writtenSoFar + size > maxSize) { + // cannot write more bytes than allowed + return 0; + } + + esp_err_t result = esp_ota_write(handle, buffer, size); + if(result != ESP_OK) { + // write failed + return 0; + } + + writtenSoFar += size; + + return size; +} + +} // namespace Ota diff --git a/Sming/Components/Ota/src/Arch/Esp32/include/Ota/IdfUpgrader.h b/Sming/Components/Ota/src/Arch/Esp32/include/Ota/IdfUpgrader.h new file mode 100644 index 0000000000..906354e45c --- /dev/null +++ b/Sming/Components/Ota/src/Arch/Esp32/include/Ota/IdfUpgrader.h @@ -0,0 +1,77 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * IdfUpgrader.h + * + * This header includes all unified Over-The-Air functions. + * +*/ + +#pragma once +#include +#include + +namespace Ota +{ +class IdfUpgrader : public UpgraderBase +{ +public: + bool begin(Partition partition, size_t size = 0) override; + size_t write(const uint8_t* buffer, size_t size) override; + + bool end() override + { + return esp_ota_end(handle) == ESP_OK; + } + + bool abort() override + { + return true; + } + + bool setBootPartition(Partition partition, bool save = true) override + { + if(!save) { + return false; + } + + return esp_ota_set_boot_partition(convertToIdfPartition(partition)) == ESP_OK; + } + + Partition getBootPartition() override + { + return convertFromIdfPartition(esp_ota_get_boot_partition()); + } + + Partition getRunningPartition() override + { + return convertFromIdfPartition(esp_ota_get_running_partition()); + } + + Partition getNextBootPartition(Partition startFrom = {}) override + { + const esp_partition_t* idfFrom = startFrom ? convertToIdfPartition(startFrom) : nullptr; + return convertFromIdfPartition(esp_ota_get_next_update_partition(idfFrom)); + } + + static const esp_partition_t* convertToIdfPartition(Partition partition) + { + return esp_partition_find_first(esp_partition_type_t(partition.type()), + esp_partition_subtype_t(partition.subType()), partition.name().c_str()); + } + + static Partition convertFromIdfPartition(const esp_partition_t* partition) + { + return partition ? Storage::findPartition(String(partition->label)) : Partition{}; + } + +private: + size_t maxSize{0}; + size_t writtenSoFar{0}; + esp_ota_handle_t handle{}; +}; + +} // namespace Ota diff --git a/Sming/Components/Ota/src/Arch/Esp32/include/Ota/Upgrader.h b/Sming/Components/Ota/src/Arch/Esp32/include/Ota/Upgrader.h new file mode 100644 index 0000000000..c2d002c732 --- /dev/null +++ b/Sming/Components/Ota/src/Arch/Esp32/include/Ota/Upgrader.h @@ -0,0 +1,16 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Upgrader.h + * + * This header includes all unified Over-The-Air functions. + * +*/ + +#pragma once +#include "IdfUpgrader.h" + +using OtaUpgrader = Ota::IdfUpgrader; diff --git a/Sming/Components/Ota/src/Arch/Esp8266/RbootUpgrader.cpp b/Sming/Components/Ota/src/Arch/Esp8266/RbootUpgrader.cpp new file mode 100644 index 0000000000..50bbe5b372 --- /dev/null +++ b/Sming/Components/Ota/src/Arch/Esp8266/RbootUpgrader.cpp @@ -0,0 +1,75 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * RbootUpgrader.cpp + * + ****/ + +#include "include/Ota/RbootUpgrader.h" + +using namespace Storage; + +namespace Ota +{ +bool RbootUpgrader::begin(Partition partition, size_t size) +{ + if(partition.size() < size) { + return false; // the requested size is too big... + } + + status = rboot_write_init(partition.address()); + + maxSize = size ?: partition.size(); + + writtenSoFar = 0; + + return true; +} + +size_t RbootUpgrader::write(const uint8_t* buffer, size_t size) +{ + if(writtenSoFar + size > maxSize) { + // cannot write more bytes than allowed + return 0; + } + + if(!rboot_write_flash(&status, buffer, size)) { + // write failed + return 0; + } + + writtenSoFar += size; + + return size; +} + +bool RbootUpgrader::setBootPartition(Partition partition, bool save) +{ + uint8_t slot = getSlotForPartition(partition); + if(!save) { +#ifdef RBOOT_ENABLE_RTC + return rboot_set_temp_rom(slot); +#else + return false; +#endif + } + + return rboot_set_current_rom(slot); +} + +Partition RbootUpgrader::getRunningPartition() +{ + uint8_t slot = rboot_get_current_rom(); +#ifdef RBOOT_ENABLE_RTC + rboot_rtc_data rtc; + if(rboot_get_rtc_data(&rtc) && rtc.last_mode == MODE_TEMP_ROM) { + slot = rtc.last_rom; + } +#endif + return getPartitionForSlot(slot); +} + +} // namespace Ota diff --git a/Sming/Components/Ota/src/Arch/Esp8266/include/Ota/RbootUpgrader.h b/Sming/Components/Ota/src/Arch/Esp8266/include/Ota/RbootUpgrader.h new file mode 100644 index 0000000000..ecf42af05c --- /dev/null +++ b/Sming/Components/Ota/src/Arch/Esp8266/include/Ota/RbootUpgrader.h @@ -0,0 +1,67 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Ota.h + * + * This header includes all unified Over-The-Air functions. + * +*/ + +#pragma once +#include +#include + +namespace Ota +{ +class RbootUpgrader : public UpgraderBase +{ +public: + /** + * @brief Prepare the partition for + */ + bool begin(Partition partition, size_t size = 0) override; + size_t write(const uint8_t* buffer, size_t size) override; + + bool end() override + { + return rboot_write_end(&status); + } + + bool setBootPartition(Partition partition, bool save = true) override; + + Partition getBootPartition() override + { + return getPartitionForSlot(rboot_get_current_rom()); + } + + Partition getRunningPartition() override; + + Partition getNextBootPartition(Partition startFrom = {}) override + { + uint8_t currentSlot = rboot_get_current_rom(); + if(startFrom) { + currentSlot = getSlotForPartition(startFrom); + } + return getPartitionForSlot(currentSlot ? 0 : 1); + } + + static uint8_t getSlotForPartition(Partition partition) + { + return (partition.subType() == uint8_t(Partition::SubType::App::ota1)) ? 1 : 0; + } + + static Partition getPartitionForSlot(uint8_t slot) + { + return Storage::spiFlash->partitions().findOta(slot); + } + +private: + rboot_write_status status{}; + size_t maxSize{0}; + size_t writtenSoFar{0}; +}; + +} // namespace Ota diff --git a/Sming/Components/Ota/src/Arch/Esp8266/include/Ota/Upgrader.h b/Sming/Components/Ota/src/Arch/Esp8266/include/Ota/Upgrader.h new file mode 100644 index 0000000000..5ceee81425 --- /dev/null +++ b/Sming/Components/Ota/src/Arch/Esp8266/include/Ota/Upgrader.h @@ -0,0 +1,16 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Upgrader.h + * + * This header includes all unified Over-The-Air functions. + * +*/ + +#pragma once +#include "RbootUpgrader.h" + +using OtaUpgrader = Ota::RbootUpgrader; diff --git a/Sming/Components/Ota/src/Manager.cpp b/Sming/Components/Ota/src/Manager.cpp new file mode 100644 index 0000000000..d04ade81d3 --- /dev/null +++ b/Sming/Components/Ota/src/Manager.cpp @@ -0,0 +1,4 @@ +#include "include/Ota/Manager.h" + +/* Global Instance of the OtaManager */ +OtaUpgrader OtaManager; diff --git a/Sming/Components/Ota/src/UpgradeOutputStream.cpp b/Sming/Components/Ota/src/UpgradeOutputStream.cpp new file mode 100644 index 0000000000..f84e348ed3 --- /dev/null +++ b/Sming/Components/Ota/src/UpgradeOutputStream.cpp @@ -0,0 +1,60 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * UpgradeOutputStream.cpp + * + * + */ + +#include "include/Ota/UpgradeOutputStream.h" + +namespace Ota +{ +bool UpgradeOutputStream::init() +{ + bool success = ota.begin(partition, maxLength); + initialized = true; + + return success; +} + +size_t UpgradeOutputStream::write(const uint8_t* data, size_t size) +{ + if(!initialized && size > 0) { + if(!init()) { // unable to initialize + return 0; + } + + initialized = true; + } + + if(written + size > maxLength) { + debug_e("The ROM size is bigger than the maximum allowed"); + return 0; + } + + if(!ota.write(data, size)) { + debug_e("ota_write_flash: Failed. Size: %d", size); + return 0; + } + + written += size; + + debug_d("ota_write_flash: item.size: %d", written); + + return size; +} + +bool UpgradeOutputStream::close() +{ + if(initialized) { + return ota.end(); + } + + return true; +} + +} // namespace Ota diff --git a/Sming/Components/Ota/src/include/Ota/Manager.h b/Sming/Components/Ota/src/include/Ota/Manager.h new file mode 100644 index 0000000000..9e6c1f4114 --- /dev/null +++ b/Sming/Components/Ota/src/include/Ota/Manager.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +/** @brief Global instance of Over-The-Air manager +*/ +extern OtaUpgrader OtaManager; diff --git a/Sming/Components/Ota/src/include/Ota/UpgradeOutputStream.h b/Sming/Components/Ota/src/include/Ota/UpgradeOutputStream.h new file mode 100644 index 0000000000..fa3a6157c3 --- /dev/null +++ b/Sming/Components/Ota/src/include/Ota/UpgradeOutputStream.h @@ -0,0 +1,92 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * UpgradeOutputStream.h + * + * +*/ + +#pragma once + +#include +#include +#include + +namespace Ota +{ +/** + * @brief Write-only stream type used during firmware upgrade + */ +class UpgradeOutputStream : public ReadWriteStream +{ +public: + using Partition = Storage::Partition; + + /** + * @brief Construct a stream for the given partition + * @param partition + */ + UpgradeOutputStream(Partition partition, size_t maxLength = 0) + : partition(partition), maxLength(maxLength != 0 ? std::min(maxLength, partition.size()) : partition.size()) + { + } + + virtual ~UpgradeOutputStream() + { + close(); + } + + size_t write(const uint8_t* data, size_t size) override; + + StreamType getStreamType() const override + { + return eSST_File; + } + + uint16_t readMemoryBlock(char* data, int bufSize) override + { + return 0; + } + + bool seek(int len) override + { + return false; + } + + int available() override + { + return written; + } + + bool isFinished() override + { + return true; + } + + virtual bool close(); + + size_t getStartAddress() const + { + return partition.address(); + } + + size_t getMaxLength() const + { + return maxLength; + } + +protected: + OtaUpgrader ota; + Partition partition; + bool initialized{false}; + size_t written{0}; // << the number of written bytes + size_t maxLength{0}; // << maximum allowed length + +protected: + virtual bool init(); +}; + +} // namespace Ota diff --git a/Sming/Components/Ota/src/include/Ota/UpgraderBase.h b/Sming/Components/Ota/src/include/Ota/UpgraderBase.h new file mode 100644 index 0000000000..ebfbf2781a --- /dev/null +++ b/Sming/Components/Ota/src/include/Ota/UpgraderBase.h @@ -0,0 +1,129 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Ota.h + * + * This header includes all unified Over-The-Air functions. + * +*/ + +#pragma once +#include +#include + +namespace Ota +{ +class UpgraderBase +{ +public: + static constexpr uint8_t SLOT_NONE{255}; + + using Partition = Storage::Partition; + + virtual ~UpgraderBase() + { + } + + /** + * @brief Prepares a partition for an upgrade. + * The preparation is bootloader and architecture dependant. + * @param partition + * @param size + * + * @retval bool + */ + virtual bool begin(Partition partition, size_t size = 0) = 0; + + /** + * @brief Writes chunk of data to the partition set in ``begin()``. + * @param buffer + * @param size + * + * @retval size_t actually written bytes + */ + virtual size_t write(const uint8_t* buffer, size_t size) = 0; + + /** + * @brief Finilizes the partition upgrade. + */ + virtual bool end() = 0; + + /** + * @brief Aborts a partition upgrade + */ + virtual bool abort() + { + return false; + } + + /** + * @brief Sets the default parition from where the application will be booted on next restart. + * @param partition + * @param save if true the change is persisted on the flash, otherwise it will be valid only for the next boot + * + * @retval bool + */ + virtual bool setBootPartition(Partition partition, bool save = true) = 0; + + /** + * @brief Gets information about the parition that is set as the default one to boot. + * @note The returned parition can be different than the current running partition. + * + * @retval partition + */ + virtual Partition getBootPartition() = 0; + + /** + * @brief Gets information about the parition from which the current application is running. + * @note The returned parition can be different than the default boot partition. + * + * @retval partition + */ + virtual Partition getRunningPartition() = 0; + + /** + * @brief Gets the next bootable partition that can be used after successful OTA upgrade + * @param startFrom - optional + * + * @retval partition + */ + virtual Partition getNextBootPartition(Partition startFrom = {}) = 0; + + /** + * @brief Gets information about all bootable partitions. + * + * @retval Storage::Iterator + */ + Storage::Iterator getBootPartitions() + { + return Storage::findPartition(Partition::Type::app); + } + + // utility functions + + /** + * @brief Gets slot number for a partition + * @param partition + * + * @retval uint8_t slot number + */ + uint8_t getSlot(Partition partition) + { + if(partition.type() != Partition::Type::app) { + return SLOT_NONE; + } + + using App = Partition::SubType::App; + auto subtype = App(partition.subType()); + if(subtype < App::ota_min || subtype > App::ota_max) { + return SLOT_NONE; + } + + return uint8_t(subtype) - uint8_t(App::ota_min); + } +}; + +} // namespace Ota diff --git a/Sming/Components/OtaNetwork/README.rst b/Sming/Components/OtaNetwork/README.rst new file mode 100644 index 0000000000..84f479d9cf --- /dev/null +++ b/Sming/Components/OtaNetwork/README.rst @@ -0,0 +1,61 @@ +Over-The-Air(OTA) Network Upgrader +================================== + +.. highlight:: c++ + +Introduction +------------ + +This architecture-agnostic component adds support for Over-The-Air upgrades. + +Usage +----- +1. Add ``COMPONENT_DEPENDS += Ota`` to your application componenent.mk file. +2. Add these lines to your application:: + + #include + +After that you will have access to a global ``OtaManager`` instance that can be used to manage your OTA upgrade process. + +3. You can use ``OtaManager`` to get information about the bootable partitions and update them. + The code below will display the current bootable and running partition:: + + void init() + { + + // ... + auto partition = OtaManager.getRunningPartition(); + + Serial.printf("\r\nCurrently running %s @ 0x%08x.\r\n", partition.name().c_str(), partition.address()); + + } + +4. If needed you can also create your own instance of the of OtaUpgrader as shown below:: + + + // Call when IP address has been obtained + void onIp(IpAddress ip, IpAddress mask, IpAddress gateway) + { + // ... + + OtaUpgrader ota; + + auto partition = ota.getNextBootPartition(); + + ota.begin(partition); + + // ... write all the data to the partition + + ota.end(); + + // ... + } + +See the :sample:`Upgrade` sample application. + +API Documentation +----------------- + +.. doxygennamespace:: Ota + :members: + diff --git a/Sming/Components/OtaNetwork/component.mk b/Sming/Components/OtaNetwork/component.mk new file mode 100644 index 0000000000..176580747f --- /dev/null +++ b/Sming/Components/OtaNetwork/component.mk @@ -0,0 +1,4 @@ +COMPONENT_SRCDIRS := src +COMPONENT_INCDIRS := src/include +COMPONENT_DEPENDS := Ota Network + diff --git a/Sming/Components/OtaNetwork/src/.cs b/Sming/Components/OtaNetwork/src/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Components/OtaNetwork/src/HttpUpgrader.cpp b/Sming/Components/OtaNetwork/src/HttpUpgrader.cpp new file mode 100644 index 0000000000..90b893391f --- /dev/null +++ b/Sming/Components/OtaNetwork/src/HttpUpgrader.cpp @@ -0,0 +1,120 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * HttpUpdater.cpp + * + */ + +#include "include/Ota/Network/HttpUpgrader.h" +#include + +namespace Ota +{ +namespace Network +{ +void HttpUpgrader::start() +{ + for(unsigned i = 0; i < items.count(); i++) { + auto& it = items[i]; + debug_d("Download file:\r\n" + " (%u) %s -> %s @ 0x%X", + currentItem, it.url.c_str(), it.partition.name().c_str(), it.partition.address()); + + HttpRequest* request; + if(baseRequest != nullptr) { + request = baseRequest->clone(); + request->setURL(it.url); + } else { + request = new HttpRequest(it.url); + } + + request->setMethod(HTTP_GET); + request->setResponseStream(it.getStream()); + + if(i == items.count() - 1) { + request->onRequestComplete(RequestCompletedDelegate(&HttpUpgrader::updateComplete, this)); + } else { + request->onRequestComplete(RequestCompletedDelegate(&HttpUpgrader::itemComplete, this)); + } + + if(!send(request)) { + debug_e("ERROR: Rejected sending new request."); + break; + } + } +} + +int HttpUpgrader::itemComplete(HttpConnection& client, bool success) +{ + if(!success) { + updateFailed(); + return -1; + } + + auto& it = items[currentItem]; + debug_d("Finished: URL: %s, Offset: 0x%X, Length: %u", it.url.c_str(), it.partition.address(), + it.stream->available()); + + it.size = it.stream->available(); + it.stream = nullptr; // the actual deletion will happen outside of this class + currentItem++; + + return 0; +} + +int HttpUpgrader::updateComplete(HttpConnection& client, bool success) +{ + int hasError = itemComplete(client, success); + if(hasError != 0) { + return hasError; + } + + debug_d("\r\nFirmware download finished!"); + for(unsigned i = 0; i < items.count(); i++) { + debug_d(" - item: %u, addr: 0x%X, url: %s", i, items[i].partition.address(), items[i].url.c_str()); + } + + if(!success) { + updateFailed(); + return -1; + } + + if(updateDelegate) { + updateDelegate(*this, true); + } + + applyUpdate(); + + return 0; +} + +void HttpUpgrader::updateFailed() +{ + debug_e("\r\nFirmware download failed.."); + if(updateDelegate) { + updateDelegate(*this, false); + } + items.clear(); +} + +void HttpUpgrader::applyUpdate() +{ + items.clear(); + if(romSlot == NO_ROM_SWITCH) { + debug_d("Firmware updated."); + return; + } + + // set to boot new rom and then reboot + debug_d("Firmware updated, rebooting to rom %u...\r\n", romSlot); + + OtaManager.setBootPartition(items[romSlot].partition); + System.restart(); +} + +} // namespace Network + +} // namespace Ota diff --git a/Sming/Components/OtaNetwork/src/include/Ota/Network/HttpUpgrader.h b/Sming/Components/OtaNetwork/src/include/Ota/Network/HttpUpgrader.h new file mode 100644 index 0000000000..40816b6491 --- /dev/null +++ b/Sming/Components/OtaNetwork/src/include/Ota/Network/HttpUpgrader.h @@ -0,0 +1,156 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * HttpUpgrader.h + * + * Created on: 2015/09/03. + * Author: Richard A Burton & Anakod + * + * Modified: 2017, 2021 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include +#include + +namespace Ota +{ +namespace Network +{ +/** + * @brief Magic value for ROM slot indicating slot won't change after successful OTA + */ +constexpr uint8_t NO_ROM_SWITCH{0xff}; + +class HttpUpgrader : protected HttpClient +{ +public: + using CompletedDelegate = Delegate; + using Partition = Storage::Partition; + + struct Item { + String url; + Partition partition; // << partition to write the data to + size_t size{0}; // << actual size of written bytes + ReadWriteStream* stream{nullptr}; // (optional) output stream to use. + + Item(String url, Partition partition, ReadWriteStream* stream) : url(url), partition(partition), stream(stream) + { + } + + ~Item() + { + delete stream; + } + + ReadWriteStream* getStream() + { + if(stream == nullptr) { + stream = new Ota::UpgradeOutputStream(partition); + } + return stream; + } + }; + + class ItemList : public Vector + { + public: + bool addNew(Item* it) + { + if(addElement(it)) { + return true; + } + delete it; + return false; + } + }; + + /** + * @brief Add an item to update + * @param firmwareFileUrl + * @param partition Target partition to write + * @param stream + * + * @retval bool + */ + bool addItem(const String& firmwareFileUrl, Partition partition, ReadWriteStream* stream = nullptr) + { + return items.addNew(new Item{firmwareFileUrl, partition, stream}); + } + + void start(); + + /** + * @brief On completion, switch to the given ROM slot + * @param romSlot specify NO_ROM_SWITCH (the default) to cancel any previously set switch + */ + void switchToRom(uint8_t romSlot) + { + this->romSlot = romSlot; + } + + void setCallback(CompletedDelegate reqUpdateDelegate) + { + setDelegate(reqUpdateDelegate); + } + + void setDelegate(CompletedDelegate reqUpdateDelegate) + { + this->updateDelegate = reqUpdateDelegate; + } + + /** + * @brief Sets the base request that can be used to pass + * + * - default request parameters, like request headers... + * - default SSL options + * - default SSL fingeprints + * - default SSL client certificates + * + * @param request + */ + void setBaseRequest(HttpRequest* request) + { + baseRequest = request; + } + + /** + * @brief Allow reading items + * @deprecated Access list directly using `getItems()` + */ + const Item& getItem(unsigned int index) const SMING_DEPRECATED + { + return items[index]; + } + + /** + * @brief Allow read access to item list + */ + const ItemList& getItems() const + { + return items; + } + +protected: + void applyUpdate(); + void updateFailed(); + + virtual int itemComplete(HttpConnection& client, bool success); + virtual int updateComplete(HttpConnection& client, bool success); + +protected: + ItemList items; + CompletedDelegate updateDelegate; + HttpRequest* baseRequest{nullptr}; + uint8_t romSlot{NO_ROM_SWITCH}; + uint8_t currentItem{0}; +}; + +} // namespace Network + +} // namespace Ota diff --git a/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp b/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp index af28c04518..451936f54f 100644 --- a/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp +++ b/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp @@ -10,10 +10,8 @@ #include "BasicStream.h" #include -#include #include #include -#include extern "C" uint32 user_rf_cal_sector_set(void); @@ -27,14 +25,8 @@ DECLARE_FSTR_ARRAY(AppFlashRegionOffsets, uint32_t); BasicStream::Slot::Slot() { - // Get parameters of the slot where the firmware image should be stored. - uint8_t currentSlot = rboot_get_current_rom(); - index = (currentSlot == 0) ? 1 : 0; - // Lookup slot details from partition table - auto part = Storage::spiFlash->partitions().findOta(index); - address = part.address(); - size = part.size(); + partition = OtaManager.getNextBootPartition(); } BasicStream::BasicStream() @@ -85,11 +77,12 @@ void BasicStream::nextRom() void BasicStream::processRomHeader() { - bool addressMatch = (slot.address & 0xFFFFF) == (romHeader.address & 0xFFFFF); + bool addressMatch = (slot.partition.address() & 0xFFFFF) == (romHeader.address & 0xFFFFF); if(!slot.updated && addressMatch) { - if(romHeader.size <= slot.size) { - debug_i("Update slot %u [0x%08X..0x%08X)", slot.index, slot.address, slot.address + romHeader.size); - rbootWriteStatus = rboot_write_init(slot.address); + if(romHeader.size <= slot.partition.size()) { + debug_i("Update slot %s [0x%08X..0x%08X)", slot.partition.name().c_str(), slot.partition.address(), + slot.partition.address() + romHeader.size); + ota.begin(slot.partition); setupChunk(State::WriteRom, romHeader.size); } else { setError(Error::RomTooLarge); @@ -110,7 +103,7 @@ void BasicStream::verifyRoms() if(!verifier.verify(verificationData)) { if(slot.updated) { // Destroy start sector of updated ROM to avoid accidentally booting an unsanctioned firmware - flashmem_erase_sector(slot.address / SECTOR_SIZE); + Storage::spiFlash->erase_range(slot.partition.address(), Storage::spiFlash->getBlockSize()); } setError(Error::VerificationFailed); return; @@ -127,8 +120,8 @@ void BasicStream::verifyRoms() return; } - if(rboot_set_current_rom(slot.index)) { - debug_i("ROM %u activated", slot.index); + if(ota.setBootPartition(slot.partition)) { + debug_i("ROM %u activated", toLongString(slot.partition.type(), slot.partition.subType()).c_str()); } else { setError(Error::RomActivationFailed); } @@ -169,10 +162,10 @@ size_t BasicStream::write(const uint8_t* data, size_t size) break; case State::WriteRom: { - bool ok = rboot_write_flash(&rbootWriteStatus, data, std::min(remainingBytes, size)); + bool ok = ota.write(data, std::min(remainingBytes, size)); if(ok) { if(consume(data, size)) { - ok = slot.updated = rboot_write_end(&rbootWriteStatus); + ok = slot.updated = ota.end(); nextRom(); } } diff --git a/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.h b/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.h index 303431caae..aaa96251bc 100644 --- a/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.h +++ b/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.h @@ -11,7 +11,8 @@ #pragma once #include -#include +#include +#include #include "FileFormat.h" #ifdef ENABLE_OTA_SIGNING #include "SignatureVerifier.h" @@ -24,20 +25,20 @@ namespace OtaUpgrade /** * @brief A write-only stream to parse and apply firmware unencrypted upgrade files generated by otatool.py * - * The class fully automates the firmware upgrade process without any manual - * configuration. At construction time, the rBoot configuration is read to determine + * The class fully automates the firmware upgrade process without any manual + * configuration. At construction time, the rBoot configuration is read to determine * the unused ROM slot which should receive the upgrade. - * Just feed the upgrade file content into the `write()` method in arbitrarily - * sized chunks. The relevant portion(s) of the Flash memory (currently only the + * Just feed the upgrade file content into the `write()` method in arbitrarily + * sized chunks. The relevant portion(s) of the Flash memory (currently only the * application rom) are updated on the fly as data arrives. When the file is complete * and signature validation (if enabled) was successful, the updated slot is activated * in the rBoot configuration. - * Call `hasError()` and/or check the public \c #errorCode member to determine if + * Call `hasError()` and/or check the public \c #errorCode member to determine if * everything went smoothly. - * - * For further information on configuration options and the file format, + * + * For further information on configuration options and the file format, * refer to the library's documentation. - * + * * @see `EncryptedStream` for encryption support. */ class BasicStream : public ReadWriteStream @@ -111,15 +112,13 @@ class BasicStream : public ReadWriteStream /** Determine the parameters of the slot to receive the upgrade. */ Slot(); - uint32_t address; - uint32_t size; - uint8_t index; + Storage::Partition partition; bool updated{false}; }; Slot slot; // Instead of RbootOutputStream, the rboot write API is used directly because in a future extension the OTA file may contain data for multiple FLASH regions. - rboot_write_status rbootWriteStatus{}; + OtaUpgrader ota; enum class State { Error, @@ -165,16 +164,16 @@ class BasicStream : public ReadWriteStream * It "consumes" as much bytes as possible for completing the current chunk (file header, signature, etc.) and * updates \a data and \a size accordingly. Depending on `state`, this method also writes to flash memory and * performs incremental checksum/signature calculation. - * @return `true` if the current chunk is complete and ready for processing. + * @return `true` if the current chunk is complete and ready for processing. * It is the caller's responsibility to setup a new chunk, advance the state machine, etc. */ bool consume(const uint8_t*& data, size_t& size); - /** Called after completion of a single ROM image. + /** Called after completion of a single ROM image. * Sets up reception of next ROM image or the checksum/signature if this was the laste ROM image. */ void nextRom(); - /** Called after reception of an #OTA_RomHeader. + /** Called after reception of an #OTA_RomHeader. * Decides if the ROM fits the selected upgrade slot or must be ignored. */ void processRomHeader(); diff --git a/Sming/Libraries/OtaUpgrade/component.mk b/Sming/Libraries/OtaUpgrade/component.mk index d19bec3b07..50bc8e66bc 100644 --- a/Sming/Libraries/OtaUpgrade/component.mk +++ b/Sming/Libraries/OtaUpgrade/component.mk @@ -1,7 +1,7 @@ COMPONENT_SRCDIRS := COMPONENT_SRCFILES := OtaUpgrade/BasicStream.cpp COMPONENT_APPCODE := appcode -COMPONENT_DEPENDS := +COMPONENT_DEPENDS := Ota COMPONENT_INCDIRS := . diff --git a/Sming/Libraries/OtaUpgradeMqtt/component.mk b/Sming/Libraries/OtaUpgradeMqtt/component.mk index 7896ab0797..505288b2af 100644 --- a/Sming/Libraries/OtaUpgradeMqtt/component.mk +++ b/Sming/Libraries/OtaUpgradeMqtt/component.mk @@ -1,14 +1,15 @@ COMPONENT_SRCDIRS := -COMPONENT_SRCFILES := src/PayloadParser.cpp src/RbootPayloadParser.cpp +COMPONENT_SRCFILES := src/PayloadParser.cpp src/StandardPayloadParser.cpp COMPONENT_INCDIRS := src/include # If enabled (set to 1) then we can use all sofisticated mechanisms to upgrade the firmware using the ``OtaUpgrade`` library. COMPONENT_VARS := ENABLE_OTA_ADVANCED ENABLE_OTA_ADVANCED ?= 0 +COMPONENT_DEPENDS := Ota ifneq ($(ENABLE_OTA_ADVANCED),0) COMPONENT_SRCFILES += src/AdvancedPayloadParser.cpp - COMPONENT_DEPENDS := OtaUpgrade + COMPONENT_DEPENDS += OtaUpgrade endif # If enabled (set to 1) then we can use unlimited number of patch versions diff --git a/Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/app/application.cpp b/Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/app/application.cpp index d93860fc1a..37f8125182 100644 --- a/Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/app/application.cpp +++ b/Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/app/application.cpp @@ -1,7 +1,7 @@ #include -#include #include -#include +#include +#include #if ENABLE_OTA_ADVANCED #include @@ -28,15 +28,6 @@ IMPORT_FSTR(privateKeyData, PROJECT_DIR "/files/private.pem.key.der"); IMPORT_FSTR(certificateData, PROJECT_DIR "/files/certificate.pem.crt.der"); #endif -Storage::Partition findRomPartition(uint8_t slot) -{ - auto part = Storage::spiFlash->partitions().findOta(slot); - if(!part) { - debug_w("Rom slot %d not found", slot); - } - return part; -} - void otaUpdate() { if(mqtt.isProcessing()) { @@ -44,16 +35,9 @@ void otaUpdate() return; } - uint8 slot = rboot_get_current_rom(); - if(slot == 0) { - slot = 1; - } else { - slot = 0; - } - Serial.println("Checking for a new application firmware..."); - auto part = findRomPartition(slot); + auto part = OtaManager.getBootPartition(); if(!part) { Serial.println("FAILED: Cannot find application address"); return; @@ -92,7 +76,7 @@ void otaUpdate() * The command below uses class that stores the firmware directly * using RbootOutputStream on a location provided by us */ - auto parser = new OtaUpgrade::Mqtt::RbootPayloadParser(part, APP_VERSION_PATCH); + auto parser = new OtaUpgrade::Mqtt::StandardPayloadParser(part, APP_VERSION_PATCH); #endif mqtt.setPayloadParser([parser](MqttPayloadParserState& state, mqtt_message_t* message, const char* buffer, @@ -113,15 +97,20 @@ void showInfo() Serial.printf(_F("CPU Frequency: %d MHz\r\n"), system_get_cpu_freq()); Serial.printf(_F("System Chip ID: %x\r\n"), system_get_chip_id()); - rboot_config conf = rboot_get_config(); + int total = 0; + for(auto it = OtaManager.getBootPartitions(); it; ++it) { + auto part = *it; + debug_d("ROM %s: 0x%08x, SubType: %s", part.name().c_str(), part.address(), + toLongString(part.type(), part.subType()).c_str()); + total++; + } + debug_d("======================="); + debug_d("Bootable ROMs found: %d", total); - debug_d("Count: %d", conf.count); - debug_d("ROM 0: 0x%08x", conf.roms[0]); - debug_d("ROM 1: 0x%08x", conf.roms[1]); - debug_d("ROM 2: 0x%08x", conf.roms[2]); - debug_d("GPIO ROM: %d", conf.gpio_rom); + auto part = OtaManager.getRunningPartition(); - Serial.printf(_F("\r\nCurrently running rom %d. Application version: %s\r\n"), conf.current_rom, APP_VERSION); + Serial.printf(_F("\r\nCurrently running %s: 0x%08x. Application version: %s\r\n"), part.name().c_str(), + part.address(), APP_VERSION); Serial.println(); } diff --git a/Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/component.mk b/Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/component.mk index 806c2b46b6..7b087c1768 100644 --- a/Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/component.mk +++ b/Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/component.mk @@ -48,7 +48,7 @@ COMPONENT_DEPENDS := OtaUpgradeMqtt RBOOT_ENABLED := 1 ## Use standard hardware config with two ROM slots and two SPIFFS partitions -HWCONFIG := spiffs-two-roms +HWCONFIG := ota APP_CFLAGS = -DMQTT_URL="\"$(MQTT_URL)"\" \ -DMQTT_FINGERPRINT_SHA1=$(MQTT_FINGERPRINT_SHA1) \ @@ -59,4 +59,4 @@ APP_CFLAGS = -DMQTT_URL="\"$(MQTT_URL)"\" \ ifneq ($(APP_VERSION),) APP_CFLAGS += -DAPP_VERSION="\"$(APP_VERSION)"\" \ -DAPP_VERSION_PATCH=$(APP_VERSION_PATCH) -endif \ No newline at end of file +endif diff --git a/Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/ota.hw b/Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/ota.hw new file mode 100644 index 0000000000..c5d71daa72 --- /dev/null +++ b/Sming/Libraries/OtaUpgradeMqtt/samples/Upgrade/ota.hw @@ -0,0 +1,11 @@ +{ + "base_config": "spiffs-two-roms", + "partitions": { + "rom0": { + "subtype": "ota_0" + }, + "rom1": { + "subtype": "ota_1" + } + } +} \ No newline at end of file diff --git a/Sming/Libraries/OtaUpgradeMqtt/src/RbootPayloadParser.cpp b/Sming/Libraries/OtaUpgradeMqtt/src/RbootPayloadParser.cpp deleted file mode 100644 index b18a8fb151..0000000000 --- a/Sming/Libraries/OtaUpgradeMqtt/src/RbootPayloadParser.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * RbootPayloadParser.cpp - * - * Created: 2021 - Slavey Karadzhov - * - ****/ - -#include "include/OtaUpgrade/Mqtt/RbootPayloadParser.h" - -namespace OtaUpgrade -{ -namespace Mqtt -{ -bool RbootPayloadParser::switchRom(const UpdateState& updateState) -{ - uint8_t before = rboot_get_current_rom(); - uint8_t after = (before == 0) ? 1 : 0; - - debug_d("Swapping from rom %u to rom %u.\r\n", before, after); - - return rboot_set_current_rom(after); -} - -ReadWriteStream* RbootPayloadParser::getStorageStream(size_t storageSize) -{ - if(storageSize > part.size()) { - debug_e("The new rom is too big to fit!"); - return nullptr; - } - - return new RbootOutputStream(part.address(), part.size()); -} - -} // namespace Mqtt -} // namespace OtaUpgrade diff --git a/Sming/Libraries/OtaUpgradeMqtt/src/StandardPayloadParser.cpp b/Sming/Libraries/OtaUpgradeMqtt/src/StandardPayloadParser.cpp new file mode 100644 index 0000000000..f312423442 --- /dev/null +++ b/Sming/Libraries/OtaUpgradeMqtt/src/StandardPayloadParser.cpp @@ -0,0 +1,43 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * StandardPayloadParser.cpp + * + * Created: 2021 - Slavey Karadzhov + * + ****/ + +#include "include/OtaUpgrade/Mqtt/StandardPayloadParser.h" +#include +#include + +namespace OtaUpgrade +{ +namespace Mqtt +{ +bool StandardPayloadParser::switchRom(const UpdateState& updateState) +{ + auto before = OtaManager.getBootPartition(); + auto after = OtaManager.getNextBootPartition(); + + debug_d("Swapping from %s @ 0x%08x to %s @ 0x%08x.\r\n", before.name().c_str(), before.address(), + after.name().c_str(), after.address()); + + return OtaManager.setBootPartition(after); +} + +ReadWriteStream* StandardPayloadParser::getStorageStream(size_t storageSize) +{ + if(storageSize > part.size()) { + debug_e("The new rom is too big to fit!"); + return nullptr; + } + + return new Ota::UpgradeOutputStream(part, storageSize); +} + +} // namespace Mqtt +} // namespace OtaUpgrade diff --git a/Sming/Libraries/OtaUpgradeMqtt/src/include/OtaUpgrade/Mqtt/RbootPayloadParser.h b/Sming/Libraries/OtaUpgradeMqtt/src/include/OtaUpgrade/Mqtt/RbootPayloadParser.h index 68e87dd53a..c3d53498fa 100644 --- a/Sming/Libraries/OtaUpgradeMqtt/src/include/OtaUpgrade/Mqtt/RbootPayloadParser.h +++ b/Sming/Libraries/OtaUpgradeMqtt/src/include/OtaUpgrade/Mqtt/RbootPayloadParser.h @@ -12,32 +12,14 @@ #pragma once -#include "PayloadParser.h" -#include +#include "StandardPayloadParser.h" namespace OtaUpgrade { namespace Mqtt { -/** - * @brief This parser allows the processing of firmware data that is directly stored - * to the flash memory using RbootOutputStream. - */ -class RbootPayloadParser : public PayloadParser -{ -public: - RbootPayloadParser(Storage::Partition part, size_t currentVersion, size_t allowedVersionBytes = 24) - : PayloadParser(currentVersion, allowedVersionBytes), part(part) - { - } - - bool switchRom(const UpdateState& updateState) override; - - ReadWriteStream* getStorageStream(size_t storageSize) override; - -private: - Storage::Partition part; -}; +/** @deprecated Use `StandardPayloadParser` */ +using RbootPayloadParser = StandardPayloadParser SMING_DEPRECATED; } // namespace Mqtt } // namespace OtaUpgrade diff --git a/Sming/Libraries/OtaUpgradeMqtt/src/include/OtaUpgrade/Mqtt/StandardPayloadParser.h b/Sming/Libraries/OtaUpgradeMqtt/src/include/OtaUpgrade/Mqtt/StandardPayloadParser.h new file mode 100644 index 0000000000..e6babbc027 --- /dev/null +++ b/Sming/Libraries/OtaUpgradeMqtt/src/include/OtaUpgrade/Mqtt/StandardPayloadParser.h @@ -0,0 +1,43 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * StandardPayloadParser.h + * + * Created: 2021 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include "PayloadParser.h" +#include + +namespace OtaUpgrade +{ +namespace Mqtt +{ +/** + * @brief This parser allows the processing of firmware data that is directly stored + * to the flash memory using RbootOutputStream. + */ +class StandardPayloadParser : public PayloadParser +{ +public: + StandardPayloadParser(Storage::Partition part, size_t currentVersion, size_t allowedVersionBytes = 24) + : PayloadParser(currentVersion, allowedVersionBytes), part(part) + { + } + + bool switchRom(const UpdateState& updateState) override; + + ReadWriteStream* getStorageStream(size_t storageSize) override; + +private: + Storage::Partition part; +}; + +} // namespace Mqtt +} // namespace OtaUpgrade diff --git a/docs/source/getting-started/index.rst b/docs/source/getting-started/index.rst index 9de62a7292..3f8e4e04d5 100644 --- a/docs/source/getting-started/index.rst +++ b/docs/source/getting-started/index.rst @@ -47,7 +47,7 @@ The examples are a great way to learn the API and brush up your C/C++ knowledge. - `Connect to WiFi <#connect-to-wifi>`__ - `Read DHT22 sensor <#read-dht22-sensor>`__ - `HTTP client <#http-client>`__ -- `OTA application update based on rBoot <#ota-application-update-based-on-rboot>`__ +- `OTA application update based on rBoot <#ota-application-update>`__ - `Embedded HTTP Web Server <#embedded-http-web-server>`__ - `Email Client <#email-client>`__ @@ -140,46 +140,33 @@ For more examples take a look at the :sample:`HttpClient_Instapush` and :sample:`HttpClient_ThingSpeak` samples. -OTA application update based on rBoot -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +OTA application update +~~~~~~~~~~~~~~~~~~~~~~ :: - void OtaUpdate() + void doUpgrade() { - uint8 slot; - rboot_config bootconf; - - Serial.println("Updating..."); - // need a clean object, otherwise if run before and failed will not run again - if (otaUpdater) { - delete otaUpdater; + if(otaUpdater) { + delete otaUpdater; } + otaUpdater = new Ota::Network::HttpUpgrader(); - otaUpdater = new RbootHttpUpdater(); - - // select rom slot to flash - bootconf = rboot_get_config(); - slot = bootconf.current_rom; - if (slot == 0) { - slot = 1; - } - else { - slot = 0; - } + // select rom partition to flash + auto part = ota.getNextBootPartition(); - // flash rom to position indicated in the rBoot config rom table - otaUpdater->addItem(bootconf.roms[slot], ROM_0_URL); +   // The content located on ROM_0_URL will be stored to the new partition +   otaUpdater->addItem(ROM_0_URL, part); - // and/or set a callback (called on failure or success without switching requested) - otaUpdater->setCallback(OtaUpdate_CallBack); +   // and/or set a callback (called on failure or success without switching requested) +   otaUpdater->setCallback(upgradeCallback); - // start update - otaUpdater->start(); +   // start update +   otaUpdater->start(); } -For a complete example take a look at the :sample:`Basic_rBoot` sample. +For a complete example take a look at the :sample:`Basic_Ota` sample. Embedded HTTP Web Server ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/samples/Basic_rBoot/.cproject b/samples/Basic_Ota/.cproject similarity index 100% rename from samples/Basic_rBoot/.cproject rename to samples/Basic_Ota/.cproject diff --git a/samples/Basic_rBoot/.project b/samples/Basic_Ota/.project similarity index 96% rename from samples/Basic_rBoot/.project rename to samples/Basic_Ota/.project index 6160ca7119..2d511edc6e 100644 --- a/samples/Basic_rBoot/.project +++ b/samples/Basic_Ota/.project @@ -1,6 +1,6 @@ - Basic_rBoot + Basic_Ota SmingFramework diff --git a/samples/Basic_rBoot/Makefile b/samples/Basic_Ota/Makefile similarity index 100% rename from samples/Basic_rBoot/Makefile rename to samples/Basic_Ota/Makefile diff --git a/samples/Basic_Ota/README.rst b/samples/Basic_Ota/README.rst new file mode 100644 index 0000000000..c099370144 --- /dev/null +++ b/samples/Basic_Ota/README.rst @@ -0,0 +1,99 @@ +Basic Ota +========= + +.. highlight:: bash + +Introduction +------------ + +This sample integrates :component:`Ota`, :component:`OtaNetwork` and Sming. +It demonstrates dual rom booting, big flash support, OTA updates and dual spiffs filesystems. +This sample should work on all supported architectures. + +Esp8266 +~~~~~~~ +On Esp8266 we use rBoot as bootloader. +When using rBoot big flash support with multiple 1MB slots only one rom +image needs to be created. If you don’t want to use big flash support +(e.g. for a device with smaller flash) see the separate instructions +below. You can easily take the ota files and add them to your own +project to add OTA support. + +Building +-------- + +1) Set :envvar:`WIFI_SSID` & :envvar:`WIFI_PWD` environment variables with your wifi details. +2) Edit the OTA server details defined in the application ``component.mk`` file. +3) ``make && make flash`` +4) Put *rom0.bin* and *spiff_rom.bin* in the root of your webserver for OTA. +5) Interact with the sample using a terminal (``make terminal``). Sorry - no web-gui (yet). + + +Testing +------- + +For testing purposes we provide an Ota server that can be started on your desktop machine:: + + make otaserver + +The server listens on port 9999 and all network interfaces. If your desktop has the following IP address ``192.168.1.30`` +after connecting to your WIFI router then you can compile the sample to use this IP address and the testing OTA server:: + + make ROM_0_URL=http://192.168.1.30:9999/rom0.bin SPIFFS_URL=http://192.168.1.30:9999/spiff_rom.bin + make flash + +Make sure to replace ``192.168.1.30`` with your WIFI IP address. + +Flash layout considerations +--------------------------- + +Esp8266 +~~~~~~~ +If you want to use, for example, two 512k roms in the first 1MB block of +flash (old style) then Sming will automatically create two separately linked +roms. If you are flashing a single rom to multiple 1MB flash blocks, all using +the same offset inside their 1MB blocks, only a single rom is created. +See :component:`rboot` for further details. + +- If using a very small flash (e.g. 512k) there may be no room for a + spiffs fileystem, so use *HWCONFIG = standard* +- After building copy all the rom*.bin files to the root of your web + server. + +If you want more than two roms you must be an advanced user and should +be able to work out what to copy and edit to acheive this! + +Configuration +------------- +.. envvar:: RBOOT_TWO_ROMS + + Default: 1 (enabled) + + Allows specifying two different URLs for ROM0 and ROM1. + + If not set then only the URL defined in ROM_0_URL will be used. + +.. envvar:: ROM_0_URL + + Default: http://192.168.7.5:80/rom0.bin + + The URL where the firmware for the first application partition can be downloaded. + +.. envvar:: ROM_1_URL + + Default: http://192.168.7.5:80/rom1.bin + + Used when ``RBOOT_TWO_ROMS`` is set. The URL where the firmware for the second application partition can be downloaded. + +.. envvar:: SPIFFS_URL + + Default: http://192.168.7.5:80/spiff_rom.bin + + The URL where the spiffs partition attached can be downloaded. + + +Credits +------- + +The initial sample was made possible with the assistance of piperpilot, +gschmott and robotiko on the esp8266.com forum. diff --git a/samples/Basic_rBoot/app/application.cpp b/samples/Basic_Ota/app/application.cpp similarity index 67% rename from samples/Basic_rBoot/app/application.cpp rename to samples/Basic_Ota/app/application.cpp index e53a3a84fc..dbeabcaf06 100644 --- a/samples/Basic_rBoot/app/application.cpp +++ b/samples/Basic_Ota/app/application.cpp @@ -1,11 +1,8 @@ #include -#include +#include +#include #include - -// download urls, set appropriately -#define ROM_0_URL "http://192.168.7.5:80/rom0.bin" -#define ROM_1_URL "http://192.168.7.5:80/rom1.bin" -#define SPIFFS_URL "http://192.168.7.5:80/spiff_rom.bin" +#include // If you want, you can define WiFi settings globally in Eclipse Environment Variables #ifndef WIFI_SSID @@ -13,13 +10,14 @@ #define WIFI_PWD "PleaseEnterPass" #endif -RbootHttpUpdater* otaUpdater; +Ota::Network::HttpUpgrader* otaUpdater; Storage::Partition spiffsPartition; +OtaUpgrader ota; -Storage::Partition findSpiffsPartition(uint8_t slot) +Storage::Partition findSpiffsPartition(Storage::Partition partition) { String name = F("spiffs"); - name += slot; + name += ota.getSlot(partition); auto part = Storage::findPartition(name); if(!part) { debug_w("Partition '%s' not found", name.c_str()); @@ -27,80 +25,71 @@ Storage::Partition findSpiffsPartition(uint8_t slot) return part; } -void otaUpdateCallBack(RbootHttpUpdater& client, bool result) +void upgradeCallback(Ota::Network::HttpUpgrader& client, bool result) { Serial.println("In callback..."); if(result == true) { // success - uint8 slot; - slot = rboot_get_current_rom(); - if(slot == 0) { - slot = 1; - } else { - slot = 0; - } + ota.end(); + + auto part = ota.getNextBootPartition(); // set to boot new rom and then reboot - Serial.printf("Firmware updated, rebooting to rom %d...\r\n", slot); - rboot_set_current_rom(slot); + Serial.printf("Firmware updated, rebooting to %s @ ...\r\n", part.name().c_str()); + ota.setBootPartition(part); System.restart(); } else { + ota.abort(); // fail Serial.println("Firmware update failed!"); } } -void OtaUpdate() +void doUpgrade() { - uint8 slot; - rboot_config bootconf; - Serial.println("Updating..."); // need a clean object, otherwise if run before and failed will not run again if(otaUpdater) { delete otaUpdater; } - otaUpdater = new RbootHttpUpdater(); + otaUpdater = new Ota::Network::HttpUpgrader(); // select rom slot to flash - bootconf = rboot_get_config(); - slot = bootconf.current_rom; - if(slot == 0) { - slot = 1; - } else { - slot = 0; - } + auto part = ota.getNextBootPartition(); #ifndef RBOOT_TWO_ROMS // flash rom to position indicated in the rBoot config rom table - otaUpdater->addItem(bootconf.roms[slot], ROM_0_URL); + otaUpdater->addItem(ROM_0_URL, part); #else // flash appropriate ROM - otaUpdater->addItem(bootconf.roms[slot], (slot == 0) ? ROM_0_URL : ROM_1_URL); + otaUpdater->addItem((ota.getSlot(part) == 0) ? ROM_0_URL : ROM_1_URL, part); #endif - auto part = findSpiffsPartition(slot); - if(part) { + ota.begin(part); + + auto spiffsPart = findSpiffsPartition(part); + if(spiffsPart) { // use user supplied values (defaults for 4mb flash in hardware config) - otaUpdater->addItem(part.address(), SPIFFS_URL, part.size()); + otaUpdater->addItem(SPIFFS_URL, spiffsPart, new Storage::PartitionStream(spiffsPart)); } // request switch and reboot on success //otaUpdater->switchToRom(slot); // and/or set a callback (called on failure or success without switching requested) - otaUpdater->setCallback(otaUpdateCallBack); + otaUpdater->setCallback(upgradeCallback); // start update otaUpdater->start(); } -void Switch() +void doSwitch() { - uint8_t before = rboot_get_current_rom(); - uint8_t after = (before == 0) ? 1 : 0; + auto before = ota.getRunningPartition(); + auto after = ota.getNextBootPartition(); - Serial.printf(_F("Swapping from rom %u to rom %u.\r\n"), before, after); - if(rboot_set_current_rom(after)) { + Serial.printf(_F("Swapping from %s @ 0x%08x to %s @ 0x%08x.\r\n"), before.name().c_str(), before.address(), + after.name().c_str(), after.address()); + if(ota.setBootPartition(after)) { Serial.println(F("Restarting...\r\n")); System.restart(); } else { @@ -108,7 +97,7 @@ void Switch() } } -void ShowInfo() +void showInfo() { Serial.printf("\r\nSDK: v%s\r\n", system_get_sdk_version()); Serial.printf("Free Heap: %d\r\n", system_get_free_heap_size()); @@ -117,14 +106,11 @@ void ShowInfo() Serial.printf("SPI Flash ID: %x\r\n", Storage::spiFlash->getId()); Serial.printf("SPI Flash Size: %x\r\n", Storage::spiFlash->getSize()); - rboot_config conf; - conf = rboot_get_config(); + auto before = ota.getRunningPartition(); + auto after = ota.getNextBootPartition(); - debugf("Count: %d", conf.count); - for(unsigned i = 0; i < MAX_ROMS; ++i) { - debugf("ROM %u: 0x%08x", i, conf.roms[i]); - } - debugf("GPIO ROM: %d", conf.gpio_rom); + Serial.printf(_F("Current %s @ 0x%08x, future %s @ 0x%08x\r\n"), before.name().c_str(), before.address(), + after.name().c_str(), after.address()); } void serialCallBack(Stream& stream, char arrivedChar, unsigned short availableCharsCount) @@ -147,12 +133,12 @@ void serialCallBack(Stream& stream, char arrivedChar, unsigned short availableCh } else if(!strcmp(str, "ip")) { Serial.print("ip: "); Serial.print(WifiStation.getIP()); - Serial.print("mac: "); + Serial.print(" mac: "); Serial.println(WifiStation.getMacAddress()); } else if(!strcmp(str, "ota")) { - OtaUpdate(); + doUpgrade(); } else if(!strcmp(str, "switch")) { - Switch(); + doSwitch(); } else if(!strcmp(str, "restart")) { System.restart(); } else if(!strcmp(str, "ls")) { @@ -178,17 +164,17 @@ void serialCallBack(Stream& stream, char arrivedChar, unsigned short availableCh Serial.println("Empty spiffs!"); } } else if(!strcmp(str, "info")) { - ShowInfo(); + showInfo(); } else if(!strcmp(str, "help")) { Serial.println(); Serial.println("available commands:"); Serial.println(" help - display this message"); Serial.println(" ip - show current ip address"); Serial.println(" connect - connect to wifi"); - Serial.println(" restart - restart the esp8266"); + Serial.println(" restart - restart the device"); Serial.println(" switch - switch to the other rom and reboot"); Serial.println(" ota - perform ota update, switch rom and reboot"); - Serial.println(" info - show esp8266 info"); + Serial.println(" info - show device info"); if(spiffsPartition) { Serial.println(" ls - list files in spiffs"); Serial.println(" cat - show first file in spiffs"); @@ -206,17 +192,17 @@ void init() Serial.systemDebugOutput(true); // Debug output to serial // mount spiffs - auto slot = rboot_get_current_rom(); - spiffsPartition = findSpiffsPartition(slot); + auto partition = ota.getRunningPartition(); + spiffsPartition = findSpiffsPartition(partition); if(spiffsPartition) { - debugf("trying to mount '%s' at 0x%08x, length %d", spiffsPartition.name().c_str(), spiffsPartition.address(), + debugf("trying to mount %s @ 0x%08x, length %d", spiffsPartition.name().c_str(), spiffsPartition.address(), spiffsPartition.size()); spiffs_mount(spiffsPartition); } WifiAccessPoint.enable(false); - Serial.printf("\r\nCurrently running rom %d.\r\n", slot); + Serial.printf("\r\nCurrently running %s @ 0x%08x.\r\n", partition.name().c_str(), partition.address()); Serial.println("Type 'help' and press enter for instructions."); Serial.println(); diff --git a/samples/Basic_Ota/component.mk b/samples/Basic_Ota/component.mk new file mode 100644 index 0000000000..e3cab59ca4 --- /dev/null +++ b/samples/Basic_Ota/component.mk @@ -0,0 +1,23 @@ +COMPONENT_DEPENDS := OtaNetwork + +HWCONFIG := ota + +CONFIG_VARS := RBOOT_TWO_ROMS +RBOOT_TWO_ROMS := 1 + +# download urls, set appropriately +CONFIG_VARS += ROM_0_URL \ + ROM_1_URL \ + SPIFFS_URL + +ROM_0_URL := "http://192.168.7.5:80/rom0.bin" +ROM_1_URL := "http://192.168.7.5:80/rom1.bin" +SPIFFS_URL := "http://192.168.7.5:80/spiff_rom.bin" + +APP_CFLAGS = -DROM_0_URL="\"$(ROM_0_URL)"\" \ + -DROM_1_URL="\"$(ROM_1_URL)"\" \ + -DSPIFFS_URL="\"$(SPIFFS_URL)"\" + +ifneq ($(RBOOT_TWO_ROMS),) + APP_CFLAGS += -DRBOOT_TWO_ROMS=$(RBOOT_TWO_ROMS) +endif \ No newline at end of file diff --git a/samples/Basic_rBoot/files/testfile.txt b/samples/Basic_Ota/files/testfile.txt similarity index 100% rename from samples/Basic_rBoot/files/testfile.txt rename to samples/Basic_Ota/files/testfile.txt diff --git a/samples/Basic_Ota/ota.hw b/samples/Basic_Ota/ota.hw new file mode 100644 index 0000000000..c5d71daa72 --- /dev/null +++ b/samples/Basic_Ota/ota.hw @@ -0,0 +1,11 @@ +{ + "base_config": "spiffs-two-roms", + "partitions": { + "rom0": { + "subtype": "ota_0" + }, + "rom1": { + "subtype": "ota_1" + } + } +} \ No newline at end of file diff --git a/samples/Basic_rBoot/README.rst b/samples/Basic_rBoot/README.rst deleted file mode 100644 index f49ea3da08..0000000000 --- a/samples/Basic_rBoot/README.rst +++ /dev/null @@ -1,83 +0,0 @@ -Basic rBoot -=========== - -.. highlight:: bash - -Introduction ------------- - -This sample integrates :component:`rboot` and Sming, for the many people who have -been asking for it. It demonstrates dual rom booting, big flash support, -OTA updates and dual spiffs filesystems. You must enable big flash -support in rBoot and use on an ESP12 (or similar device with 4MB flash). -When using rBoot big flash support with multiple 1MB slots only one rom -image needs to be created. If you don’t want to use big flash support -(e.g. for a device with smaller flash) see the separate instructions -below. You can easily take the ota files and add them to your own -project to add OTA support. - -Building --------- - -1) Set :envvar:`ESP_HOME` & :envvar:`SMING_HOME`, as environment variables or edit - component.mk as you would for general Sming app compiling. -2) Set :envvar:`WIFI_SSID` & :envvar:`WIFI_PWD` environment variables with your wifi details. -3) Edit the OTA server details at the top of ``app/application.cpp``. -4) Check overridable variables in component.mk, or set as env vars. -5) ``make && make flash`` -6) Put *rom0.bin* and *spiff_rom.bin* in the root of your webserver for OTA. -7) Interact with the sample using a terminal (``make terminal``). Sorry - no web-gui (yet). - -Flashing --------- - -If flashing manually use *esptool.py* to flash rBoot, rom & spiffs e.g.:: - - esptool.py –port write_flash -fs 32m 0x00000 rboot.bin 0x02000 rom0.bin 0x100000 spiffs.rom - -Using the correct -fs parameter is important. This will be ``-fs 32m`` on an ESP12. - -You can also flash rom0.bin to 0x202000, but booting and using OTA is quicker! - -Technical Notes ---------------- - -``spiffs_mount_manual(address, length)`` must be called from init. - -.. note:: - - This method is now deprecated. Please configure partitions appropriately, - use PartitionTable methods to locate the desired partition, then mount it:: - - auto part = PartitionTable().find('spiffs0'); - spiffs_mount(part); - - See :ref:`hardware_config` for further details. - -Important compiler flags used: - -- BOOT_BIG_FLASH - when using big flash mode, ensures flash mapping code is built in to the rom. -- RBOOT_INTEGRATION - ensures Sming specific options are pulled in to the rBoot source at compile time. - -Flash layout considerations ---------------------------- - -If you want to use, for example, two 512k roms in the first 1MB block of -flash (old style) then Sming will automatically create two separately linked -roms. If you are flashing a single rom to multiple 1MB flash blocks, all using -the same offset inside their 1MB blocks, only a single rom is created. -See :component:`rboot` for further details. - -- If using a very small flash (e.g. 512k) there may be no room for a - spiffs fileystem, so use *HWCONFIG = standard* -- After building copy all the rom*.bin files to the root of your web - server. - -If you want more than two roms you must be an advanced user and should -be able to work out what to copy and edit to acheive this! - -Credits -------- - -This sample was made possible with the assistance of piperpilot, -gschmott and robotiko on the esp8266.com forum. diff --git a/samples/Basic_rBoot/basic_rboot.hw b/samples/Basic_rBoot/basic_rboot.hw deleted file mode 100644 index 27332cb161..0000000000 --- a/samples/Basic_rBoot/basic_rboot.hw +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "Two ROM slots, two SPIFFS", - "base_config": "spiffs", - "partitions": { - "rom0": { - "subtype": "ota_0" - }, - "rom1": { - "address": "0x108000", - "size": "992K", - "type": "app", - "subtype": "ota_1", - "filename": "$(RBOOT_ROM_1_BIN)" - }, - "spiffs1": { - "address": "0x300000", - "size": "512K", - "type": "data", - "subtype": "spiffs" - } - } -} \ No newline at end of file diff --git a/samples/Basic_rBoot/component.mk b/samples/Basic_rBoot/component.mk deleted file mode 100644 index 9555e50099..0000000000 --- a/samples/Basic_rBoot/component.mk +++ /dev/null @@ -1,7 +0,0 @@ -#### overridable rBoot options #### - -## use rboot build mode -RBOOT_ENABLED := 1 - -## Use standard hardware config with two ROM slots and two SPIFFS partitions -HWCONFIG := basic_rboot From f2bcf7960b692f2f69796397da49d333a40237bc Mon Sep 17 00:00:00 2001 From: slaff Date: Sun, 30 May 2021 11:20:52 +0200 Subject: [PATCH 028/130] Read the docs is missing npm and npx. Try to use python only. (#2334) --- docs/source/conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index ad7b98982f..2deb3a29db 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -66,6 +66,8 @@ offline_skin_js_path = '_static/WaveSkin.js' offline_wavedrom_js_path = '_static/WaveDrom.js' +render_using_wavedrompy = True + # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. From 000f6208ed75bf0d36594f5c3d6ff227fdf82c36 Mon Sep 17 00:00:00 2001 From: slaff Date: Tue, 1 Jun 2021 10:19:35 +0200 Subject: [PATCH 029/130] Fixed an issue with Esp32 and OtaUpgrade. (#2335) --- Sming/Libraries/OtaUpgrade/component.mk | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Sming/Libraries/OtaUpgrade/component.mk b/Sming/Libraries/OtaUpgrade/component.mk index 50bc8e66bc..c476d7cfea 100644 --- a/Sming/Libraries/OtaUpgrade/component.mk +++ b/Sming/Libraries/OtaUpgrade/component.mk @@ -148,7 +148,7 @@ ifneq ($(SMING_ARCH),Host) CUSTOM_TARGETS += ota-file endif -$(OTA_UPGRADE_FILE): $(RBOOT_ROM_0_BIN) $(RBOOT_ROM_1_BIN) $(OTA_KEY_IMAGE) +$(OTA_UPGRADE_FILE): $(PARTITION_factory_FILENAME) $(PARTITION_rom0_FILENAME) $(PARTITION_rom1_FILENAME) $(OTA_KEY_IMAGE) ifeq ($(ENABLE_OTA_DOWNGRADE),0) ifeq ($(OTA_CRYPTO_FEATURES),) $(warning WARNING: Downgrade protection ineffective without encryption or digital signature. \ @@ -158,8 +158,9 @@ endif $(Q) $(OTATOOL) mkfile \ $(OTA_CRYPTO_FEATURES_IMAGE) \ $(if $(OTA_CRYPTO_FEATURES_IMAGE),--key=$(OTA_KEY_IMAGE)) \ - --rom=$(RBOOT_ROM_0_BIN)@$(RBOOT_ROM0_ADDR) \ - $(if $(RBOOT_ROM_1_BIN),--rom=$(RBOOT_ROM_1_BIN)@$(RBOOT_ROM1_ADDR)) \ + $(if $(PARTITION_factory_FILENAME),--rom=$(PARTITION_factory_FILENAME)@$(PARTITION_factory_ADDRESS)) \ + $(if $(PARTITION_rom0_FILENAME),--rom=$(PARTITION_rom0_FILENAME)@$(PARTITION_rom0_ADDRESS)) \ + $(if $(PARTITION_rom1_FILENAME),--rom=$(PARTITION_rom1_FILENAME)@$(PARTITION_rom1_ADDRESS)) \ --output=$@ ifdef OTA_ROLLOVER_IN_PROGRESS @echo From 870bb78e73d34e0a4e628a6cf8d9d4d14c391e9c Mon Sep 17 00:00:00 2001 From: alexdz18 Date: Fri, 11 Jun 2021 22:33:58 +0300 Subject: [PATCH 030/130] Arch/Esp8266/Core/Digital.cpp: fixed pulseIn() for pins >= 8 (#2337) --- Sming/Arch/Esp8266/Core/Digital.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sming/Arch/Esp8266/Core/Digital.cpp b/Sming/Arch/Esp8266/Core/Digital.cpp index 240de8e91f..68a81dbccd 100644 --- a/Sming/Arch/Esp8266/Core/Digital.cpp +++ b/Sming/Arch/Esp8266/Core/Digital.cpp @@ -141,9 +141,9 @@ unsigned long pulseIn(uint16_t pin, uint8_t state, unsigned long timeout) // cache the port and bit of the pin in order to speed up the // pulse width measuring loop and achieve finer resolution. calling // digitalRead() instead yields much coarser resolution. - uint8_t bit = digitalPinToBitMask(pin); + uint32_t bit = digitalPinToBitMask(pin); // uint8_t port = digitalPinToPort(pin); // Does nothing in Sming, comment-out to prevent compiler warning - uint8_t stateMask = (state ? bit : 0); + uint32_t stateMask = (state ? bit : 0); unsigned long width = 0; // keep initialization out of time critical area // convert the timeout from microseconds to a number of times through From 0a7d0f8041759562aacf1b4ef5dd1239045e52d3 Mon Sep 17 00:00:00 2001 From: slaff Date: Tue, 15 Jun 2021 14:34:55 +0200 Subject: [PATCH 031/130] Added pulseIn to the list of functions that can be called via Hosted RPC. (#2338) --- Sming/Components/Hosted-Lib/src/Digital.cpp | 6 ++++++ Sming/Components/Hosted/samples/serial/app/application.cpp | 4 +++- Sming/Components/Hosted/samples/tcp/app/application.cpp | 4 +++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Sming/Components/Hosted-Lib/src/Digital.cpp b/Sming/Components/Hosted-Lib/src/Digital.cpp index d99e748ded..1462f33811 100644 --- a/Sming/Components/Hosted-Lib/src/Digital.cpp +++ b/Sming/Components/Hosted-Lib/src/Digital.cpp @@ -31,3 +31,9 @@ uint8_t digitalRead(uint16_t pin) hostedClient->send(__func__, pin); return hostedClient->wait(); } + +unsigned long pulseIn(uint16_t pin, uint8_t state, unsigned long timeout) +{ + hostedClient->send(__func__, pin, state, timeout); + return hostedClient->wait(); +} diff --git a/Sming/Components/Hosted/samples/serial/app/application.cpp b/Sming/Components/Hosted/samples/serial/app/application.cpp index ba70d7576f..fab436ee0e 100644 --- a/Sming/Components/Hosted/samples/serial/app/application.cpp +++ b/Sming/Components/Hosted/samples/serial/app/application.cpp @@ -22,12 +22,14 @@ void init() * - pinMode * - digitalRead * - digitalWrite + * - pulseIn * You can add more commands here. For every command you should specify command and text description in the format below. * For more information read the SimpleRPC interface API: https://simplerpc.readthedocs.io/en/latest/api/interface.html */ pinMode, F("pinMode: Sets mode of digital pin. @pin: Pin number, @mode: Mode type."), digitalRead, F("digitalRead: Read digital pin. @pin: Pin number. @return: Pin value."), - digitalWrite, F("digitalWrite: Write to a digital pin. @pin: Pin number. @value: Pin value.") + digitalWrite, F("digitalWrite: Write to a digital pin. @pin: Pin number. @value: Pin value."), + pulseIn, F("pulseIn: Measure duration of pulse on pin. @pin: Pin number. @state: State of pulse to measure. @timeout: Maximum duration of pulse. @return: Pulse duration in microseconds)") ); // clang-format on diff --git a/Sming/Components/Hosted/samples/tcp/app/application.cpp b/Sming/Components/Hosted/samples/tcp/app/application.cpp index 2e3a467e83..3702dc2580 100644 --- a/Sming/Components/Hosted/samples/tcp/app/application.cpp +++ b/Sming/Components/Hosted/samples/tcp/app/application.cpp @@ -44,12 +44,14 @@ void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) * - pinMode * - digitalRead * - digitalWrite + * - pulseIn * You can add more commands here. For every command you should specify command and text description in the format below. * For more information read the SimpleRPC interface API: https://simplerpc.readthedocs.io/en/latest/api/interface.html */ pinMode, F("pinMode: Sets mode of digital pin. @pin: Pin number, @mode: Mode type."), digitalRead, F("digitalRead: Read digital pin. @pin: Pin number. @return: Pin value."), - digitalWrite, F("digitalWrite: Write to a digital pin. @pin: Pin number. @value: Pin value.") + digitalWrite, F("digitalWrite: Write to a digital pin. @pin: Pin number. @value: Pin value."), + pulseIn, F("pulseIn: Measure duration of pulse on pin. @pin: Pin number. @state: State of pulse to measure. @timeout: Maximum duration of pulse. @return: Pulse duration in microseconds)") ); // clang-format on From fbf8caa0e0f8ddf07974e9252f393c34eb5f2c39 Mon Sep 17 00:00:00 2001 From: charlesschaefer Date: Tue, 22 Jun 2021 06:42:01 -0300 Subject: [PATCH 032/130] Added support for zsh in export.sh script (#2340) --- Tools/export.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Tools/export.sh b/Tools/export.sh index a9b9e7facd..81cf497ea8 100755 --- a/Tools/export.sh +++ b/Tools/export.sh @@ -18,7 +18,12 @@ # if [ -z "$SMING_HOME" ]; then - export SMING_HOME=$(readlink -m $BASH_SOURCE/../../Sming) + if [ $(basename $SHELL) = "zsh" ]; then + _SOURCE=${(%):-%N} + else + _SOURCE=$BASH_SOURCE + fi + export SMING_HOME=$(readlink -m $_SOURCE/../../Sming) fi # Common From 5da5592341b85ac094eb8b3269413ad27d73335c Mon Sep 17 00:00:00 2001 From: slaff Date: Tue, 29 Jun 2021 09:26:42 +0200 Subject: [PATCH 033/130] Added support for sending streams directly from TcpClient. (#2341) --- .../Network/src/Network/MqttClient.cpp | 16 +-- .../Network/src/Network/MqttClient.h | 19 ++-- .../Network/src/Network/TcpClient.cpp | 65 +++++++---- .../Network/src/Network/TcpClient.h | 5 +- .../Network/src/Network/TcpServer.cpp | 1 - Sming/Core/Data/Stream/DataSourceStream.h | 17 +-- Sming/Core/Data/Stream/MemoryDataStream.h | 2 +- Sming/Core/Data/Stream/StreamChain.h | 5 + tests/HostTests/include/modules.h | 1 + tests/HostTests/modules/TcpClient.cpp | 104 ++++++++++++++++++ 10 files changed, 180 insertions(+), 55 deletions(-) create mode 100644 tests/HostTests/modules/TcpClient.cpp diff --git a/Sming/Components/Network/src/Network/MqttClient.cpp b/Sming/Components/Network/src/Network/MqttClient.cpp index 349095d718..2e37419af8 100644 --- a/Sming/Components/Network/src/Network/MqttClient.cpp +++ b/Sming/Components/Network/src/Network/MqttClient.cpp @@ -10,8 +10,7 @@ #include "MqttClient.h" -#include "Data/Stream/MemoryDataStream.h" -#include "Data/Stream/StreamChain.h" +#include "Data/Stream/DataSourceStream.h" const mqtt_parser_callbacks_t MqttClient::callbacks PROGMEM = { .on_message_begin = staticOnMessageBegin, @@ -387,7 +386,7 @@ void MqttClient::onReadyToSendData(TcpConnectionEvent sourceEvent) } if(outgoingMessage->common.type == MQTT_TYPE_PUBLISH && payloadStream != nullptr) { - // The packetLength should be big enought for the header ONLY. + // The packetLength should be big enough for the header ONLY. // Payload will be attached as a second stream packetLength -= outgoingMessage->publish.content.length; outgoingMessage->publish.content.data = nullptr; @@ -396,16 +395,9 @@ void MqttClient::onReadyToSendData(TcpConnectionEvent sourceEvent) uint8_t packet[packetLength]; mqtt_serialiser_write(&serialiser, outgoingMessage, packet, packetLength); - delete stream; - auto headerStream = new MemoryDataStream(); - headerStream->write(packet, packetLength); + send(reinterpret_cast(packet), packetLength); if(payloadStream != nullptr) { - auto streamChain = new StreamChain(); - streamChain->attachStream(headerStream); - streamChain->attachStream(payloadStream); - stream = streamChain; - } else { - stream = headerStream; + send(payloadStream); } state = eMCS_SendingData; diff --git a/Sming/Components/Network/src/Network/MqttClient.h b/Sming/Components/Network/src/Network/MqttClient.h index 5e9e06f2d7..dff5177e2f 100644 --- a/Sming/Components/Network/src/Network/MqttClient.h +++ b/Sming/Components/Network/src/Network/MqttClient.h @@ -182,9 +182,9 @@ class MqttClient : protected TcpClient #ifndef MQTT_NO_COMPAT /** - * @todo deprecate: Use setWill(const String& topic, const String& message,uint8_t flags) instead + * @deprecated: Use setWill(const String& topic, const String& message,uint8_t flags) instead */ - bool setWill(const String& topic, const String& message, int QoS, bool retained = false) + bool setWill(const String& topic, const String& message, int QoS, bool retained = false) SMING_DEPRECATED { uint8_t flags = (uint8_t)(retained + (QoS << 1)); return setWill(topic, message, flags); @@ -197,12 +197,12 @@ class MqttClient : protected TcpClient */ /** - * @todo deprecate: Use publish(const String& topic, const String& message, uint8_t flags = 0) instead. + * @deprecated: Use publish(const String& topic, const String& message, uint8_t flags = 0) instead. * If you want to have a callback that should be triggered on successful delivery of messages * then use setEventHandler(MQTT_TYPE_PUBACK, youCallback) instead. */ bool publishWithQoS(const String& topic, const String& message, int QoS, bool retained = false, - MqttMessageDeliveredCallback onDelivery = nullptr) + MqttMessageDeliveredCallback onDelivery = nullptr) SMING_DEPRECATED { if(onDelivery) { if(QoS == 1) { @@ -220,10 +220,11 @@ class MqttClient : protected TcpClient return publish(topic, message, flags); } - /** @brief Provide a function to be called when a message is received from the broker - * @todo deprecate: Use setEventHandler(MQTT_TYPE_PUBLISH, MqttDelegate handler) instead. + /** + * @brief Provide a function to be called when a message is received from the broker + * @deprecated: Use setEventHandler(MQTT_TYPE_PUBLISH, MqttDelegate handler) instead. */ - void setCallback(MqttStringSubscriptionCallback subscriptionCallback = nullptr) + void setCallback(MqttStringSubscriptionCallback subscriptionCallback = nullptr) SMING_DEPRECATED { this->subscriptionCallback = subscriptionCallback; setEventHandler(MQTT_TYPE_PUBLISH, onPublish); @@ -331,8 +332,8 @@ class MqttClient : protected TcpClient */ #ifndef MQTT_NO_COMPAT - MqttMessageDeliveredCallback onDelivery = nullptr; ///< @deprecated - MqttStringSubscriptionCallback subscriptionCallback = nullptr; ///< @deprecated + SMING_DEPRECATED MqttMessageDeliveredCallback onDelivery = nullptr; ///< @deprecated + SMING_DEPRECATED MqttStringSubscriptionCallback subscriptionCallback = nullptr; ///< @deprecated #endif }; diff --git a/Sming/Components/Network/src/Network/TcpClient.cpp b/Sming/Components/Network/src/Network/TcpClient.cpp index d6f698954c..3f24675a9e 100644 --- a/Sming/Components/Network/src/Network/TcpClient.cpp +++ b/Sming/Components/Network/src/Network/TcpClient.cpp @@ -10,28 +10,14 @@ #include "TcpClient.h" #include "Data/Stream/MemoryDataStream.h" +#include "Data/Stream/StreamChain.h" void TcpClient::freeStreams() { - if(buffer != nullptr) { - if(buffer != stream) { - debug_e("TcpClient: buffer doesn't match stream"); - delete buffer; - } - buffer = nullptr; - } - delete stream; stream = nullptr; } -void TcpClient::setBuffer(ReadWriteStream* stream) -{ - freeStreams(); - buffer = stream; - this->stream = buffer; -} - bool TcpClient::connect(const String& server, int port, bool useSsl) { if(isProcessing()) { @@ -58,21 +44,56 @@ bool TcpClient::send(const char* data, uint16_t len, bool forceCloseAfterSent) return false; } - if(buffer == nullptr) { - setBuffer(new MemoryDataStream()); - if(buffer == nullptr) { - return false; + auto memoryStream = static_cast(stream); + if(memoryStream == nullptr || memoryStream->getStreamType() != eSST_MemoryWritable) { + memoryStream = new MemoryDataStream(); + if(stream == nullptr) { + stream = memoryStream; } } - if(buffer->write((const uint8_t*)data, len) != len) { + if(memoryStream->write(data, len) != len) { debug_e("TcpClient::send ERROR: Unable to store %d bytes in buffer", len); return false; } - debug_d("Storing %d bytes in stream", len); + return send(memoryStream, forceCloseAfterSent); +} + +bool TcpClient::send(IDataSourceStream* source, bool forceCloseAfterSent) +{ + if(state != eTCS_Connecting && state != eTCS_Connected) { + return false; + } + + if(source == nullptr) { + return false; + } + + if(stream == nullptr) { + stream = source; + } + else if(stream != source){ + auto chainStream = static_cast(stream); + if(chainStream != nullptr && chainStream->getStreamType() == eSST_Chain) { + chainStream->attachStream(source); + } + else { + debug_d("Creating stream chain ..."); + chainStream = new StreamChain(); + chainStream->attachStream(stream); + chainStream->attachStream(source); + stream = chainStream; + } + } + + int length = source->available(); + if(length > 0) { + totalSentBytes += length; + } + + debug_d("Sending stream. Bytes to send: %d", length); - totalSentBytes += len; closeAfterSent = forceCloseAfterSent ? eTCCASS_AfterSent : eTCCASS_None; return true; diff --git a/Sming/Components/Network/src/Network/TcpClient.h b/Sming/Components/Network/src/Network/TcpClient.h index 9be0e46cde..0056409503 100644 --- a/Sming/Components/Network/src/Network/TcpClient.h +++ b/Sming/Components/Network/src/Network/TcpClient.h @@ -107,6 +107,8 @@ class TcpClient : public TcpConnection return send(data.c_str(), data.length(), forceCloseAfterSent); } + bool send(IDataSourceStream* source, bool forceCloseAfterSent = false); + bool isProcessing() { return state == eTCS_Connected || state == eTCS_Connecting; @@ -151,9 +153,6 @@ class TcpClient : public TcpConnection void freeStreams(); protected: - void setBuffer(ReadWriteStream* stream); - - ReadWriteStream* buffer = nullptr; ///< Used internally to buffer arbitrary data via send() methods IDataSourceStream* stream = nullptr; ///< The currently active stream being sent private: diff --git a/Sming/Components/Network/src/Network/TcpServer.cpp b/Sming/Components/Network/src/Network/TcpServer.cpp index 81a41f0da0..8e761c08b6 100644 --- a/Sming/Components/Network/src/Network/TcpServer.cpp +++ b/Sming/Components/Network/src/Network/TcpServer.cpp @@ -122,7 +122,6 @@ void TcpServer::onClientComplete(TcpClient& client, bool successful) bool TcpServer::onClientReceive(TcpClient& client, char* data, int size) { debug_d("TcpSever onReceive: %s, %d bytes\r\n", client.getRemoteIp().toString().c_str(), size); - debug_d("Data: %s", data); if(clientReceiveDelegate) { return clientReceiveDelegate(client, data, size); } diff --git a/Sming/Core/Data/Stream/DataSourceStream.h b/Sming/Core/Data/Stream/DataSourceStream.h index 053100fbfc..86b4494faa 100644 --- a/Sming/Core/Data/Stream/DataSourceStream.h +++ b/Sming/Core/Data/Stream/DataSourceStream.h @@ -24,13 +24,16 @@ * @ingroup constants */ enum StreamType { - eSST_Invalid, ///< Stream content not valid - eSST_Memory, ///< Memory data stream - eSST_File, ///< File data stream - eSST_Template, ///< Template data stream - eSST_JsonObject, ///< JSON object data stream - eSST_User, ///< User defined data stream - eSST_Unknown ///< Unknown data stream type + eSST_Invalid, ///< Stream content not valid + eSST_Memory, ///< Memory stream + eSST_MemoryWritable, /// < Memory stream where data can be safely written to. + // Expands on demand and does not transform the data. + eSST_File, ///< File data stream + eSST_Template, ///< Template data stream + eSST_JsonObject, ///< JSON object data stream + eSST_User, ///< User defined data stream + eSST_Chain, ///< A stream (chain) containing multiple streams + eSST_Unknown ///< Unknown data stream type }; /** diff --git a/Sming/Core/Data/Stream/MemoryDataStream.h b/Sming/Core/Data/Stream/MemoryDataStream.h index 3e03f8821b..cef511a62b 100644 --- a/Sming/Core/Data/Stream/MemoryDataStream.h +++ b/Sming/Core/Data/Stream/MemoryDataStream.h @@ -42,7 +42,7 @@ class MemoryDataStream : public ReadWriteStream StreamType getStreamType() const override { - return eSST_Memory; + return eSST_MemoryWritable; } /** @brief Get a pointer to the current position diff --git a/Sming/Core/Data/Stream/StreamChain.h b/Sming/Core/Data/Stream/StreamChain.h index 7cfb60413b..d328441b4f 100644 --- a/Sming/Core/Data/Stream/StreamChain.h +++ b/Sming/Core/Data/Stream/StreamChain.h @@ -47,6 +47,11 @@ class StreamChain : public MultiStream return queue.enqueue(stream); } + StreamType getStreamType() const override + { + return eSST_Chain; + } + protected: IDataSourceStream* getNextStream() override { diff --git a/tests/HostTests/include/modules.h b/tests/HostTests/include/modules.h index 485055de6c..49d3db46b6 100644 --- a/tests/HostTests/include/modules.h +++ b/tests/HostTests/include/modules.h @@ -27,4 +27,5 @@ XX(Clocks) \ XX(Timers) \ XX(HttpRequest) \ + XX(TcpClient) \ XX(Hosted) diff --git a/tests/HostTests/modules/TcpClient.cpp b/tests/HostTests/modules/TcpClient.cpp new file mode 100644 index 0000000000..e22d02391e --- /dev/null +++ b/tests/HostTests/modules/TcpClient.cpp @@ -0,0 +1,104 @@ +#include + +#include +#include +#include +#include + +class TcpClientTest : public TestGroup +{ +public: + TcpClientTest() : TestGroup(_F("TcpClient")) + { + } + + void execute() override + { + if(!WifiStation.isConnected()) { + Serial.println("No network, skipping tests"); + return; + } + + constexpr int port = 9876; + String inputData = "This is very long and complex text that will be sent using multiple complicated streams."; + + // Tcp Server + server = new TcpServer( + [this](TcpClient& client, char* data, int size) -> bool { + // on data + return receivedData.concat(data, size); + }, + [this, inputData](TcpClient& client, bool successful) { + // on client close + if(finished) { + return; + } + REQUIRE(successful == true); + REQUIRE(receivedData == inputData); + finished = true; + shutdown(); + }); + server->listen(port); + server->setTimeOut(USHRT_MAX); // disable connection timeout + server->setKeepAlive(USHRT_MAX); // disable connection timeout + + // Tcp Client + bool connected = client.connect(WifiStation.getIP(), port); + debug_d("Connected: %d", connected); + + TEST_CASE("TcpClient::send stream") + { + size_t offset = 0; + + // Send text using bytes + client.send(inputData.c_str(), 5); + offset += 5; + + // send data using more bytes + client.send(inputData.c_str() + offset, 7); + offset += 7; + + // send data as stream + auto stream1 = new MemoryDataStream(); + stream1->write(inputData.c_str() + offset, 3); + client.send(stream1); + offset += 3; + client.commit(); + + // more stream + auto stream2 = new LimitedMemoryStream(4); + stream2->write(reinterpret_cast(inputData.c_str()) + offset, 4); + client.send(stream2); + offset += 4; + + // and finally the rest of the bytes + String rest = inputData.substring(offset); + client.send(rest.c_str(), rest.length()); + client.setTimeOut(1); + + pending(); + } + } + + void shutdown() + { + server->shutdown(); + server = nullptr; + timer.initializeMs<1000>([this]() { complete(); }); + timer.startOnce(); + } + +private: + String receivedData; + TcpServer* server{nullptr}; + TcpClient client{false}; + Timer timer; + volatile bool finished = false; +}; + +void REGISTER_TEST(TcpClient) +{ +#ifdef ARCH_HOST + registerGroup(); +#endif +} From 4c64f49bc3c93cd6fa277b860207809ad9b8d37e Mon Sep 17 00:00:00 2001 From: slaff Date: Thu, 1 Jul 2021 15:50:01 +0200 Subject: [PATCH 034/130] Disable old MqttClient calls by default. (#2343) If desired they can be enabled with `make MQTT_NO_COMPAT=`. * Removed deprecated MQTT calls from the samples. --- Sming/Components/Network/component.mk | 4 +- .../Network/src/Network/Mqtt/MqttBuffer.h | 0 .../Network/src/Network/MqttClient.h | 13 +++++ samples/Basic_AWS/app/application.cpp | 2 +- samples/MeteoControl_mqtt/app/application.cpp | 51 ++++++++++--------- .../MeteoControl_mqtt/include/configuration.h | 1 - samples/MqttClient_Hello/app/application.cpp | 30 ++++++----- 7 files changed, 61 insertions(+), 40 deletions(-) rename samples/Basic_AWS/app/MqttMessage.h => Sming/Components/Network/src/Network/Mqtt/MqttBuffer.h (100%) diff --git a/Sming/Components/Network/component.mk b/Sming/Components/Network/component.mk index c2341b907c..572ef6a501 100644 --- a/Sming/Components/Network/component.mk +++ b/Sming/Components/Network/component.mk @@ -36,7 +36,9 @@ COMPONENT_VARS += HTTP_SERVER_EXPOSE_VERSION HTTP_SERVER_EXPOSE_VERSION ?= 0 GLOBAL_CFLAGS += -DHTTP_SERVER_EXPOSE_VERSION=$(HTTP_SERVER_EXPOSE_VERSION) - +# => MQTT +COMPONENT_VARS += MQTT_NO_COMPAT +MQTT_NO_COMPAT ?= 1 # => LWIP COMPONENT_VARS += ENABLE_CUSTOM_LWIP diff --git a/samples/Basic_AWS/app/MqttMessage.h b/Sming/Components/Network/src/Network/Mqtt/MqttBuffer.h similarity index 100% rename from samples/Basic_AWS/app/MqttMessage.h rename to Sming/Components/Network/src/Network/Mqtt/MqttBuffer.h diff --git a/Sming/Components/Network/src/Network/MqttClient.h b/Sming/Components/Network/src/Network/MqttClient.h index dff5177e2f..8030af0857 100644 --- a/Sming/Components/Network/src/Network/MqttClient.h +++ b/Sming/Components/Network/src/Network/MqttClient.h @@ -127,6 +127,19 @@ class MqttClient : protected TcpClient /* [ Convenience methods ] */ + /** + * @brief Compute the flags value + * @param QoS - Quality of Service + * @param retain - Retain flag + * @param dup - Duplicate delivery + * + * @retval uint8_t calculated flags value + */ + static uint8_t getFlags(mqtt_qos_t QoS, mqtt_retain_t retain = MQTT_RETAIN_FALSE, mqtt_dup_t dup = MQTT_DUP_FALSE) + { + return (retain + (QoS << 1) + (dup << 3)); + } + /** * @brief Sets a handler to be called after successful MQTT connection * diff --git a/samples/Basic_AWS/app/application.cpp b/samples/Basic_AWS/app/application.cpp index a1a4ee4ac9..dedcca2674 100644 --- a/samples/Basic_AWS/app/application.cpp +++ b/samples/Basic_AWS/app/application.cpp @@ -1,5 +1,5 @@ #include -#include "MqttMessage.h" +#include // If you want, you can define WiFi settings globally in Eclipse Environment Variables #ifndef WIFI_SSID diff --git a/samples/MeteoControl_mqtt/app/application.cpp b/samples/MeteoControl_mqtt/app/application.cpp index 5e41aa9d08..74622ca477 100644 --- a/samples/MeteoControl_mqtt/app/application.cpp +++ b/samples/MeteoControl_mqtt/app/application.cpp @@ -1,4 +1,5 @@ #include +#include #include "configuration.h" // application configuration @@ -7,28 +8,6 @@ Timer publishTimer; -void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway); - -void init() -{ - Serial.begin(SERIAL_BAUD_RATE); // 115200 by default - - Wire.pins(4, 5); // SDA, SCL - Wire.begin(); - - // initialization config - mqtt.setCallback(onMessageReceived); - - BMPinit(); // BMP180 sensor initialization - SIinit(); // HTU21D sensor initialization - - WifiStation.config(WIFI_SSID, WIFI_PWD); - WifiStation.enable(true); - WifiEvents.onStationGotIP(gotIP); - WifiAccessPoint.enable(false); - WDT.enable(false); //disable watchdog -} - // Publish our message void publishMessage() // uncomment timer in connectOk() if need publishMessage() loop { @@ -40,11 +19,13 @@ void publishMessage() // uncomment timer in connectOk() if need publishMessage() } // Callback for messages, arrived from MQTT server -void onMessageReceived(String topic, String message) +int onMessageReceived(MqttClient& client, mqtt_message_t* message) { - Serial.print(topic); + Serial.print("Received: "); + Serial.print(MqttBuffer(message->publish.topic_name)); Serial.print(":\r\n\t"); // Pretify alignment for printing - Serial.println(message); + Serial.println(MqttBuffer(message->publish.content)); + return 0; } // Run MQTT client @@ -63,3 +44,23 @@ void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) startMqttClient(); publishMessage(); // run once publishMessage } + +void init() +{ + Serial.begin(SERIAL_BAUD_RATE); // 115200 by default + + Wire.pins(4, 5); // SDA, SCL + Wire.begin(); + + // initialization config + mqtt.setMessageHandler(onMessageReceived); + + BMPinit(); // BMP180 sensor initialization + SIinit(); // HTU21D sensor initialization + + WifiStation.config(WIFI_SSID, WIFI_PWD); + WifiStation.enable(true); + WifiEvents.onStationGotIP(gotIP); + WifiAccessPoint.enable(false); + WDT.enable(false); //disable watchdog +} diff --git a/samples/MeteoControl_mqtt/include/configuration.h b/samples/MeteoControl_mqtt/include/configuration.h index cfc8fb06e3..ba02ccdb83 100644 --- a/samples/MeteoControl_mqtt/include/configuration.h +++ b/samples/MeteoControl_mqtt/include/configuration.h @@ -35,7 +35,6 @@ int TIMER = 20; // every N* seconds send to mqtt server // Forward declarations void startMqttClient(); -void onMessageReceived(String topic, String message); MqttClient mqtt; diff --git a/samples/MqttClient_Hello/app/application.cpp b/samples/MqttClient_Hello/app/application.cpp index 6a2dfbddd2..733eb4cd30 100644 --- a/samples/MqttClient_Hello/app/application.cpp +++ b/samples/MqttClient_Hello/app/application.cpp @@ -1,4 +1,5 @@ #include +#include // If you want, you can define WiFi settings globally in Eclipse Environment Variables #ifndef WIFI_SSID @@ -21,7 +22,6 @@ // Forward declarations void startMqttClient(); -void onMessageReceived(String topic, String message); MqttClient mqtt; @@ -40,10 +40,11 @@ void checkMQTTDisconnect(TcpClient& client, bool flag) procTimer.initializeMs(2 * 1000, startMqttClient).start(); // every 2 seconds } -void onMessageDelivered(uint16_t msgId, int type) +int onMessageDelivered(MqttClient& client, mqtt_message_t* message) { - Serial.printf(_F("Message with id %d and QoS %d was delivered successfully.\n"), msgId, - (type == MQTT_MSG_PUBREC ? 2 : 1)); + Serial.printf(_F("Message with id %d and QoS %d was delivered successfully.\n"), message->puback.message_id, + message->puback.qos); + return 0; } // Publish our message @@ -57,16 +58,18 @@ void publishMessage() Serial.println(system_get_free_heap_size()); mqtt.publish(F("main/frameworks/sming"), F("Hello friends, from Internet of things :)")); - mqtt.publishWithQoS(F("important/frameworks/sming"), F("Request Return Delivery"), 1, false, - onMessageDelivered); // or publishWithQoS + mqtt.publish(F("important/frameworks/sming"), F("Request Return Delivery"), + MqttClient::getFlags(MQTT_QOS_AT_LEAST_ONCE)); } // Callback for messages, arrived from MQTT server -void onMessageReceived(String topic, String message) +int onMessageReceived(MqttClient& client, mqtt_message_t* message) { - Serial.print(topic); - Serial.print(":\r\n\t"); // Prettify alignment for printing - Serial.println(message); + Serial.print("Received: "); + Serial.print(MqttBuffer(message->publish.topic_name)); + Serial.print(":\r\n\t"); // Pretify alignment for printing + Serial.println(MqttBuffer(message->publish.content)); + return 0; } // Run MQTT client @@ -75,10 +78,13 @@ void startMqttClient() procTimer.stop(); // 1. [Setup] - if(!mqtt.setWill(F("last/will"), F("The connection from this device is lost:("), 1, true)) { + if(!mqtt.setWill(F("last/will"), F("The connection from this device is lost:("), + MqttClient::getFlags(MQTT_QOS_AT_LEAST_ONCE, MQTT_RETAIN_TRUE))) { debugf("Unable to set the last will and testament. Most probably there is not enough memory on the device."); } + mqtt.setEventHandler(MQTT_TYPE_PUBACK, onMessageDelivered); + mqtt.setConnectedHandler([](MqttClient& client, mqtt_message_t* message) { Serial.print(_F("Connected to ")); Serial.println(client.getRemoteIp()); @@ -91,7 +97,7 @@ void startMqttClient() }); mqtt.setCompleteDelegate(checkMQTTDisconnect); - mqtt.setCallback(onMessageReceived); + mqtt.setMessageHandler(onMessageReceived); #ifdef ENABLE_SSL mqtt.setSslInitHandler([](Ssl::Session& session) { From dd58670cb13ff60b7f02a99475753816cb55e70c Mon Sep 17 00:00:00 2001 From: slaff Date: Mon, 5 Jul 2021 08:52:20 +0200 Subject: [PATCH 035/130] Improved Websocket Connection to allow sending of huge payloads. (#2342) * Added support for sending huge payloads from websocket client. * Added check for memory leaks in the SharedMemoryStream test. --- .../Network/src/Data/Stream/XorOutputStream.h | 77 ++++++++++ .../Http/Websocket/WebsocketConnection.cpp | 145 ++++++++++++------ .../Http/Websocket/WebsocketConnection.h | 39 +++-- .../Network/src/Network/TcpClient.cpp | 29 +++- .../Network/src/Network/TcpClient.h | 10 ++ Sming/Core/Data/Stream/DataSourceStream.h | 1 + Sming/Core/Data/Stream/LimitedMemoryStream.h | 2 +- Sming/Core/Data/Stream/SharedMemoryStream.h | 83 ++++++++++ Sming/Core/Data/Stream/StreamChain.h | 2 +- tests/HostTests/modules/Stream.cpp | 80 ++++++++++ 10 files changed, 390 insertions(+), 78 deletions(-) create mode 100644 Sming/Components/Network/src/Data/Stream/XorOutputStream.h create mode 100644 Sming/Core/Data/Stream/SharedMemoryStream.h diff --git a/Sming/Components/Network/src/Data/Stream/XorOutputStream.h b/Sming/Components/Network/src/Data/Stream/XorOutputStream.h new file mode 100644 index 0000000000..dbf63e488d --- /dev/null +++ b/Sming/Components/Network/src/Data/Stream/XorOutputStream.h @@ -0,0 +1,77 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * XorOutputStream.h + * + * @author Slavey Karadzhov + * + ****/ + +#pragma once + +#include + +class XorOutputStream: public IDataSourceStream { +public: + /** + * @brief Xors original stream content with the specified mask + * @param stream pointer to the original stream. Will be deleted after use + * @param mask + * @param maskLenth + */ + XorOutputStream(IDataSourceStream *stream, uint8_t *mask, size_t maskLength) : + stream(stream), mask(mask), maskLength(maskLength) { + } + + ~XorOutputStream() + { + delete stream; + } + + StreamType getStreamType() const override + { + return eSST_Transform; + } + + int available() override + { + return stream->available(); + } + + uint16_t readMemoryBlock(char *data, int bufSize) override + { + uint16_t max = stream->readMemoryBlock(data, bufSize); + size_t pos = maskPos; + for(unsigned i=0; iseek(len)) { + return false; + } + + maskPos = (maskPos + len) % maskLength; + return true; + } + + bool isFinished() override + { + return stream->isFinished(); + } + +private: + IDataSourceStream *stream; + uint8_t *mask; + size_t maskLength; + size_t maskPos = 0; +}; diff --git a/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.cpp b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.cpp index 0651d39a5d..03a72ee095 100644 --- a/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.cpp +++ b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.cpp @@ -12,6 +12,10 @@ #include #include #include +#include +#include +#include +#include DEFINE_FSTR(WSSTR_CONNECTION, "connection") DEFINE_FSTR(WSSTR_UPGRADE, "upgrade") @@ -171,91 +175,132 @@ int WebsocketConnection::staticOnControlEnd(void* userData) return WS_OK; } -void WebsocketConnection::send(const char* message, size_t length, ws_frame_type_t type) +bool WebsocketConnection::send(const char* message, size_t length, ws_frame_type_t type) { - debug_d("Sending: %s, Type: %d\n", message, type); - if(connection == nullptr) { - return; + auto stream = new MemoryDataStream(); + if(stream == nullptr) { + debug_e("Unable to create memory buffer"); + return false; } - if(!activated) { - debug_e("WS Connection is not activated yet!"); - return; + size_t written = stream->write(message, length); + if(written != length) { + debug_e("Unable to store data in memory buffer"); + return false; } - auto bufferLength = length + 1 + 4 + 4; - char buffer[bufferLength]; - size_t outLength = encodeFrame(type, message, length, buffer, bufferLength, isClientConnection); - if(outLength != 0) { - connection->send(buffer, outLength); - } + return send(stream, type, isClientConnection); } -void WebsocketConnection::broadcast(const char* message, size_t length, ws_frame_type_t type) +bool WebsocketConnection::send(IDataSourceStream* source, ws_frame_type_t type, bool useMask, bool isFin) { - for(unsigned i = 0; i < websocketList.count(); i++) { - websocketList[i]->send(message, length, type); + if(source == nullptr) { + return false; } -} -size_t WebsocketConnection::encodeFrame(ws_frame_type_t type, const char* inData, size_t inLength, char* outData, - size_t outLength, bool useMask, bool isFin) -{ - if(inLength > 0xFFFF) { - return 0; // we don't support big payloads yet + if(connection == nullptr) { + return false; } - int headerLength = 2; - uint8_t maskKey[4] = {0x00, 0x00, 0x00, 0x00}; - if(inLength > 125) { - headerLength = 4; + if(!activated) { + debug_e("WS Connection is not activated yet!"); + return false; } - if(useMask) { - headerLength += 4; // if present, mask is 4 bytes in header before payload + + + int available = source->available(); + if(available < 1) { + debug_e("Streams without known size are not supported"); + return false; } - if(headerLength + inLength > outLength) { - // not enough memory to store the data - return 0; + debug_d("Sending: %d bytes, Type: %d\n", available, type); + + size_t packetLength = 2; + uint16_t lengthValue = available; + + // calculate message length .... + if (available <= 125) { + lengthValue = available; + } + else if (available < 65536) { + lengthValue = 126; + packetLength += 2; + } + else { + lengthValue = 127; + packetLength += 8; + } + + if(useMask) { + packetLength += 4; // we use mask with size 4 bytes } - memset(outData, 0, headerLength); //set initial header state to be all zero + uint8_t packet[packetLength]; + memset(packet, 0, packetLength); int i = 0; // byte 0 if(isFin) { - outData[i] |= bit(7); // set Fin + packet[i] |= bit(7); // set Fin } - outData[i++] |= (uint8_t)type; // set opcode - + packet[i++] |= (uint8_t)type; // set opcode // byte 1 if(useMask) { - outData[i] |= bit(7); // set mask + packet[i] |= bit(7); // set mask } - if(inLength < 126) { - outData[i++] |= inLength; - } else { - outData[i++] |= 126; - outData[i++] = ((inLength >> 8) & 0xFF); - outData[i++] = (inLength & 0xFF); + // length + if(lengthValue < 126) { + packet[i++] |= lengthValue; + } else if (lengthValue == 126){ + packet[i++] |= 126; + packet[i++] = (available >> 8) & 0xFF; + packet[i++] = available & 0xFF; + } + else if(lengthValue == 127){ + packet[i++] |= 127; + packet[i++] = 0; + packet[i++] = 0; + packet[i++] = 0; + packet[i++] = 0; + packet[i++] = (available >> 24) & 0xFF; + packet[i++] = (available >> 16) & 0xFF; + packet[i++] = (available >> 8) & 0xFF; + packet[i++] = (available) & 0xFF; } if(useMask) { + uint8_t maskKey[4] = {0x00, 0x00, 0x00, 0x00}; for(uint8_t x = 0; x < sizeof(maskKey); x++) { maskKey[x] = (char)os_random(); - outData[i++] = maskKey[x]; + packet[i++] = maskKey[x]; } - for(size_t x = 0; x < inLength; x++) { - outData[i++] = (inData[x] ^ maskKey[x % 4]); - } - } else { - memcpy(&outData[i], inData, inLength); - i += inLength; + auto xorStream = new XorOutputStream(source, maskKey, sizeof(maskKey)); + source = xorStream; } - return i; + // send the header + if(!connection->send(reinterpret_cast(packet), packetLength)) { + return false; + } + + return connection->send(source); +} + +void WebsocketConnection::broadcast(const char* message, size_t length, ws_frame_type_t type) +{ + char* copy = new char[length]; + memcpy(copy, message, length); + std::shared_ptr data(copy, [](const char* ptr) { + delete[] ptr; + }); + + for(unsigned i = 0; i < websocketList.count(); i++) { + auto stream = new SharedMemoryStream(data, length); + websocketList[i]->send(stream, type); + } } void WebsocketConnection::close() diff --git a/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.h b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.h index 75b6ee24e1..6c285e95dd 100644 --- a/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.h +++ b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.h @@ -12,6 +12,7 @@ #include "Network/TcpServer.h" #include "../HttpConnection.h" + extern "C" { #include "ws_parser/ws_parser.h" } @@ -90,7 +91,7 @@ class WebsocketConnection * @param length Quantity of data in message * @param type */ - virtual void send(const char* message, size_t length, ws_frame_type_t type = WS_FRAME_TEXT); + bool send(const char* message, size_t length, ws_frame_type_t type = WS_FRAME_TEXT); /** * @brief Sends websocket message from a String @@ -98,11 +99,22 @@ class WebsocketConnection * @param type * @note A String may contain arbitrary data, not just text, so can use this for any frame type */ - void send(const String& message, ws_frame_type_t type = WS_FRAME_TEXT) + bool send(const String& message, ws_frame_type_t type = WS_FRAME_TEXT) { - send(message.c_str(), message.length(), type); + return send(message.c_str(), message.length(), type); } + /** + * @brief Sends websocket message from a stream + * @param stream + * @param type + * @param useMask MUST be true for client connections + * @param isFin true if this is the final frame + * + * @retval bool true on success + */ + bool send(IDataSourceStream* stream, ws_frame_type_t type = WS_FRAME_TEXT, bool useMask = false, bool isFin = true); + /** * @brief Broadcasts a message to all active websocket connections * @param message @@ -125,9 +137,9 @@ class WebsocketConnection * @brief Sends a string websocket message * @param message */ - void sendString(const String& message) + bool sendString(const String& message) { - send(message, WS_FRAME_TEXT); + return send(message, WS_FRAME_TEXT); } /** @@ -135,9 +147,9 @@ class WebsocketConnection * @param data * @param length */ - void sendBinary(const uint8_t* data, size_t length) + bool sendBinary(const uint8_t* data, size_t length) { - send(reinterpret_cast(data), length, WS_FRAME_BINARY); + return send(reinterpret_cast(data), length, WS_FRAME_BINARY); } /** @@ -287,19 +299,6 @@ class WebsocketConnection */ bool processFrame(TcpClient& client, char* at, int size); - /** @brief Encode user content into a valid websocket frame - * @param type - * @param inData - * @param inLength - * @param outData - * @param outLength - * @param useMask MUST be true for client connections - * @param isFin true if this is the final frame - * @retval size_t Size of encoded frame - */ - size_t encodeFrame(ws_frame_type_t type, const char* inData, size_t inLength, char* outData, size_t outLength, - bool useMask = true, bool isFin = true); - protected: WebsocketDelegate wsConnect = nullptr; WebsocketMessageDelegate wsMessage = nullptr; diff --git a/Sming/Components/Network/src/Network/TcpClient.cpp b/Sming/Components/Network/src/Network/TcpClient.cpp index 3f24675a9e..1acf6038c6 100644 --- a/Sming/Components/Network/src/Network/TcpClient.cpp +++ b/Sming/Components/Network/src/Network/TcpClient.cpp @@ -47,9 +47,6 @@ bool TcpClient::send(const char* data, uint16_t len, bool forceCloseAfterSent) auto memoryStream = static_cast(stream); if(memoryStream == nullptr || memoryStream->getStreamType() != eSST_MemoryWritable) { memoryStream = new MemoryDataStream(); - if(stream == nullptr) { - stream = memoryStream; - } } if(memoryStream->write(data, len) != len) { @@ -76,13 +73,33 @@ bool TcpClient::send(IDataSourceStream* source, bool forceCloseAfterSent) else if(stream != source){ auto chainStream = static_cast(stream); if(chainStream != nullptr && chainStream->getStreamType() == eSST_Chain) { - chainStream->attachStream(source); + if(!chainStream->attachStream(source)) { + debug_w("Unable to attach source to existing stream chain!"); + delete source; + return false; + } } else { debug_d("Creating stream chain ..."); chainStream = new StreamChain(); - chainStream->attachStream(stream); - chainStream->attachStream(source); + if(!chainStream) { + delete source; + debug_w("Unable to create stream chain!"); + return false; + } + + if(!chainStream->attachStream(stream)) { + delete source; + debug_w("Unable to attach stream to new chain!"); + return false; + } + + if(!chainStream->attachStream(source)) { + delete source; + debug_w("Unable to attach source to new chain!"); + return false; + } + stream = chainStream; } } diff --git a/Sming/Components/Network/src/Network/TcpClient.h b/Sming/Components/Network/src/Network/TcpClient.h index 0056409503..74b93fa14c 100644 --- a/Sming/Components/Network/src/Network/TcpClient.h +++ b/Sming/Components/Network/src/Network/TcpClient.h @@ -107,6 +107,16 @@ class TcpClient : public TcpConnection return send(data.c_str(), data.length(), forceCloseAfterSent); } + /** + * @brief Sends data stream + * + * @note This function takes ownership of the stream pointer! + * + * @param source stream pointer + * @param forceCloseAfterSent + * + * @retval bool true when the stream can be used. When false the stream will be deleted. + */ bool send(IDataSourceStream* source, bool forceCloseAfterSent = false); bool isProcessing() diff --git a/Sming/Core/Data/Stream/DataSourceStream.h b/Sming/Core/Data/Stream/DataSourceStream.h index 86b4494faa..d0f961cbe8 100644 --- a/Sming/Core/Data/Stream/DataSourceStream.h +++ b/Sming/Core/Data/Stream/DataSourceStream.h @@ -33,6 +33,7 @@ enum StreamType { eSST_JsonObject, ///< JSON object data stream eSST_User, ///< User defined data stream eSST_Chain, ///< A stream (chain) containing multiple streams + eSST_Transform, ///< A stream that is transforming the data eSST_Unknown ///< Unknown data stream type }; diff --git a/Sming/Core/Data/Stream/LimitedMemoryStream.h b/Sming/Core/Data/Stream/LimitedMemoryStream.h index b414b6edef..9457482d91 100644 --- a/Sming/Core/Data/Stream/LimitedMemoryStream.h +++ b/Sming/Core/Data/Stream/LimitedMemoryStream.h @@ -35,7 +35,7 @@ class LimitedMemoryStream : public ReadWriteStream /** @brief Constructor to set size of internal buffer * @param length Size of buffer - * @note The actual momory for the buffer will be allocated at the first write operation. + * @note The actual memory for the buffer will be allocated at the first write operation. */ LimitedMemoryStream(size_t length) : LimitedMemoryStream(nullptr, length, 0, false) { diff --git a/Sming/Core/Data/Stream/SharedMemoryStream.h b/Sming/Core/Data/Stream/SharedMemoryStream.h new file mode 100644 index 0000000000..6a6ff0bf5e --- /dev/null +++ b/Sming/Core/Data/Stream/SharedMemoryStream.h @@ -0,0 +1,83 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * SharedMemoryStream.h + * + ****/ + +#pragma once + +#include "DataSourceStream.h" +#include + +/** + * @brief Memory stream operating on fixed shared buffer. + * + * @ingroup stream + */ + +class SharedMemoryStream : public IDataSourceStream +{ +public: + /** @brief Constructor for use with pre-existing buffer + * @param buffer + * @param capacity Size of buffer + */ + SharedMemoryStream(std::shared_ptr(buffer), size_t size) : buffer(buffer), capacity(size) + { + } + + StreamType getStreamType() const override + { + return eSST_Memory; + } + + /** @brief Get a pointer to the current position + * @retval "const char*" Pointer to current cursor position within the data stream + */ + char* getStreamPointer() const + { + return (char*)(buffer.get() + readPos); + } + + int available() override + { + return capacity - readPos; + } + + size_t getCapacity() const + { + return capacity; + } + + uint16_t readMemoryBlock(char* data, int bufSize) override + { + int written = std::min(bufSize, available()); + memcpy(data, buffer.get() + readPos, written); + + return written; + } + + bool seek(int len) override + { + if(readPos + len > capacity) { + return false; + } + + readPos += len; + return true; + } + + bool isFinished() override + { + return available() <= 0; + } + +private: + std::shared_ptr buffer; + size_t capacity; + size_t readPos{0}; +}; diff --git a/Sming/Core/Data/Stream/StreamChain.h b/Sming/Core/Data/Stream/StreamChain.h index d328441b4f..5cf24cdb6c 100644 --- a/Sming/Core/Data/Stream/StreamChain.h +++ b/Sming/Core/Data/Stream/StreamChain.h @@ -19,7 +19,7 @@ /** * @brief Limit on number of streams in a chain */ -#define MAX_STREAM_CHAIN_SIZE 10 +#define MAX_STREAM_CHAIN_SIZE 100 #endif /** diff --git a/tests/HostTests/modules/Stream.cpp b/tests/HostTests/modules/Stream.cpp index b5894ea850..3d967ebb9c 100644 --- a/tests/HostTests/modules/Stream.cpp +++ b/tests/HostTests/modules/Stream.cpp @@ -3,7 +3,10 @@ #include #include #include +#include +#include #include +#include DEFINE_FSTR_LOCAL(template1, "Stream containing {var1}, {var2} and {var3}. {} {{}} {{12345") DEFINE_FSTR_LOCAL(template1_1, "Stream containing value #1, value #2 and {var3}. {} {{}} {{12345") @@ -23,6 +26,8 @@ class StreamTest : public TestGroup { const FlashString& FS_abstract = Resource::abstract_txt; + MallocCount::setLogThreshold(0); + TEST_CASE("MemoryDataStream::moveString") { FSTR::Stream src(FS_abstract); @@ -127,6 +132,81 @@ class StreamTest : public TestGroup REQUIRE(mem.moveString(s)); REQUIRE(Resource::multipart_result == s); } + + TEST_CASE("XorOutputStream") + { + auto mem = new MemoryDataStream(); + String input = "For testing, hack the boundary value so we can compare it against a reference output"; + mem->write(input.c_str(), input.length()); + + uint8_t maskKey[4] = {0x00, 0x01, 0x02, 0x03}; + for(uint8_t x = 0; x < sizeof(maskKey); x++) { + maskKey[x] = (char)os_random(); + } + + XorOutputStream encodeStream(mem, maskKey, sizeof(maskKey)); + auto maskedStream = new MemoryDataStream(); + while(!encodeStream.isFinished()) { + char buffer[21]; + uint16_t obtained = encodeStream.readBytes(buffer, sizeof(buffer)); + maskedStream->write(buffer, obtained); + } + + XorOutputStream decodeStream(maskedStream, maskKey, sizeof(maskKey)); + MemoryDataStream unmaskedStream; + while(!decodeStream.isFinished()) { + char buffer[13]; + uint16_t obtained = decodeStream.readBytes(buffer, sizeof(buffer)); + unmaskedStream.write(buffer, obtained); + } + + String unmaskedString; + unmaskedStream.moveString(unmaskedString); + + // running xor two times should produce the original content + REQUIRE(input == unmaskedString); + debug_hex(DBG, "Text", unmaskedString.c_str(), unmaskedString.length()); + } + + auto memStart = MallocCount::getCurrent(); + + TEST_CASE("SharedMemoryStream") + { + char* message = new char[18]; + memcpy(message, "Wonderful data...", 17); + message[17] = '\0'; + + std::shared_ptr data(message, [&message](const char* p) { delete[] p; }); + + debug_d("RefCount: %d", data.use_count()); + + Vector list; + for(unsigned i = 0; i < 4; i++) { + list.addElement(new SharedMemoryStream(data, strlen(message))); + } + + for(unsigned i = 0; i < list.count(); i++) { + size_t bufferSize = 5; + char buffer[bufferSize]{}; + auto element = list[i]; + + String output; + while(!element->isFinished()) { + size_t consumed = element->readBytes(buffer, bufferSize); + output.concat(buffer, consumed); + } + + REQUIRE(output.equals(message)); + + delete element; + debug_d("RefCount: %d", data.use_count()); + } + + REQUIRE(data.use_count() == 1); + } + + debug_i("memStart = %d, now mem = %d", memStart, MallocCount::getCurrent()); + REQUIRE(memStart == MallocCount::getCurrent()); } private: From b437b025bc818b2ec48d08ef8966c1d53c6bfedc Mon Sep 17 00:00:00 2001 From: slaff Date: Wed, 7 Jul 2021 09:23:40 +0200 Subject: [PATCH 036/130] Fixed possible memory leaks. (#2344) --- .../src/Network/Http/Websocket/WebsocketConnection.cpp | 1 + Sming/Components/Network/src/Network/TcpClient.cpp | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.cpp b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.cpp index 03a72ee095..9fefe9c931 100644 --- a/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.cpp +++ b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.cpp @@ -283,6 +283,7 @@ bool WebsocketConnection::send(IDataSourceStream* source, ws_frame_type_t type, // send the header if(!connection->send(reinterpret_cast(packet), packetLength)) { + delete source; return false; } diff --git a/Sming/Components/Network/src/Network/TcpClient.cpp b/Sming/Components/Network/src/Network/TcpClient.cpp index 1acf6038c6..af36b93605 100644 --- a/Sming/Components/Network/src/Network/TcpClient.cpp +++ b/Sming/Components/Network/src/Network/TcpClient.cpp @@ -71,8 +71,8 @@ bool TcpClient::send(IDataSourceStream* source, bool forceCloseAfterSent) stream = source; } else if(stream != source){ - auto chainStream = static_cast(stream); - if(chainStream != nullptr && chainStream->getStreamType() == eSST_Chain) { + if(stream->getStreamType() == eSST_Chain) { + auto chainStream = static_cast(stream); if(!chainStream->attachStream(source)) { debug_w("Unable to attach source to existing stream chain!"); delete source; @@ -81,8 +81,8 @@ bool TcpClient::send(IDataSourceStream* source, bool forceCloseAfterSent) } else { debug_d("Creating stream chain ..."); - chainStream = new StreamChain(); - if(!chainStream) { + auto chainStream = new StreamChain(); + if(chainStream == nullptr) { delete source; debug_w("Unable to create stream chain!"); return false; @@ -90,12 +90,14 @@ bool TcpClient::send(IDataSourceStream* source, bool forceCloseAfterSent) if(!chainStream->attachStream(stream)) { delete source; + delete chainStream; debug_w("Unable to attach stream to new chain!"); return false; } if(!chainStream->attachStream(source)) { delete source; + delete chainStream; debug_w("Unable to attach source to new chain!"); return false; } From 8b4f383dcc5aac81e1a68f694dfdd023059cdd03 Mon Sep 17 00:00:00 2001 From: slaff Date: Wed, 7 Jul 2021 15:44:16 +0200 Subject: [PATCH 037/130] Updated codacy badge. (#2345) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 74afea59b8..514e6da443 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ It supports multiple architectures as ESP8266 for example. [![Sponsors](https://opencollective.com/Sming/sponsors/badge.svg)](#financial-contributions) [![Download](https://img.shields.io/badge/download-~1.7M-orange.svg)](https://github.com/SmingHub/Sming/releases/latest) [![Build](https://travis-ci.org/SmingHub/Sming.svg?branch=develop)](https://travis-ci.org/SmingHub/Sming) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/a450c9b4df08406dba81456261304ace)](https://app.codacy.com/app/slaff2/SmingOfficial?utm_source=github.com&utm_medium=referral&utm_content=SmingHub/Sming&utm_campaign=Badge_Grade_Dashboard) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/23ff16f8d550440787125b0d25ba7ada)](https://www.codacy.com/gh/SmingHub/Sming/dashboard?utm_source=github.com&utm_medium=referral&utm_content=SmingHub/Sming&utm_campaign=Badge_Grade) [![Coverity Badge](https://img.shields.io/coverity/scan/12007.svg)](https://scan.coverity.com/projects/sminghub-sming) If you like **Sming**, give it a star, or fork it and [contribute](#contribute)! From 112da39f9d718a37e937de8825f6faf4a1c45cc1 Mon Sep 17 00:00:00 2001 From: slaff Date: Fri, 9 Jul 2021 15:16:46 +0200 Subject: [PATCH 038/130] Added method to return a list of stations connected to an Access Point(AP) (#2346) * Added code to list stations connected to our Access Point. * Re-added coding style checks. * Minor update to XorOutputStream * Template SharedMemoryStream, remove `getStreamPointer` * Add StationList for Host * Add toString(WifiAuthMode) * Remove cruft. Co-authored-by: mikee47 --- Sming/Components/Network/Arch/.cs | 0 .../Arch/Esp32/Platform/AccessPointImpl.cpp | 12 +- .../Arch/Esp32/Platform/AccessPointImpl.h | 1 + .../Esp32/Platform/LwipAdapter.cpp.changed | 876 ------------------ .../Arch/Esp32/Platform/LwipAdapter.cpp.copy | 861 ----------------- .../Arch/Esp32/Platform/StationListImpl.h | 64 ++ .../Arch/Esp8266/Platform/AccessPointImpl.cpp | 6 + .../Arch/Esp8266/Platform/AccessPointImpl.h | 1 + .../Arch/Esp8266/Platform/StationListImpl.h | 62 ++ .../Arch/Host/Platform/AccessPointImpl.cpp | 6 + .../Arch/Host/Platform/AccessPointImpl.h | 1 + .../Arch/Host/Platform/StationListImpl.h | 52 ++ Sming/Components/Network/src/.cs | 0 .../Network/src/Data/Stream/XorOutputStream.h | 24 +- .../Http/Websocket/WebsocketConnection.cpp | 24 +- .../Network/src/Network/MqttClient.h | 2 +- .../Network/src/Network/TcpClient.cpp | 8 +- .../Network/src/Network/WebHelpers/base64.h | 3 +- .../Network/src/Network/WebHelpers/escape.h | 3 +- .../Network/src/Platform/AccessPoint.h | 8 + .../Network/src/Platform/BssInfo.cpp | 6 +- .../Components/Network/src/Platform/BssInfo.h | 7 +- .../Network/src/Platform/StationList.h | 44 + Sming/Core/Data/Range.h | 107 +++ Sming/Core/Data/Stream/SharedMemoryStream.h | 25 +- .../{modules => Arch/Host}/Hosted.cpp | 0 tests/HostTests/component.mk | 12 +- tests/HostTests/include/modules.h | 9 +- tests/HostTests/modules/Stream.cpp | 10 +- 29 files changed, 430 insertions(+), 1804 deletions(-) create mode 100644 Sming/Components/Network/Arch/.cs delete mode 100644 Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp.changed delete mode 100644 Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp.copy create mode 100644 Sming/Components/Network/Arch/Esp32/Platform/StationListImpl.h create mode 100644 Sming/Components/Network/Arch/Esp8266/Platform/StationListImpl.h create mode 100644 Sming/Components/Network/Arch/Host/Platform/StationListImpl.h create mode 100644 Sming/Components/Network/src/.cs create mode 100644 Sming/Components/Network/src/Platform/StationList.h create mode 100644 Sming/Core/Data/Range.h rename tests/HostTests/{modules => Arch/Host}/Hosted.cpp (100%) diff --git a/Sming/Components/Network/Arch/.cs b/Sming/Components/Network/Arch/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.cpp b/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.cpp index a6cda7928a..a081708842 100644 --- a/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.cpp +++ b/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.cpp @@ -9,6 +9,7 @@ ****/ #include "AccessPointImpl.h" +#include "StationListImpl.h" #include #include #include @@ -164,7 +165,7 @@ String AccessPointImpl::getSSID() const { wifi_config_t config{}; if(esp_wifi_get_config(ESP_IF_WIFI_AP, &config) != ESP_OK) { - debugf("Can't read station configuration!"); + debug_w("Can't read station configuration!"); return nullptr; } auto ssid = reinterpret_cast(config.ap.ssid); @@ -176,14 +177,19 @@ String AccessPointImpl::getPassword() const { wifi_config_t config{}; if(esp_wifi_get_config(ESP_IF_WIFI_AP, &config) != ESP_OK) { - debugf("Can't read station configuration!"); + debug_w("Can't read station configuration!"); return nullptr; } auto pwd = reinterpret_cast(config.ap.password); - debugf("Pass: %s", pwd); + debug_d("Pass: %s", pwd); return pwd; } +std::unique_ptr AccessPointImpl::getStations() const +{ + return std::unique_ptr(new StationListImpl); +} + void AccessPointImpl::onSystemReady() { } diff --git a/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.h b/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.h index 00f299cab1..4d6cc073ef 100644 --- a/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.h +++ b/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.h @@ -36,6 +36,7 @@ class AccessPointImpl : public AccessPointClass, protected ISystemReadyHandler IpAddress getNetworkBroadcast() const override; String getSSID() const override; String getPassword() const override; + std::unique_ptr getStations() const override; protected: void onSystemReady() override; diff --git a/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp.changed b/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp.changed deleted file mode 100644 index 232b82b4cc..0000000000 --- a/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp.changed +++ /dev/null @@ -1,876 +0,0 @@ -/** - A big thank you to Hristo Gochkov and his Asynchronous TCP library - Big chunks of the code are inspired from this project. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "LwipAdapter.h" -#include -#include "esp_task_wdt.h" -extern "C" { -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "lwip/priv/tcpip_priv.h" -} - -// TODO: Tune these.... -#define CONFIG_ASYNC_TCP_RUNNING_CORE 1 -#define CONFIG_ASYNC_TCP_USE_WDT 1 - -typedef enum { - LWIP_TCP_SENT, - LWIP_TCP_RECV, - LWIP_TCP_FIN, - LWIP_TCP_ERROR, - LWIP_TCP_POLL, - LWIP_TCP_CLEAR, - LWIP_TCP_ACCEPT, - LWIP_TCP_CONNECTED, - LWIP_TCP_DNS -} lwip_event_t; - -typedef struct { - lwip_event_t event; - void* arg; - union { - struct { - tcp_pcb* pcb; - int8_t err; - } connected; - struct { - int8_t err; - } error; - struct { - tcp_pcb* pcb; - uint16_t len; - } sent; - struct { - tcp_pcb* pcb; - pbuf* pb; - int8_t err; - } recv; - struct { - tcp_pcb* pcb; - int8_t err; - } fin; - struct { - tcp_pcb* pcb; - } poll; - struct { - tcp_pcb* pcb; - int8_t err; - } accept; - struct { - const char* name; - ip_addr_t addr; - } dns; - }; -} lwip_event_packet_t; - -struct DnsLookup { - TcpConnection* con; - int port; -}; - -typedef struct { - TcpConnection* connection; - uint8_t closed; // 0 - open, 1 - closed - struct { - tcp_connected_fn callback; - } connect; - struct { - tcp_accept_fn callback; - } accept; - struct { - tcp_recv_fn callback; - } receive; - struct { - tcp_sent_fn callback; - } sent; - struct { - tcp_poll_fn callback; - } poll; - struct { - tcp_err_fn callback; - } error; - struct { - dns_found_callback callback; - DnsLookup* lookup; - } dns; - // TODO: ... -} tcp_api_arg_t; - -typedef struct { - struct tcpip_api_call_data call; - tcp_pcb* pcb; - int8_t err; - union { - struct { - const char* data; - size_t size; - uint8_t apiflags; - } write; - size_t received; - struct { - const ip_addr_t* addr; - uint16_t port; - tcp_connected_fn cb; - } connect; - struct { - const ip_addr_t* addr; - uint16_t port; - } bind; - uint8_t backlog; - }; -} tcp_api_call_t; - -// Event Logic -static xQueueHandle _async_queue; -static TaskHandle_t _async_service_task_handle = NULL; - -// TODO: remove the slots.. -//SemaphoreHandle_t _slots_lock; -//const int _number_of_closed_slots = CONFIG_LWIP_MAX_ACTIVE_TCP; -//static uint32_t _closed_slots[_number_of_closed_slots]; -//static uint32_t _closed_index = []() { -// _slots_lock = xSemaphoreCreateBinary(); -// xSemaphoreGive(_slots_lock); -// for(int i = 0; i < _number_of_closed_slots; ++i) { -// _closed_slots[i] = 1; -// } -// return 1; -//}(); - -static inline bool _init_async_event_queue() -{ - if(!_async_queue) { - _async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t*)); - if(!_async_queue) { - return false; - } - } - return true; -} - -static inline bool _send_async_event(lwip_event_packet_t** e) -{ - return _async_queue && xQueueSend(_async_queue, e, portMAX_DELAY) == pdPASS; -} - -static inline bool _prepend_async_event(lwip_event_packet_t** e) -{ - return _async_queue && xQueueSendToFront(_async_queue, e, portMAX_DELAY) == pdPASS; -} - -static inline bool _get_async_event(lwip_event_packet_t** e) -{ - return _async_queue && xQueueReceive(_async_queue, e, portMAX_DELAY) == pdPASS; -} - -static bool _remove_events_with_arg(void* arg) -{ - lwip_event_packet_t* first_packet = NULL; - lwip_event_packet_t* packet = NULL; - - if(!_async_queue) { - return false; - } - //figure out which is the first packet so we can keep the order - while(!first_packet) { - if(xQueueReceive(_async_queue, &first_packet, 0) != pdPASS) { - return false; - } - //discard packet if matching - if((int)first_packet->arg == (int)arg) { - free(first_packet); - first_packet = NULL; - //return first packet to the back of the queue - } else if(xQueueSend(_async_queue, &first_packet, portMAX_DELAY) != pdPASS) { - return false; - } - } - - while(xQueuePeek(_async_queue, &packet, 0) == pdPASS && packet != first_packet) { - if(xQueueReceive(_async_queue, &packet, 0) != pdPASS) { - return false; - } - if((int)packet->arg == (int)arg) { - free(packet); - packet = NULL; - } else if(xQueueSend(_async_queue, &packet, portMAX_DELAY) != pdPASS) { - return false; - } - } - return true; -} - -static void _handle_async_event(lwip_event_packet_t* e) -{ - tcp_api_arg_t* arg = reinterpret_cast(e->arg); - - if(e->arg == NULL) { - // do nothing when arg is NULL - debug_d("event arg == NULL (before cast: 0x%08x): 0x%08x", e->arg, e->recv.pcb); - } else if(e->event == LWIP_TCP_CLEAR) { - _remove_events_with_arg(e->arg); - } else if(e->event == LWIP_TCP_RECV) { - debug_d("-R: 0x%08x", e->recv.pcb); - arg->receive.callback(arg->connection, e->recv.pcb, e->recv.pb, e->recv.err); - } else if(e->event == LWIP_TCP_FIN) { - debug_d("-F: 0x%08x", e->fin.pcb); - // TODO: Handle this event too... - // TcpConnection::_s_fin(e->arg, e->fin.pcb, e->fin.err); - } else if(e->event == LWIP_TCP_SENT) { - debug_d("-S: 0x%08x", e->sent.pcb); - arg->sent.callback(arg->connection, e->sent.pcb, e->sent.len); - } else if(e->event == LWIP_TCP_POLL) { - debug_d("-P: 0x%08x", e->poll.pcb); - arg->poll.callback(arg->connection, e->poll.pcb); - } else if(e->event == LWIP_TCP_ERROR) { - debug_d("-E: 0x%08x %d", e->arg, e->error.err); - arg->error.callback(arg->connection, e->error.err); - } else if(e->event == LWIP_TCP_CONNECTED) { - debug_d("C: 0x%08x %d", e->arg, e->connected.err); - arg->connect.callback(arg->connection, e->connected.pcb, e->connected.err); - } else if(e->event == LWIP_TCP_ACCEPT) { - debug_d("A: 0x%08x 0x%08x", e->arg, e->accept.pcb); - arg->accept.callback(arg->connection, e->accept.pcb, e->accept.err); - } else if(e->event == LWIP_TCP_DNS) { - debug_d("D: 0x%08x %s = %s", e->arg, e->dns.name, ipaddr_ntoa(&e->dns.addr)); - arg->dns.callback(e->dns.name, &e->dns.addr, arg->dns.lookup); - delete arg; - } - free((void*)(e)); -} - -static void _async_service_task(void* pvParameters) -{ - lwip_event_packet_t* packet = NULL; - for(;;) { - if(_get_async_event(&packet)) { -#if CONFIG_ASYNC_TCP_USE_WDT - if(esp_task_wdt_add(NULL) != ESP_OK) { - debug_e("Failed to add async task to WDT"); - } -#endif - _handle_async_event(packet); -#if CONFIG_ASYNC_TCP_USE_WDT - if(esp_task_wdt_delete(NULL) != ESP_OK) { - debug_e("Failed to remove loop task from WDT"); - } -#endif - } - } - vTaskDelete(NULL); - _async_service_task_handle = NULL; -} -/* -static void _stop_async_task(){ - if(_async_service_task_handle){ - vTaskDelete(_async_service_task_handle); - _async_service_task_handle = NULL; - } -} -*/ -static bool _start_async_task() -{ - if(!_init_async_event_queue()) { - return false; - } - if(!_async_service_task_handle) { - xTaskCreatePinnedToCore(_async_service_task, "async_tcp", 8192 * 2, NULL, 3, &_async_service_task_handle, - CONFIG_ASYNC_TCP_RUNNING_CORE); - if(!_async_service_task_handle) { - return false; - } - debug_d("Starting Async Event Queue."); - } - return true; -} - -// Low-Level LwIP Functions -static int8_t _tcp_clear_events(void* arg) -{ - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_CLEAR; - e->arg = arg; - if(!_prepend_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_connected(void* arg, tcp_pcb* pcb, int8_t err) -{ - debug_d("+C: 0x%08x", pcb); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - - e->event = LWIP_TCP_CONNECTED; - e->arg = arg; - e->connected.pcb = pcb; - e->connected.err = err; - if(!_prepend_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_poll(void* arg, struct tcp_pcb* pcb) -{ - debug_d("+P: 0x%08x", pcb); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_POLL; - e->arg = arg; - e->poll.pcb = pcb; - if(!_send_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* pb, int8_t err) -{ - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->arg = arg; - if(pb) { - debug_d("+R: 0x%08x", pcb); - e->event = LWIP_TCP_RECV; - e->recv.pcb = pcb; - e->recv.pb = pb; - e->recv.err = err; - // } else { - // //debug_d("+F: 0x%08x", pcb); - // e->event = LWIP_TCP_FIN; - // e->fin.pcb = pcb; - // e->fin.err = err; - // //close the PCB in LwIP thread - // // TODO: - //// TcpConnection::_s_lwip_fin(e->arg, e->fin.pcb, e->fin.err); - } - if(!_send_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_sent(void* arg, struct tcp_pcb* pcb, uint16_t len) -{ - debug_d("+S: 0x%08x", pcb); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_SENT; - e->arg = arg; - e->sent.pcb = pcb; - e->sent.len = len; - if(!_send_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -static void _tcp_error(void* arg, int8_t err) -{ - debug_d("+E: 0x%08x", arg); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_ERROR; - e->arg = arg; - e->error.err = err; - if(!_send_async_event(&e)) { - free((void*)(e)); - } -} - -static void _tcp_dns_found(const char* name, const ip_addr* ipaddr, void* arg) -{ - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - debug_d("+DNS: name=%s ipaddr=0x%08x arg=%x", name, ipaddr, arg); - e->event = LWIP_TCP_DNS; - e->arg = arg; - e->dns.name = name; - if(ipaddr) { - memcpy(&e->dns.addr, ipaddr, sizeof(struct ip_addr)); - } else { - memset(&e->dns.addr, 0, sizeof(e->dns.addr)); - } - if(!_send_async_event(&e)) { - free((void*)(e)); - } -} - -//Used to switch out from LwIP thread -static int8_t _tcp_accept(void* arg, struct tcp_pcb* newpcb, err_t err) -{ - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_ACCEPT; - e->arg = arg; - e->accept.pcb = newpcb; - e->accept.err = err; - if(!_prepend_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -/* - * TCP/IP API Calls - * */ - -static void _update_tcp_arg(struct tcp_pcb* pcb, void* arg) -{ - tcp_api_arg_t* apiArgument = reinterpret_cast(pcb->callback_arg); - // delete the current argument if this is an api argument - if(apiArgument != nullptr) { - debug_d("_update_tcp_arg: 0x%08x deleting argument: 0x%08x", pcb, pcb->callback_arg); - delete apiArgument; - } - - debug_d("_update_tcp_arg: 0x%08x updated argument: 0x%08x", pcb, arg); - tcp_arg(pcb, arg); -} - -static err_t _tcp_output_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = ERR_CONN; - tcp_api_arg_t* arg = reinterpret_cast(msg->pcb->callback_arg); - if(arg != nullptr && arg->closed) { - return msg->err; - } - msg->err = tcp_output(msg->pcb); - return msg->err; -} - -static esp_err_t _tcp_output(tcp_pcb* pcb) -{ - if(!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - tcpip_api_call(_tcp_output_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_write_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = ERR_CONN; - tcp_api_arg_t* arg = reinterpret_cast(msg->pcb->callback_arg); - if(arg != nullptr && arg->closed) { - return msg->err; - } - msg->err = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags); - return msg->err; -} - -static esp_err_t _tcp_write(tcp_pcb* pcb, const char* data, size_t size, uint8_t apiflags) -{ - if(!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - - msg.write.data = data; - msg.write.size = size; - msg.write.apiflags = apiflags; - tcpip_api_call(_tcp_write_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_recved_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = ERR_CONN; - tcp_api_arg_t* arg = reinterpret_cast(msg->pcb->callback_arg); - if(arg != nullptr && arg->closed) { - return msg->err; - } - - tcp_recved(msg->pcb, msg->received); - return msg->err; -} - -static esp_err_t _tcp_recved(tcp_pcb* pcb, size_t len) -{ - if(!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - - msg.received = len; - tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_close_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = ERR_CONN; - tcp_api_arg_t* arg = reinterpret_cast(msg->pcb->callback_arg); - if(arg != nullptr) { - if(arg->closed) { - return msg->err; - } - arg->closed = 1; - } - - //if(msg->pcb->state != CLOSED) { - msg->err = tcp_close(msg->pcb); - _update_tcp_arg(msg->pcb, nullptr); - // } - _tcp_clear_events(msg->pcb); - return msg->err; -} - -static esp_err_t _tcp_close(tcp_pcb* pcb) -{ - if(!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - - tcpip_api_call(_tcp_close_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_abort_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = ERR_CONN; - tcp_api_arg_t* arg = reinterpret_cast(msg->pcb->callback_arg); - if(arg != nullptr) { - if(arg->closed) { - return msg->err; - } - arg->closed = 1; - } - - tcp_abort(msg->pcb); - return msg->err; -} - -static esp_err_t _tcp_abort(tcp_pcb* pcb) -{ - if(!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - - tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_connect_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = tcp_connect(msg->pcb, msg->connect.addr, msg->connect.port, _tcp_connected); - return msg->err; -} - -static esp_err_t _tcp_connect(tcp_pcb* pcb, const ip_addr_t* addr, uint16_t port, tcp_connected_fn cb) -{ - if(!pcb) { - return ESP_FAIL; - } - - tcp_api_call_t msg; - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg != nullptr) { - debug_d("Connecting ..."); - arg->closed = 0; - arg->connect.callback = cb; - } - - msg.pcb = pcb; - - msg.connect.addr = addr; - msg.connect.port = port; - msg.connect.cb = _tcp_connected; - tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_bind_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port); - return msg->err; -} - -static esp_err_t _tcp_bind(tcp_pcb* pcb, const ip_addr_t* addr, uint16_t port) -{ - if(!pcb) { - return ESP_FAIL; - } - tcp_api_call_t msg; - msg.pcb = pcb; - tcp_api_arg_t* arg = reinterpret_cast(msg.pcb->callback_arg); - if(arg != nullptr) { - arg->closed = 0; - } - msg.bind.addr = addr; - msg.bind.port = port; - tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_listen_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = 0; - msg->pcb = tcp_listen_with_backlog(msg->pcb, msg->backlog); - return msg->err; -} - -static tcp_pcb* _tcp_listen_with_backlog(tcp_pcb* pcb, uint8_t backlog) -{ - if(!pcb) { - return NULL; - } - tcp_api_call_t msg; - msg.pcb = pcb; - tcp_api_arg_t* arg = reinterpret_cast(msg.pcb->callback_arg); - if(arg != nullptr) { - arg->closed = 0; - } - msg.backlog = backlog ? backlog : TCP_DEFAULT_LISTEN_BACKLOG; - tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call_data*)&msg); - return msg.pcb; -} - -// High Level Functions - -tcp_pcb* async_tcp_new() -{ - return tcp_new_ip_type(IPADDR_TYPE_V4); -} - -void async_tcp_arg(struct tcp_pcb* pcb, void* arg) -{ - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* apiArgument = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr && apiArgument != nullptr) { - // we are trying to replace the current argument with nullptr. - // the async_tcp_close call should take care for releasing it.... - return; - } - - TcpConnection* connectionArg = reinterpret_cast(arg); - if(connectionArg != nullptr) { - // we have the right argument, lets wrap it - if(apiArgument == nullptr) { - apiArgument = new tcp_api_arg_t(); - memset(apiArgument, 0, sizeof(tcp_api_arg_t)); - } - apiArgument->connection = connectionArg; - _update_tcp_arg(pcb, apiArgument); - return; - } - - // This should not happen - _update_tcp_arg(pcb, arg); -} - -err_t async_tcp_connect(tcp_pcb* pcb, const ip_addr_t* ipaddr, u16_t port, tcp_connected_fn connected) -{ - if(!_start_async_task()) { - return ERR_ABRT; - } - - debug_d("Connecting: 0x%08x", pcb); - return _tcp_connect(pcb, ipaddr, port, connected); -} - -tcp_pcb* async_tcp_listen_with_backlog(struct tcp_pcb* pcb, u8_t backlog) -{ - if(!_start_async_task()) { - return nullptr; - } - - return _tcp_listen_with_backlog(pcb, backlog); -} - -err_t async_tcp_bind(struct tcp_pcb* pcb, const ip_addr_t* ipaddr, u16_t port) -{ - if(!_start_async_task()) { - return ERR_ABRT; - } - - return _tcp_bind(pcb, ipaddr, port); -} - -void async_tcp_accept(struct tcp_pcb* pcb, tcp_accept_fn callback) -{ - if(callback == nullptr) { - tcp_accept(pcb, nullptr); - return; - } - - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - // This should not happen - return; - } - - arg->accept.callback = callback; - tcp_accept(pcb, _tcp_accept); -} - -err_t async_tcp_write(struct tcp_pcb* pcb, const void* dataptr, u16_t len, u8_t apiflags) -{ - return _tcp_write(pcb, (const char*)dataptr, len, apiflags); -} - -void async_tcp_recved(struct tcp_pcb* pcb, u16_t len) -{ - _tcp_recved(pcb, len); -} - -err_t async_tcp_output(struct tcp_pcb* pcb) -{ - return _tcp_output(pcb); -} - -void async_tcp_abort(struct tcp_pcb* pcb) -{ - _tcp_abort(pcb); -} - -err_t async_tcp_close(struct tcp_pcb* pcb) -{ - return _tcp_close(pcb); -} - -void async_tcp_recv(struct tcp_pcb* pcb, tcp_recv_fn callback) -{ - if(callback == nullptr) { - tcp_recv(pcb, nullptr); - return; - } - - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - // This should not happen - return; - } - - arg->receive.callback = callback; - tcp_recv(pcb, _tcp_recv); -} - -void async_tcp_sent(struct tcp_pcb* pcb, tcp_sent_fn callback) -{ - if(callback == nullptr) { - tcp_sent(pcb, nullptr); - return; - } - - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - // This should not happen - return; - } - - arg->sent.callback = callback; - tcp_sent(pcb, _tcp_sent); -} - -void async_tcp_poll(struct tcp_pcb* pcb, tcp_poll_fn callback, u8_t interval) -{ - if(callback == nullptr) { - tcp_poll(pcb, nullptr, interval); - return; - } - - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - // This should not happen - return; - } - - arg->poll.callback = callback; - tcp_poll(pcb, _tcp_poll, interval); -} - -void async_tcp_err(struct tcp_pcb* pcb, tcp_err_fn callback) -{ - if(callback == nullptr) { - tcp_err(pcb, nullptr); - return; - } - - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - // This should not happen - return; - } - - arg->error.callback = callback; - tcp_err(pcb, _tcp_error); -} - -err_t async_dns_gethostbyname(const char* hostname, ip_addr_t* addr, dns_found_callback found, void* callback_arg) -{ - DnsLookup* dnsLookup = reinterpret_cast(callback_arg); - if(dnsLookup == nullptr) { - // This should not happen - return ERR_ABRT; - } - - tcp_api_arg_t* arg = new tcp_api_arg_t(); - if(arg == nullptr) { - // This should not happen - return ERR_ABRT; - } - - if(!_start_async_task()) { - delete arg; - return ERR_ABRT; - } - - arg->dns.callback = found; - arg->dns.lookup = dnsLookup; - return dns_gethostbyname(hostname, addr, _tcp_dns_found, arg); -} diff --git a/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp.copy b/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp.copy deleted file mode 100644 index 1fe0618d4a..0000000000 --- a/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp.copy +++ /dev/null @@ -1,861 +0,0 @@ -/** - A big thank you to Hristo Gochkov and his Asynchronous TCP library - Big chunks of the code are inspired from this project. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include -#include -#include "esp_task_wdt.h" -extern "C" { -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "lwip/priv/tcpip_priv.h" -} - -// TODO: Tune these.... -#define CONFIG_ASYNC_TCP_RUNNING_CORE 1 -#define CONFIG_ASYNC_TCP_USE_WDT 1 - -typedef enum { - LWIP_TCP_SENT, - LWIP_TCP_RECV, - LWIP_TCP_FIN, - LWIP_TCP_ERROR, - LWIP_TCP_POLL, - LWIP_TCP_CLEAR, - LWIP_TCP_ACCEPT, - LWIP_TCP_CONNECTED, - LWIP_TCP_DNS -} lwip_event_t; - -typedef struct { - lwip_event_t event; - void* arg; - union { - struct { - tcp_pcb* pcb; - int8_t err; - } connected; - struct { - int8_t err; - } error; - struct { - tcp_pcb* pcb; - uint16_t len; - } sent; - struct { - tcp_pcb* pcb; - pbuf* pb; - int8_t err; - } recv; - struct { - tcp_pcb* pcb; - int8_t err; - } fin; - struct { - tcp_pcb* pcb; - } poll; - struct { - tcp_pcb* pcb; - int8_t err; - } accept; - struct { - const char* name; - ip_addr_t addr; - } dns; - }; -} lwip_event_packet_t; - -struct DnsLookup { - TcpConnection* con; - int port; -}; - -typedef struct { - TcpConnection* connection; - uint8_t closed; // 0 - open, 1 - closed - struct { - tcp_connected_fn callback; - } connect; - struct { - tcp_accept_fn callback; - } accept; - struct { - tcp_recv_fn callback; - } receive; - struct { - tcp_sent_fn callback; - } sent; - struct { - tcp_poll_fn callback; - } poll; - struct { - tcp_err_fn callback; - } error; - struct { - dns_found_callback callback; - DnsLookup* lookup; - } dns; - // TODO: ... -} tcp_api_arg_t; - -typedef struct { - struct tcpip_api_call_data call; - tcp_pcb* pcb; - int8_t err; - union { - struct { - const char* data; - size_t size; - uint8_t apiflags; - } write; - size_t received; - struct { - const ip_addr_t* addr; - uint16_t port; - tcp_connected_fn cb; - } connect; - struct { - const ip_addr_t* addr; - uint16_t port; - } bind; - uint8_t backlog; - }; -} tcp_api_call_t; - -// Event Logic -static xQueueHandle _async_queue; -static TaskHandle_t _async_service_task_handle = NULL; - -// TODO: remove the slots.. -//SemaphoreHandle_t _slots_lock; -//const int _number_of_closed_slots = CONFIG_LWIP_MAX_ACTIVE_TCP; -//static uint32_t _closed_slots[_number_of_closed_slots]; -//static uint32_t _closed_index = []() { -// _slots_lock = xSemaphoreCreateBinary(); -// xSemaphoreGive(_slots_lock); -// for(int i = 0; i < _number_of_closed_slots; ++i) { -// _closed_slots[i] = 1; -// } -// return 1; -//}(); - -static inline bool _init_async_event_queue() -{ - if(!_async_queue) { - _async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t*)); - if(!_async_queue) { - return false; - } - } - return true; -} - -static inline bool _send_async_event(lwip_event_packet_t** e) -{ - return _async_queue && xQueueSend(_async_queue, e, portMAX_DELAY) == pdPASS; -} - -static inline bool _prepend_async_event(lwip_event_packet_t** e) -{ - return _async_queue && xQueueSendToFront(_async_queue, e, portMAX_DELAY) == pdPASS; -} - -static inline bool _get_async_event(lwip_event_packet_t** e) -{ - return _async_queue && xQueueReceive(_async_queue, e, portMAX_DELAY) == pdPASS; -} - -static bool _remove_events_with_arg(void* arg) -{ - lwip_event_packet_t* first_packet = NULL; - lwip_event_packet_t* packet = NULL; - - if(!_async_queue) { - return false; - } - //figure out which is the first packet so we can keep the order - while(!first_packet) { - if(xQueueReceive(_async_queue, &first_packet, 0) != pdPASS) { - return false; - } - //discard packet if matching - if((int)first_packet->arg == (int)arg) { - free(first_packet); - first_packet = NULL; - //return first packet to the back of the queue - } else if(xQueueSend(_async_queue, &first_packet, portMAX_DELAY) != pdPASS) { - return false; - } - } - - while(xQueuePeek(_async_queue, &packet, 0) == pdPASS && packet != first_packet) { - if(xQueueReceive(_async_queue, &packet, 0) != pdPASS) { - return false; - } - if((int)packet->arg == (int)arg) { - free(packet); - packet = NULL; - } else if(xQueueSend(_async_queue, &packet, portMAX_DELAY) != pdPASS) { - return false; - } - } - return true; -} - -static void _handle_async_event(lwip_event_packet_t* e) -{ - tcp_api_arg_t* arg = reinterpret_cast(e->arg); - - if(e->arg == NULL) { - // do nothing when arg is NULL - debug_d("event arg == NULL: 0x%08x", e->recv.pcb); - } else if(e->event == LWIP_TCP_CLEAR) { - _remove_events_with_arg(e->arg); - } else if(e->event == LWIP_TCP_RECV) { - debug_d("-R: 0x%08x", e->recv.pcb); - arg->receive.callback(arg->connection, e->recv.pcb, e->recv.pb, e->recv.err); - } else if(e->event == LWIP_TCP_FIN) { - debug_d("-F: 0x%08x", e->fin.pcb); - // TODO: Handle this event too... - // TcpConnection::_s_fin(e->arg, e->fin.pcb, e->fin.err); - } else if(e->event == LWIP_TCP_SENT) { - debug_d("-S: 0x%08x", e->sent.pcb); - arg->sent.callback(arg->connection, e->sent.pcb, e->sent.len); - } else if(e->event == LWIP_TCP_POLL) { - debug_d("-P: 0x%08x", e->poll.pcb); - arg->poll.callback(arg->connection, e->poll.pcb); - } else if(e->event == LWIP_TCP_ERROR) { - debug_d("-E: 0x%08x %d", e->arg, e->error.err); - arg->error.callback(arg->connection, e->error.err); - } else if(e->event == LWIP_TCP_CONNECTED) { - debug_d("C: 0x%08x %d", e->arg, e->connected.err); - arg->connect.callback(arg->connection, e->connected.pcb, e->connected.err); - } else if(e->event == LWIP_TCP_ACCEPT) { - debug_d("A: 0x%08x 0x%08x", e->arg, e->accept.pcb); - arg->accept.callback(arg->connection, e->accept.pcb, e->accept.err); - } else if(e->event == LWIP_TCP_DNS) { - debug_d("D: 0x%08x %s = %s", e->arg, e->dns.name, ipaddr_ntoa(&e->dns.addr)); - arg->dns.callback(e->dns.name, &e->dns.addr, arg->dns.lookup); - delete arg; - } - free((void*)(e)); -} - -static void _async_service_task(void* pvParameters) -{ - lwip_event_packet_t* packet = NULL; - for(;;) { - if(_get_async_event(&packet)) { -#if CONFIG_ASYNC_TCP_USE_WDT - if(esp_task_wdt_add(NULL) != ESP_OK) { - debug_e("Failed to add async task to WDT"); - } -#endif - _handle_async_event(packet); -#if CONFIG_ASYNC_TCP_USE_WDT - if(esp_task_wdt_delete(NULL) != ESP_OK) { - debug_e("Failed to remove loop task from WDT"); - } -#endif - } - } - vTaskDelete(NULL); - _async_service_task_handle = NULL; -} -/* -static void _stop_async_task(){ - if(_async_service_task_handle){ - vTaskDelete(_async_service_task_handle); - _async_service_task_handle = NULL; - } -} -*/ -static bool _start_async_task() -{ - if(!_init_async_event_queue()) { - return false; - } - if(!_async_service_task_handle) { - xTaskCreatePinnedToCore(_async_service_task, "async_tcp", 8192 * 2, NULL, 3, &_async_service_task_handle, - CONFIG_ASYNC_TCP_RUNNING_CORE); - if(!_async_service_task_handle) { - return false; - } - debug_d("Starting Async Event Queue."); - } - return true; -} - -// Low-Level LwIP Functions -static int8_t _tcp_clear_events(void* arg) -{ - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_CLEAR; - e->arg = arg; - if(!_prepend_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_connected(void* arg, tcp_pcb* pcb, int8_t err) -{ - debug_d("+C: 0x%08x", pcb); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - - e->event = LWIP_TCP_CONNECTED; - e->arg = arg; - e->connected.pcb = pcb; - e->connected.err = err; - if(!_prepend_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_poll(void* arg, struct tcp_pcb* pcb) -{ - debug_d("+P: 0x%08x", pcb); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_POLL; - e->arg = arg; - e->poll.pcb = pcb; - if(!_send_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* pb, int8_t err) -{ - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->arg = arg; - if(pb) { - debug_d("+R: 0x%08x", pcb); - e->event = LWIP_TCP_RECV; - e->recv.pcb = pcb; - e->recv.pb = pb; - e->recv.err = err; - // } else { - // //debug_d("+F: 0x%08x", pcb); - // e->event = LWIP_TCP_FIN; - // e->fin.pcb = pcb; - // e->fin.err = err; - // //close the PCB in LwIP thread - // // TODO: - //// TcpConnection::_s_lwip_fin(e->arg, e->fin.pcb, e->fin.err); - } - if(!_send_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_sent(void* arg, struct tcp_pcb* pcb, uint16_t len) -{ - debug_d("+S: 0x%08x", pcb); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_SENT; - e->arg = arg; - e->sent.pcb = pcb; - e->sent.len = len; - if(!_send_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -static void _tcp_error(void* arg, int8_t err) -{ - debug_d("+E: 0x%08x", arg); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_ERROR; - e->arg = arg; - e->error.err = err; - if(!_send_async_event(&e)) { - free((void*)(e)); - } -} - -static void _tcp_dns_found(const char* name, const ip_addr* ipaddr, void* arg) -{ - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - debug_d("+DNS: name=%s ipaddr=0x%08x arg=%x", name, ipaddr, arg); - e->event = LWIP_TCP_DNS; - e->arg = arg; - e->dns.name = name; - if(ipaddr) { - memcpy(&e->dns.addr, ipaddr, sizeof(struct ip_addr)); - } else { - memset(&e->dns.addr, 0, sizeof(e->dns.addr)); - } - if(!_send_async_event(&e)) { - free((void*)(e)); - } -} - -//Used to switch out from LwIP thread -static int8_t _tcp_accept(void* arg, struct tcp_pcb* newpcb, err_t err) -{ - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_ACCEPT; - e->arg = arg; - e->accept.pcb = newpcb; - e->accept.err = err; - if(!_prepend_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -/* - * TCP/IP API Calls - * */ - -static err_t _tcp_output_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = ERR_CONN; - tcp_api_arg_t* arg = reinterpret_cast(msg->pcb->callback_arg); - if(arg != nullptr && arg->closed) { - return msg->err; - } - msg->err = tcp_output(msg->pcb); - return msg->err; -} - -static esp_err_t _tcp_output(tcp_pcb* pcb) -{ - if(!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - tcpip_api_call(_tcp_output_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_write_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = ERR_CONN; - tcp_api_arg_t* arg = reinterpret_cast(msg->pcb->callback_arg); - if(arg != nullptr && arg->closed) { - return msg->err; - } - msg->err = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags); - return msg->err; -} - -static esp_err_t _tcp_write(tcp_pcb* pcb, const char* data, size_t size, uint8_t apiflags) -{ - if(!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - - msg.write.data = data; - msg.write.size = size; - msg.write.apiflags = apiflags; - tcpip_api_call(_tcp_write_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_recved_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = ERR_CONN; - tcp_api_arg_t* arg = reinterpret_cast(msg->pcb->callback_arg); - if(arg != nullptr && arg->closed) { - return msg->err; - } - - tcp_recved(msg->pcb, msg->received); - return msg->err; -} - -static esp_err_t _tcp_recved(tcp_pcb* pcb, size_t len) -{ - if(!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - - msg.received = len; - tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_close_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = ERR_CONN; - tcp_api_arg_t* arg = reinterpret_cast(msg->pcb->callback_arg); - if(arg != nullptr) { - if(arg->closed) { - return msg->err; - } - arg->closed = 1; - } - msg->err = tcp_close(msg->pcb); - _tcp_clear_events(msg->pcb); - return msg->err; -} - -static esp_err_t _tcp_close(tcp_pcb* pcb) -{ - if(!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - - tcpip_api_call(_tcp_close_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_abort_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = ERR_CONN; - tcp_api_arg_t* arg = reinterpret_cast(msg->pcb->callback_arg); - if(arg != nullptr) { - if(arg->closed) { - return msg->err; - } - arg->closed = 1; - } - - tcp_abort(msg->pcb); - return msg->err; -} - -static esp_err_t _tcp_abort(tcp_pcb* pcb) -{ - if(!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - - tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_connect_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = tcp_connect(msg->pcb, msg->connect.addr, msg->connect.port, _tcp_connected); - return msg->err; -} - -static esp_err_t _tcp_connect(tcp_pcb* pcb, const ip_addr_t* addr, uint16_t port, tcp_connected_fn cb) -{ - if(!pcb) { - return ESP_FAIL; - } - - tcp_api_call_t msg; - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg != nullptr) { - debug_d("Connecting ..."); - arg->closed = 0; - arg->connect.callback = cb; - } - - msg.pcb = pcb; - - msg.connect.addr = addr; - msg.connect.port = port; - msg.connect.cb = _tcp_connected; - tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_bind_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port); - return msg->err; -} - -static esp_err_t _tcp_bind(tcp_pcb* pcb, const ip_addr_t* addr, uint16_t port) -{ - if(!pcb) { - return ESP_FAIL; - } - tcp_api_call_t msg; - msg.pcb = pcb; - tcp_api_arg_t* arg = reinterpret_cast(msg.pcb->callback_arg); - if(arg != nullptr) { - arg->closed = 0; - } - msg.bind.addr = addr; - msg.bind.port = port; - tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_listen_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = 0; - msg->pcb = tcp_listen_with_backlog(msg->pcb, msg->backlog); - return msg->err; -} - -static tcp_pcb* _tcp_listen_with_backlog(tcp_pcb* pcb, uint8_t backlog) -{ - if(!pcb) { - return NULL; - } - tcp_api_call_t msg; - msg.pcb = pcb; - tcp_api_arg_t* arg = reinterpret_cast(msg.pcb->callback_arg); - if(arg != nullptr) { - arg->closed = 0; - } - msg.backlog = backlog ? backlog : TCP_DEFAULT_LISTEN_BACKLOG; - tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call_data*)&msg); - return msg.pcb; -} - -// High Level Functions - -tcp_pcb* async_tcp_new() -{ - return tcp_new_ip_type(IPADDR_TYPE_V4); -} - -void async_tcp_arg(struct tcp_pcb* pcb, void* arg) -{ - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* apiArgument = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - // delete the current argument if this is an api argument - if(apiArgument != nullptr) { - delete apiArgument; - } - tcp_arg(pcb, arg); - return; - } - - TcpConnection* connectionArg = reinterpret_cast(arg); - if(connectionArg != nullptr) { - // we have the right argument, lets wrap it - if(apiArgument == nullptr) { - apiArgument = new tcp_api_arg_t(); - memset(apiArgument, 0, sizeof(tcp_api_arg_t)); - } - apiArgument->connection = connectionArg; - tcp_arg(pcb, apiArgument); - return; - } - - // This should not happen - tcp_arg(pcb, arg); -} - -err_t async_tcp_connect(tcp_pcb* pcb, const ip_addr_t* ipaddr, u16_t port, tcp_connected_fn connected) -{ - if(!_start_async_task()) { - return ERR_ABRT; - } - - debug_d("Connecting: 0x%08x", pcb); - return _tcp_connect(pcb, ipaddr, port, connected); -} - -tcp_pcb* async_tcp_listen_with_backlog(struct tcp_pcb* pcb, u8_t backlog) -{ - if(!_start_async_task()) { - return nullptr; - } - - return _tcp_listen_with_backlog(pcb, backlog); -} - -err_t async_tcp_bind(struct tcp_pcb* pcb, const ip_addr_t* ipaddr, u16_t port) -{ - if(!_start_async_task()) { - return ERR_ABRT; - } - - return _tcp_bind(pcb, ipaddr, port); -} - -void async_tcp_accept(struct tcp_pcb* pcb, tcp_accept_fn callback) -{ - if(callback == nullptr) { - tcp_accept(pcb, nullptr); - return; - } - - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - // This should not happen - return; - } - - arg->accept.callback = callback; - tcp_accept(pcb, _tcp_accept); -} - -err_t async_tcp_write(struct tcp_pcb* pcb, const void* dataptr, u16_t len, u8_t apiflags) -{ - return _tcp_write(pcb, (const char*)dataptr, len, apiflags); -} - -void async_tcp_recved(struct tcp_pcb* pcb, u16_t len) -{ - _tcp_recved(pcb, len); -} - -err_t async_tcp_output(struct tcp_pcb* pcb) -{ - return _tcp_output(pcb); -} - -void async_tcp_abort(struct tcp_pcb* pcb) -{ - _tcp_abort(pcb); -} - -err_t async_tcp_close(struct tcp_pcb* pcb) -{ - return _tcp_close(pcb); -} - -void async_tcp_recv(struct tcp_pcb* pcb, tcp_recv_fn callback) -{ - if(callback == nullptr) { - tcp_recv(pcb, nullptr); - return; - } - - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - // This should not happen - return; - } - - arg->receive.callback = callback; - tcp_recv(pcb, _tcp_recv); -} - -void async_tcp_sent(struct tcp_pcb* pcb, tcp_sent_fn callback) -{ - if(callback == nullptr) { - tcp_sent(pcb, nullptr); - return; - } - - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - // This should not happen - return; - } - - arg->sent.callback = callback; - tcp_sent(pcb, _tcp_sent); -} - -void async_tcp_poll(struct tcp_pcb* pcb, tcp_poll_fn callback, u8_t interval) -{ - if(callback == nullptr) { - tcp_poll(pcb, nullptr, interval); - return; - } - - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - // This should not happen - return; - } - - arg->poll.callback = callback; - tcp_poll(pcb, _tcp_poll, interval); -} - -void async_tcp_err(struct tcp_pcb* pcb, tcp_err_fn callback) -{ - if(callback == nullptr) { - tcp_err(pcb, nullptr); - return; - } - - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - // This should not happen - return; - } - - arg->error.callback = callback; - tcp_err(pcb, _tcp_error); -} - -err_t async_dns_gethostbyname(const char* hostname, ip_addr_t* addr, dns_found_callback found, void* callback_arg) -{ - DnsLookup* dnsLookup = reinterpret_cast(callback_arg); - if(dnsLookup == nullptr) { - // This should not happen - return ERR_ABRT; - } - - tcp_api_arg_t* arg = new tcp_api_arg_t(); - if(arg == nullptr) { - // This should not happen - return ERR_ABRT; - } - - if(!_start_async_task()) { - return ERR_ABRT; - } - - arg->dns.callback = found; - arg->dns.lookup = dnsLookup; - return dns_gethostbyname(hostname, addr, _tcp_dns_found, arg); -} diff --git a/Sming/Components/Network/Arch/Esp32/Platform/StationListImpl.h b/Sming/Components/Network/Arch/Esp32/Platform/StationListImpl.h new file mode 100644 index 0000000000..f2c8de23fd --- /dev/null +++ b/Sming/Components/Network/Arch/Esp32/Platform/StationListImpl.h @@ -0,0 +1,64 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * StationListImpl.h + * + ****/ + +#pragma once + +#include +#include + +class StationListImpl : public StationList +{ +public: + StationListImpl() + { + auto err = esp_wifi_ap_get_sta_list(&list); + if(err != ESP_OK) { + debug_w("Can't get list of connected stations"); + return; + } + for(unsigned i = 0; i < list.num; i++) { + add(new Info{list.sta[i]}); + } + } + +private: + class Info : public StationInfo + { + public: + Info() + { + } + + Info(wifi_sta_info_t& info) : info(&info) + { + } + + MacAddress mac() const override + { + return info ? MacAddress{info->mac} : MacAddress{}; + } + + int8_t rssi() const override + { + return info ? info->rssi : 0; + } + + // Note: ESP32 does not provide IP information + IpAddress ip() const override + { + return IpAddress{}; + } + + private: + wifi_sta_info_t* info{nullptr}; + }; + + wifi_sta_list_t list{}; +}; diff --git a/Sming/Components/Network/Arch/Esp8266/Platform/AccessPointImpl.cpp b/Sming/Components/Network/Arch/Esp8266/Platform/AccessPointImpl.cpp index 93f0a266c6..2a897c13c9 100644 --- a/Sming/Components/Network/Arch/Esp8266/Platform/AccessPointImpl.cpp +++ b/Sming/Components/Network/Arch/Esp8266/Platform/AccessPointImpl.cpp @@ -9,6 +9,7 @@ ****/ #include "AccessPointImpl.h" +#include "StationListImpl.h" #include #include @@ -182,6 +183,11 @@ String AccessPointImpl::getPassword() const return pwd; } +std::unique_ptr AccessPointImpl::getStations() const +{ + return std::unique_ptr(new StationListImpl); +} + void AccessPointImpl::onSystemReady() { if(runConfig != nullptr) { diff --git a/Sming/Components/Network/Arch/Esp8266/Platform/AccessPointImpl.h b/Sming/Components/Network/Arch/Esp8266/Platform/AccessPointImpl.h index 53dce75022..8ce64e886e 100644 --- a/Sming/Components/Network/Arch/Esp8266/Platform/AccessPointImpl.h +++ b/Sming/Components/Network/Arch/Esp8266/Platform/AccessPointImpl.h @@ -34,6 +34,7 @@ class AccessPointImpl : public AccessPointClass, protected ISystemReadyHandler IpAddress getNetworkBroadcast() const override; String getSSID() const override; String getPassword() const override; + std::unique_ptr getStations() const override; protected: void onSystemReady() override; diff --git a/Sming/Components/Network/Arch/Esp8266/Platform/StationListImpl.h b/Sming/Components/Network/Arch/Esp8266/Platform/StationListImpl.h new file mode 100644 index 0000000000..a674bd8b66 --- /dev/null +++ b/Sming/Components/Network/Arch/Esp8266/Platform/StationListImpl.h @@ -0,0 +1,62 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * StationListImpl.h + * + ****/ + +#pragma once + +#include +#include + +class StationListImpl : public StationList +{ +public: + StationListImpl() + { + info.reset(wifi_softap_get_station_info()); + auto cur = info.get(); + while(cur != nullptr) { + add(new Info{cur}); + } + } + + ~StationListImpl() + { + wifi_softap_free_station_info(); + } + +private: + class Info : public StationInfo + { + public: + Info(station_info* info) : info(info) + { + } + + MacAddress mac() const override + { + return info ? MacAddress{info->bssid} : MacAddress{}; + } + + // Note: ESP8266 does not provide RSSI information + int8_t rssi() const override + { + return 0; + } + + IpAddress ip() const override + { + return info ? IpAddress{info->ip} : IpAddress{}; + } + + private: + station_info* info; + }; + + std::unique_ptr info; +}; diff --git a/Sming/Components/Network/Arch/Host/Platform/AccessPointImpl.cpp b/Sming/Components/Network/Arch/Host/Platform/AccessPointImpl.cpp index 77e52c94a6..344c8f669c 100644 --- a/Sming/Components/Network/Arch/Host/Platform/AccessPointImpl.cpp +++ b/Sming/Components/Network/Arch/Host/Platform/AccessPointImpl.cpp @@ -9,6 +9,7 @@ ****/ #include "AccessPointImpl.h" +#include "StationListImpl.h" static AccessPointImpl accessPoint; AccessPointClass& WifiAccessPoint = accessPoint; @@ -73,3 +74,8 @@ String AccessPointImpl::getPassword() const { return nullptr; } + +std::unique_ptr AccessPointImpl::getStations() const +{ + return std::unique_ptr(new StationListImpl); +} diff --git a/Sming/Components/Network/Arch/Host/Platform/AccessPointImpl.h b/Sming/Components/Network/Arch/Host/Platform/AccessPointImpl.h index c45b324102..aecb2d73e4 100644 --- a/Sming/Components/Network/Arch/Host/Platform/AccessPointImpl.h +++ b/Sming/Components/Network/Arch/Host/Platform/AccessPointImpl.h @@ -28,4 +28,5 @@ class AccessPointImpl : public AccessPointClass IpAddress getNetworkBroadcast() const override; String getSSID() const override; String getPassword() const override; + std::unique_ptr getStations() const override; }; diff --git a/Sming/Components/Network/Arch/Host/Platform/StationListImpl.h b/Sming/Components/Network/Arch/Host/Platform/StationListImpl.h new file mode 100644 index 0000000000..3009ec12cd --- /dev/null +++ b/Sming/Components/Network/Arch/Host/Platform/StationListImpl.h @@ -0,0 +1,52 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * StationListImpl.h + * + ****/ + +#pragma once + +#include +#include + +class StationListImpl : public StationList +{ +public: + StationListImpl() + { + for(unsigned i = 0; i < 5; ++i) { + add(new Info(i)); + } + } + +private: + class Info : public StationInfo + { + public: + Info(int index) : index(index) + { + } + + MacAddress mac() const override + { + return (index < 0) ? MacAddress{} : MacAddress({5, 4, 3, 2, 1, uint8_t(index)}); + } + + int8_t rssi() const override + { + return (index < 0) ? 0 : TRange(-90, -20).random(); + } + + IpAddress ip() const override + { + return (index < 0) ? IpAddress{} : IpAddress(10, 0, 0, 100 + index); + } + + private: + int index; + }; +}; diff --git a/Sming/Components/Network/src/.cs b/Sming/Components/Network/src/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Components/Network/src/Data/Stream/XorOutputStream.h b/Sming/Components/Network/src/Data/Stream/XorOutputStream.h index dbf63e488d..cd4434b09d 100644 --- a/Sming/Components/Network/src/Data/Stream/XorOutputStream.h +++ b/Sming/Components/Network/src/Data/Stream/XorOutputStream.h @@ -14,21 +14,21 @@ #include -class XorOutputStream: public IDataSourceStream { +/** + * @brief Xors original stream content with the specified mask + */ +class XorOutputStream : public IDataSourceStream +{ public: /** - * @brief Xors original stream content with the specified mask + * @brief Constructor * @param stream pointer to the original stream. Will be deleted after use * @param mask * @param maskLenth */ - XorOutputStream(IDataSourceStream *stream, uint8_t *mask, size_t maskLength) : - stream(stream), mask(mask), maskLength(maskLength) { - } - - ~XorOutputStream() + XorOutputStream(IDataSourceStream* stream, uint8_t* mask, size_t maskLength) + : stream(stream), mask(mask), maskLength(maskLength) { - delete stream; } StreamType getStreamType() const override @@ -41,11 +41,11 @@ class XorOutputStream: public IDataSourceStream { return stream->available(); } - uint16_t readMemoryBlock(char *data, int bufSize) override + uint16_t readMemoryBlock(char* data, int bufSize) override { uint16_t max = stream->readMemoryBlock(data, bufSize); size_t pos = maskPos; - for(unsigned i=0; i stream; + uint8_t* mask; size_t maskLength; size_t maskPos = 0; }; diff --git a/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.cpp b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.cpp index 9fefe9c931..d161532d58 100644 --- a/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.cpp +++ b/Sming/Components/Network/src/Network/Http/Websocket/WebsocketConnection.cpp @@ -207,7 +207,6 @@ bool WebsocketConnection::send(IDataSourceStream* source, ws_frame_type_t type, return false; } - int available = source->available(); if(available < 1) { debug_e("Streams without known size are not supported"); @@ -220,14 +219,12 @@ bool WebsocketConnection::send(IDataSourceStream* source, ws_frame_type_t type, uint16_t lengthValue = available; // calculate message length .... - if (available <= 125) { + if(available <= 125) { lengthValue = available; - } - else if (available < 65536) { + } else if(available < 65536) { lengthValue = 126; packetLength += 2; - } - else { + } else { lengthValue = 127; packetLength += 8; } @@ -253,12 +250,11 @@ bool WebsocketConnection::send(IDataSourceStream* source, ws_frame_type_t type, // length if(lengthValue < 126) { packet[i++] |= lengthValue; - } else if (lengthValue == 126){ + } else if(lengthValue == 126) { packet[i++] |= 126; packet[i++] = (available >> 8) & 0xFF; packet[i++] = available & 0xFF; - } - else if(lengthValue == 127){ + } else if(lengthValue == 127) { packet[i++] |= 127; packet[i++] = 0; packet[i++] = 0; @@ -266,8 +262,8 @@ bool WebsocketConnection::send(IDataSourceStream* source, ws_frame_type_t type, packet[i++] = 0; packet[i++] = (available >> 24) & 0xFF; packet[i++] = (available >> 16) & 0xFF; - packet[i++] = (available >> 8) & 0xFF; - packet[i++] = (available) & 0xFF; + packet[i++] = (available >> 8) & 0xFF; + packet[i++] = (available)&0xFF; } if(useMask) { @@ -294,12 +290,10 @@ void WebsocketConnection::broadcast(const char* message, size_t length, ws_frame { char* copy = new char[length]; memcpy(copy, message, length); - std::shared_ptr data(copy, [](const char* ptr) { - delete[] ptr; - }); + std::shared_ptr data(copy, [](const char* ptr) { delete[] ptr; }); for(unsigned i = 0; i < websocketList.count(); i++) { - auto stream = new SharedMemoryStream(data, length); + auto stream = new SharedMemoryStream(data, length); websocketList[i]->send(stream, type); } } diff --git a/Sming/Components/Network/src/Network/MqttClient.h b/Sming/Components/Network/src/Network/MqttClient.h index 8030af0857..e648e0ea7e 100644 --- a/Sming/Components/Network/src/Network/MqttClient.h +++ b/Sming/Components/Network/src/Network/MqttClient.h @@ -345,7 +345,7 @@ class MqttClient : protected TcpClient */ #ifndef MQTT_NO_COMPAT - SMING_DEPRECATED MqttMessageDeliveredCallback onDelivery = nullptr; ///< @deprecated + SMING_DEPRECATED MqttMessageDeliveredCallback onDelivery = nullptr; ///< @deprecated SMING_DEPRECATED MqttStringSubscriptionCallback subscriptionCallback = nullptr; ///< @deprecated #endif }; diff --git a/Sming/Components/Network/src/Network/TcpClient.cpp b/Sming/Components/Network/src/Network/TcpClient.cpp index af36b93605..b0c1e7b45e 100644 --- a/Sming/Components/Network/src/Network/TcpClient.cpp +++ b/Sming/Components/Network/src/Network/TcpClient.cpp @@ -69,8 +69,7 @@ bool TcpClient::send(IDataSourceStream* source, bool forceCloseAfterSent) if(stream == nullptr) { stream = source; - } - else if(stream != source){ + } else if(stream != source) { if(stream->getStreamType() == eSST_Chain) { auto chainStream = static_cast(stream); if(!chainStream->attachStream(source)) { @@ -78,10 +77,9 @@ bool TcpClient::send(IDataSourceStream* source, bool forceCloseAfterSent) delete source; return false; } - } - else { + } else { debug_d("Creating stream chain ..."); - auto chainStream = new StreamChain(); + auto chainStream = new StreamChain(); if(chainStream == nullptr) { delete source; debug_w("Unable to create stream chain!"); diff --git a/Sming/Components/Network/src/Network/WebHelpers/base64.h b/Sming/Components/Network/src/Network/WebHelpers/base64.h index 012cddf636..0b6e36984c 100644 --- a/Sming/Components/Network/src/Network/WebHelpers/base64.h +++ b/Sming/Components/Network/src/Network/WebHelpers/base64.h @@ -1,2 +1,3 @@ #include -#pragma GCC warning "Please update to #include - see https://sming.readthedocs.io/en/4.3.0/upgrading/4.3-4.4.html" +#pragma GCC warning \ + "Please update to #include - see https://sming.readthedocs.io/en/4.3.0/upgrading/4.3-4.4.html" diff --git a/Sming/Components/Network/src/Network/WebHelpers/escape.h b/Sming/Components/Network/src/Network/WebHelpers/escape.h index 3661e0999b..ee4d7b5727 100644 --- a/Sming/Components/Network/src/Network/WebHelpers/escape.h +++ b/Sming/Components/Network/src/Network/WebHelpers/escape.h @@ -1,2 +1,3 @@ #include -#pragma GCC warning "Please update to #include - see https://sming.readthedocs.io/en/4.3.0/upgrading/4.3-4.4.html" +#pragma GCC warning \ + "Please update to #include - see https://sming.readthedocs.io/en/4.3.0/upgrading/4.3-4.4.html" diff --git a/Sming/Components/Network/src/Platform/AccessPoint.h b/Sming/Components/Network/src/Platform/AccessPoint.h index 9fb52210f8..929eff0f14 100644 --- a/Sming/Components/Network/src/Platform/AccessPoint.h +++ b/Sming/Components/Network/src/Platform/AccessPoint.h @@ -14,6 +14,8 @@ #include #include #include "BssInfo.h" +#include "StationList.h" +#include /** @defgroup wifi_ap WiFi Access Point * @ingroup wifi platform @@ -129,6 +131,12 @@ class AccessPointClass * @retval String WiFi access point password */ virtual String getPassword() const = 0; + + /** + * @brief Gets a list of stations connected to the access point + * @retval StationList + */ + virtual std::unique_ptr getStations() const = 0; }; /** @brief Global instance of WiFi access point object diff --git a/Sming/Components/Network/src/Platform/BssInfo.cpp b/Sming/Components/Network/src/Platform/BssInfo.cpp index 38e3d6a2ea..0bf330985b 100644 --- a/Sming/Components/Network/src/Platform/BssInfo.cpp +++ b/Sming/Components/Network/src/Platform/BssInfo.cpp @@ -1,9 +1,9 @@ #include "BssInfo.h" -String BssInfo::getAuthorizationMethodName() const +String toString(WifiAuthMode mode) { - switch(authorization) { + switch(mode) { case AUTH_OPEN: return F("OPEN"); case AUTH_WEP: @@ -15,6 +15,6 @@ String BssInfo::getAuthorizationMethodName() const case AUTH_WPA_WPA2_PSK: return F("WPA_WPA2_PSK"); default: - return F("UNKNOWN_") + authorization; + return F("UNKNOWN_") + unsigned(mode); } } diff --git a/Sming/Components/Network/src/Platform/BssInfo.h b/Sming/Components/Network/src/Platform/BssInfo.h index 973007614f..93e46f1540 100644 --- a/Sming/Components/Network/src/Platform/BssInfo.h +++ b/Sming/Components/Network/src/Platform/BssInfo.h @@ -28,6 +28,8 @@ enum WifiAuthMode { using WifiAuthMode = AUTH_MODE; #endif +String toString(WifiAuthMode mode); + class BssInfo { public: @@ -42,7 +44,10 @@ class BssInfo /** @brief Get BSS authorisation method name * @retval String */ - String getAuthorizationMethodName() const; + String getAuthorizationMethodName() const + { + return toString(authorization); + } /** @brief Get BSS hash ID * @retval uint32_t BSS hash ID diff --git a/Sming/Components/Network/src/Platform/StationList.h b/Sming/Components/Network/src/Platform/StationList.h new file mode 100644 index 0000000000..4ec5ea7729 --- /dev/null +++ b/Sming/Components/Network/src/Platform/StationList.h @@ -0,0 +1,44 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * StationList.h + * + ****/ + +#pragma once + +#include +#include +#include +#include + +class StationInfo : public LinkedObjectTemplate +{ +public: + virtual MacAddress mac() const = 0; + + /** + * @brief Current average rssi of connected station. + * Available only on some architectures + */ + virtual int8_t rssi() const = 0; + + /** + * @brief Assigned IP address. + * Available only on some architectures. + */ + virtual IpAddress ip() const = 0; +}; + +class StationList : public OwnedLinkedObjectListTemplate +{ +public: + virtual ~StationList() + { + } +}; + +/** @} */ diff --git a/Sming/Core/Data/Range.h b/Sming/Core/Data/Range.h new file mode 100644 index 0000000000..136deb05e5 --- /dev/null +++ b/Sming/Core/Data/Range.h @@ -0,0 +1,107 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Range.h + * + ****/ + +#include +#include + +/** + * @brief Manage a range of numbers between specified limits + */ +template struct TRange { + T min{}; + T max{}; + + class Iterator + { + public: + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using reference = T&; + using iterator_category = std::random_access_iterator_tag; + + Iterator(T value) : value(value) + { + } + + T operator*() const + { + return value; + } + + bool operator==(const Iterator& other) const + { + return value == other.value; + } + + bool operator!=(const Iterator& other) const + { + return !(*this == other); + } + + Iterator operator++(int) + { + Iterator ret(value); + ++*this; + return ret; + } + + Iterator& operator++() + { + ++value; + return *this; + } + + private: + T value; + }; + + constexpr TRange() + { + } + + constexpr TRange(T min, T max) : min(min), max(max) + { + } + + constexpr TRange(T count) : min(0), max(count - 1) + { + } + + bool contains(T value) + { + return (value >= min) && (value <= max); + } + + T random() const + { + auto value = os_random(); + return min + value % (max - min); + } + + Iterator begin() const + { + return Iterator{min}; + } + + Iterator end() const + { + return Iterator{T(max + 1)}; + } + + String toString() const + { + String s; + s += min; + s += ", "; + s += max; + return s; + } +}; diff --git a/Sming/Core/Data/Stream/SharedMemoryStream.h b/Sming/Core/Data/Stream/SharedMemoryStream.h index 6a6ff0bf5e..6b44b55f35 100644 --- a/Sming/Core/Data/Stream/SharedMemoryStream.h +++ b/Sming/Core/Data/Stream/SharedMemoryStream.h @@ -15,18 +15,20 @@ /** * @brief Memory stream operating on fixed shared buffer. + * + * One reason for templating this class is for distinction between `char` or `const char` types, + * to avoid dangerous casts. Elements may be structures or other types. * * @ingroup stream */ - -class SharedMemoryStream : public IDataSourceStream +template class SharedMemoryStream : public IDataSourceStream { public: /** @brief Constructor for use with pre-existing buffer * @param buffer - * @param capacity Size of buffer + * @param capacity Size of buffer in elements */ - SharedMemoryStream(std::shared_ptr(buffer), size_t size) : buffer(buffer), capacity(size) + SharedMemoryStream(std::shared_ptr(buffer), size_t size) : buffer(buffer), capacity(size * sizeof(T)) { } @@ -35,14 +37,6 @@ class SharedMemoryStream : public IDataSourceStream return eSST_Memory; } - /** @brief Get a pointer to the current position - * @retval "const char*" Pointer to current cursor position within the data stream - */ - char* getStreamPointer() const - { - return (char*)(buffer.get() + readPos); - } - int available() override { return capacity - readPos; @@ -55,8 +49,9 @@ class SharedMemoryStream : public IDataSourceStream uint16_t readMemoryBlock(char* data, int bufSize) override { - int written = std::min(bufSize, available()); - memcpy(data, buffer.get() + readPos, written); + size_t written = std::min(bufSize, available()); + auto bufptr = reinterpret_cast(buffer.get()) + readPos; + memcpy(data, bufptr, written); return written; } @@ -77,7 +72,7 @@ class SharedMemoryStream : public IDataSourceStream } private: - std::shared_ptr buffer; + std::shared_ptr buffer; size_t capacity; size_t readPos{0}; }; diff --git a/tests/HostTests/modules/Hosted.cpp b/tests/HostTests/Arch/Host/Hosted.cpp similarity index 100% rename from tests/HostTests/modules/Hosted.cpp rename to tests/HostTests/Arch/Host/Hosted.cpp diff --git a/tests/HostTests/component.mk b/tests/HostTests/component.mk index be5f787a73..0518851e86 100644 --- a/tests/HostTests/component.mk +++ b/tests/HostTests/component.mk @@ -2,13 +2,19 @@ HWCONFIG = host-tests DEBUG_VERBOSE_LEVEL = 2 COMPONENT_INCDIRS := include -COMPONENT_SRCDIRS := app modules +COMPONENT_SRCDIRS := \ + app \ + modules \ + Arch/$(SMING_ARCH) ARDUINO_LIBRARIES := \ SmingTest \ ArduinoJson5 \ - ArduinoJson6 \ - Hosted + ArduinoJson6 + +ifeq ($(SMING_ARCH),Host) + ARDUINO_LIBRARIES += Hosted +endif COMPONENT_DEPENDS := \ malloc_count \ diff --git a/tests/HostTests/include/modules.h b/tests/HostTests/include/modules.h index 49d3db46b6..c1d8ec57d8 100644 --- a/tests/HostTests/include/modules.h +++ b/tests/HostTests/include/modules.h @@ -1,5 +1,12 @@ // List of test modules to register +// Architecture-specific test modules +#ifdef ARCH_HOST +#define ARCH_TEST_MAP(XX) XX(Hosted) +#else +#define ARCH_TEST_MAP(XX) +#endif + #define TEST_MAP(XX) \ XX(Libc) \ XX(PreCache) \ @@ -28,4 +35,4 @@ XX(Timers) \ XX(HttpRequest) \ XX(TcpClient) \ - XX(Hosted) + ARCH_TEST_MAP(XX) diff --git a/tests/HostTests/modules/Stream.cpp b/tests/HostTests/modules/Stream.cpp index 3d967ebb9c..2a1194c641 100644 --- a/tests/HostTests/modules/Stream.cpp +++ b/tests/HostTests/modules/Stream.cpp @@ -122,7 +122,7 @@ class StreamTest : public TestGroup // For testing, hack the boundary value so we can compare it against a reference output auto boundary = const_cast(multi.getBoundary()); - memcpy(boundary, _F("oALsXuO7vSbrvve"), 16); + memcpy(boundary, "oALsXuO7vSbrvve", 16); MemoryDataStream mem; size_t copySize = mem.copyFrom(&multi); @@ -173,16 +173,14 @@ class StreamTest : public TestGroup TEST_CASE("SharedMemoryStream") { char* message = new char[18]; - memcpy(message, "Wonderful data...", 17); - message[17] = '\0'; - + memcpy(message, "Wonderful data...", 18); std::shared_ptr data(message, [&message](const char* p) { delete[] p; }); debug_d("RefCount: %d", data.use_count()); - Vector list; + Vector*> list; for(unsigned i = 0; i < 4; i++) { - list.addElement(new SharedMemoryStream(data, strlen(message))); + list.addElement(new SharedMemoryStream(data, strlen(message))); } for(unsigned i = 0; i < list.count(); i++) { From dbbf8df70b654872e6bdbffff53e62f98dcd9ad1 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 12 Jul 2021 09:08:59 +0100 Subject: [PATCH 039/130] ESP8266 `No-WiFi` library updates (#2347) * Add `os_get_random()` implementation for no_wifi library * Filesystem fixes for `esp_no_wifi` library Require `system_rtc_clock_cali_proc` to use filesystem as SPIFFS uses time functions Call to `Storage::initialize()` required in `user_init()` KNOWN ISSUE: Call to `spi_flash_get_id()` hangs in application code. * Use flash cache control register definitions from RTOS SDK * Expand SPI ext0-2 struct definitions (likely related to flash) * Fix unused static variables in esp-lwip mem_manager.h * Add Platform/Timers.h to SmingCore.h * Update README --- .../Esp8266/Components/esp-lwip/mem_manager.h | 4 +- .../esp8266/include/espinc/eagle_soc.h | 16 +++- .../esp8266/include/espinc/spi_struct.h | 34 +++++++- .../Esp8266/Components/esp8266/startup.cpp | 2 + .../Esp8266/Components/esp_no_wifi/README.rst | 4 + .../Esp8266/Components/esp_no_wifi/no.wifi.ld | 1 + .../Arch/Esp8266/Components/esp_no_wifi/pm.c | 63 +++++++++++++++ .../Arch/Esp8266/Components/esp_no_wifi/wpa.c | 18 +++++ .../Esp8266/Components/spi_flash/flashmem.c | 6 +- Sming/Components/rboot/Cache_Read_Enable.rst | 81 +++++++++++++++++++ Sming/Core/SmingCore.h | 1 + samples/Basic_Storage/app/application.cpp | 6 +- 12 files changed, 228 insertions(+), 8 deletions(-) create mode 100644 Sming/Arch/Esp8266/Components/esp_no_wifi/wpa.c diff --git a/Sming/Arch/Esp8266/Components/esp-lwip/mem_manager.h b/Sming/Arch/Esp8266/Components/esp-lwip/mem_manager.h index 667e7ac848..c9c4ba684a 100644 --- a/Sming/Arch/Esp8266/Components/esp-lwip/mem_manager.h +++ b/Sming/Arch/Esp8266/Components/esp-lwip/mem_manager.h @@ -55,7 +55,7 @@ //#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT ) //static unsigned char ucHeap[ configTOTAL_HEAP_SIZE ]; -static unsigned char *ucHeap; +// static unsigned char *ucHeap; typedef struct A_BLOCK_LINK { @@ -67,7 +67,7 @@ static const unsigned short heapSTRUCT_SIZE = ( sizeof( xBlockLink ) + portBYTE_ //static const size_t xTotalHeapSize = ( ( size_t ) configADJUSTED_HEAP_SIZE ) & ( ( size_t ) ~portBYTE_ALIGNMENT_MASK ); -static xBlockLink xStart, *pxEnd = NULL; +// static xBlockLink xStart, *pxEnd = NULL; //static size_t xFreeBytesRemaining = ( ( size_t ) configADJUSTED_HEAP_SIZE ) & ( ( size_t ) ~portBYTE_ALIGNMENT_MASK ); diff --git a/Sming/Arch/Esp8266/Components/esp8266/include/espinc/eagle_soc.h b/Sming/Arch/Esp8266/Components/esp8266/include/espinc/eagle_soc.h index f65e7e8731..844f2f21dd 100644 --- a/Sming/Arch/Esp8266/Components/esp8266/include/espinc/eagle_soc.h +++ b/Sming/Arch/Esp8266/Components/esp8266/include/espinc/eagle_soc.h @@ -187,8 +187,20 @@ //}} //CACHE{{ -#define CACHE_FLASH_CTRL_REG (0x3ff00000 + 0x0c) -#define CACHE_READ_EN_BIT BIT8 +#define CACHE_FLASH_CTRL_REG (PERIPHS_DPORT_BASEADDR + 0x0c) +#define CACHE_FLUSH_START_BIT BIT0 // Clear then set to initiate cache flushing operation +#define CACHE_EMPTY_FLAG_BIT BIT1 // Set when cache has been cleared +#define CACHE_READ_EN_BIT BIT8 // Enable caching mechanism +#define CACHE_MAP_SEGMENT_S 16 // Which starting segment on 2M boundary (0-3) +#define CACHE_MAP_SEGMENT_MASK 0x3 +#define CACHE_MAP_2M BIT24 // Set to map 2M block, otherwise 1M +#define CACHE_MAP_1M_HIGH BIT25 // If CACHE_MAP_2M is clear determines which 1M block is mapped +#define CACHE_1M_SIZE 0x00100000 +#define CACHE_2M_SIZE 0x00200000 + +#define CACHE_IRAM_CTRL_REG (PERIPHS_DPORT_BASEADDR + 0x24) +#define CACHE_IRAM_EN_3 BIT3 // Set to enable IRAM bank #3 at 0x40108000 (16K) +#define CACHE_IRAM_EN_4 BIT4 // Set to enable IRAM bank #4 at 0x4010C000 (16K) //}} #define DRAM_BASE (0x3FFE8000) diff --git a/Sming/Arch/Esp8266/Components/esp8266/include/espinc/spi_struct.h b/Sming/Arch/Esp8266/Components/esp8266/include/espinc/spi_struct.h index a3b2d2d64c..0fc0bc417d 100644 --- a/Sming/Arch/Esp8266/Components/esp8266/include/espinc/spi_struct.h +++ b/Sming/Arch/Esp8266/Components/esp8266/include/espinc/spi_struct.h @@ -231,8 +231,38 @@ typedef struct { uint32_t val; } slave3; uint32_t data_buf[16]; /*data buffer*/ - uint32_t reserved_80[30]; - uint32_t ext2; + uint32_t reserved_80[28]; + union { + // See spi_register.h, related to flash accesses ? + struct { + uint32_t t_pp_time: 12; + uint32_t reserved13: 4; + uint32_t t_pp_shift: 4; + uint32_t reserved20: 11; + uint32_t t_pp_ena: 1; + }; + uint32_t val; + } ext0; + union { + // See spi_register.h, related to flash accesses ? + struct { + uint32_t t_erase_time: 12; + uint32_t reserved13: 4; + uint32_t t_erase_shift: 4; + uint32_t reserved20: 11; + uint32_t t_erase_ena: 1; + }; + uint32_t val; + } ext1; + union { + // See spi_register.h, related to flash accesses ? + // See also RTOS SDK `Cache_Read_Disable_2` function + struct { + uint32_t st: 3; + uint32_t reserved3: 29; + }; + uint32_t val; + } ext2; union { struct { uint32_t int_hold_ena: 2; /*This register is for two SPI masters to share the same cs clock and data signals. The bits of one SPI are set if the other SPI is busy the SPI will be hold. 1(3): hold at ,idle, phase 2: hold at ,prepare, phase.*/ diff --git a/Sming/Arch/Esp8266/Components/esp8266/startup.cpp b/Sming/Arch/Esp8266/Components/esp8266/startup.cpp index ff62f084d2..f171bb20a9 100644 --- a/Sming/Arch/Esp8266/Components/esp8266/startup.cpp +++ b/Sming/Arch/Esp8266/Components/esp8266/startup.cpp @@ -39,6 +39,8 @@ extern "C" void user_init(void) gdb_init(); + Storage::initialize(); + init(); // User code init } diff --git a/Sming/Arch/Esp8266/Components/esp_no_wifi/README.rst b/Sming/Arch/Esp8266/Components/esp_no_wifi/README.rst index a81ca6fc75..a63473d36c 100644 --- a/Sming/Arch/Esp8266/Components/esp_no_wifi/README.rst +++ b/Sming/Arch/Esp8266/Components/esp_no_wifi/README.rst @@ -74,6 +74,10 @@ If you want to disassemble other SDK libraries, do this:: Known issues ------------ +- Call to `spi_flash_get_id()` hangs in application code +- ROMs must be located below 1M + SDK implementation of `Cache_Read_Enable_New()` has internal flag which doesn't get initialised (default is 0xff, must be 1 or the function does nothing). + Further work is required to implement the following (list incomplete): - Sleep/power saving modes diff --git a/Sming/Arch/Esp8266/Components/esp_no_wifi/no.wifi.ld b/Sming/Arch/Esp8266/Components/esp_no_wifi/no.wifi.ld index f9721fb20a..c68a5b9e7f 100644 --- a/Sming/Arch/Esp8266/Components/esp_no_wifi/no.wifi.ld +++ b/Sming/Arch/Esp8266/Components/esp_no_wifi/no.wifi.ld @@ -5,3 +5,4 @@ PROVIDE ( system_get_free_heap_size = xPortGetFreeHeapSize ); PROVIDE ( system_restart = system_restart_local ); PROVIDE ( os_random = phy_get_rand ); PROVIDE ( system_get_cpu_freq = ets_get_cpu_frequency ); +PROVIDE ( system_rtc_clock_cali_proc = pm_rtc_clock_cali_proc ); diff --git a/Sming/Arch/Esp8266/Components/esp_no_wifi/pm.c b/Sming/Arch/Esp8266/Components/esp_no_wifi/pm.c index 1a344427d4..82384ac5f7 100644 --- a/Sming/Arch/Esp8266/Components/esp_no_wifi/pm.c +++ b/Sming/Arch/Esp8266/Components/esp_no_wifi/pm.c @@ -1,3 +1,66 @@ +#include +#include +#include + +void ets_delay_us(uint32_t); + +// Crystal frequency: 0=40, 1=26, 2=24 +extern uint8_t chip6_phy_init_ctrl; + void uart_tx_flush(void) { } + +/* + * Returns a calibration factor giving the ratio of system clock ticks to RTC time. + * NB. The calibration code is from `pm_rtc_clock_cali` in the `phy_sleep` module. + * To keep things simple we're just rolling it up into one function here. + */ +uint32_t pm_rtc_clock_cali_proc(void) +{ + static uint32_t calibration_value; + + rom_i2c_writeReg(106, 2, 8, 0); + + uint32_t value; + do { + value = GPIO_REG_READ(GPIO_RTC_CALIB_VALUE_ADDRESS); + } while((value & RTC_CALIB_RDY) == 0); + + const uint32_t rtcCalibValue = 0x0101; + GPIO_REG_WRITE(GPIO_RTC_CALIB_SYNC_ADDRESS, rtcCalibValue); + GPIO_REG_WRITE(GPIO_RTC_CALIB_SYNC_ADDRESS, rtcCalibValue | RTC_CALIB_START); + ets_delay_us(10); + + do { + value = GPIO_REG_READ(GPIO_RTC_CALIB_VALUE_ADDRESS); + } while((value & RTC_CALIB_RDY) == 0); + value &= RTC_CALIB_VALUE; + + uint32_t xtal_freq; + switch(chip6_phy_init_ctrl) { + case 0: + case 1: + xtal_freq = 26; + break; + case 2: + xtal_freq = 24; + break; + default: + xtal_freq = 40; + break; + } + + value = value * 16 / xtal_freq; + if(value < 512) { + return value; + } + + if(calibration_value == 0) { + calibration_value = value; + return value; + } + + calibration_value = ((calibration_value * 3) + (value * 5)) / 8; + return calibration_value; +} diff --git a/Sming/Arch/Esp8266/Components/esp_no_wifi/wpa.c b/Sming/Arch/Esp8266/Components/esp_no_wifi/wpa.c new file mode 100644 index 0000000000..6ce38aefc1 --- /dev/null +++ b/Sming/Arch/Esp8266/Components/esp_no_wifi/wpa.c @@ -0,0 +1,18 @@ +#include +#include + +uint32_t os_random(); + +int os_get_random(unsigned char* buf, size_t len) +{ + uint32_t rnd = 0; + for(size_t i = 0; i < len; ++i) { + if(rnd == 0) { + rnd = os_random(); + } + *buf++ = rnd & 0xff; + rnd >>= 8; + } + + return len; +} \ No newline at end of file diff --git a/Sming/Arch/Esp8266/Components/spi_flash/flashmem.c b/Sming/Arch/Esp8266/Components/spi_flash/flashmem.c index cf66c77799..8d06db8a8d 100644 --- a/Sming/Arch/Esp8266/Components/spi_flash/flashmem.c +++ b/Sming/Arch/Esp8266/Components/spi_flash/flashmem.c @@ -44,7 +44,11 @@ uint32_t flashmem_get_address(const void* memptr) uint32_t addr = (uint32_t)memptr - INTERNAL_FLASH_START_ADDRESS; // Determine which 1MB memory bank is mapped uint32_t ctrl = READ_PERI_REG(CACHE_FLASH_CTRL_REG); - uint8_t bank = (((ctrl >> 16) & 0x07) << 1) | ((ctrl >>25) & 0x01); + uint8_t segment = (ctrl >> CACHE_MAP_SEGMENT_S) & CACHE_MAP_SEGMENT_MASK; + uint8_t bank = segment << 1; + if(ctrl & CACHE_MAP_1M_HIGH) { + bank |= 0x01; + } addr += 0x100000U * bank; return addr; } diff --git a/Sming/Components/rboot/Cache_Read_Enable.rst b/Sming/Components/rboot/Cache_Read_Enable.rst index 89c0cdea33..00c0b631e8 100644 --- a/Sming/Components/rboot/Cache_Read_Enable.rst +++ b/Sming/Components/rboot/Cache_Read_Enable.rst @@ -62,3 +62,84 @@ Where SOC_CACHE_SIZE is defined as:: #endif +06/04/2021 @author mikee47 UPDATE +--------------------------------- + +RTOS SDK code has changed, now see usage in ``esp_fast_boot.c``. +Call looks like this:: + + Cache_Read_Enable(sub_region, region, SOC_CACHE_SIZE); + +See ``esp_fast_boot_restart()``. Code (rearranged) looks like this:: + + extern void pm_goto_rf_on(void); + extern void clockgate_watchdog(int on); + + int esp_fast_boot_restart(void) + { + const esp_partition_t* to_boot = esp_ota_get_boot_partition(); + if (!to_boot) { + ESP_LOGI(TAG, "no OTA boot partition"); + to_boot = esp_ota_get_running_partition(); + if (!to_boot) { + ESP_LOGE(TAG, "ERROR: Fail to get running partition"); + return -EINVAL; + } + } + + uint32_t image_start = to_boot->address; + uint32_t image_size = to_boot->size - 4; + + esp_image_header_t image; + int ret = spi_flash_read(image_start, &image, sizeof(esp_image_header_t)); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "ERROR: Fail to read image head from spi flash error=%d", ret); + return -EIO; + } + + uint32_t image_entry = image.entry_addr; + uint8_t region; + if (image_start < 0x200000) { + region = 0; + } else if (image_start < 0x400000) { + region = 1; + } else if (image_start < 0x600000) { + region = 2; + } else if (image_start < 0x800000) { + region = 3; + } else { + ESP_LOGE(TAG, "ERROR: App bin error, start_addr 0x%08x image_len %d\n", image_start, image_size); + return -EINVAL; + } + + uint8_t sub_region; + uint32_t image_mask = image_start & 0x1fffff; + if (image_mask < 0x100000) { + sub_region = 0; + } else { + sub_region = 1; + } + + pm_goto_rf_on(); + clockgate_watchdog(0); + REG_WRITE(0x3ff00018, 0xffff00ff); + SET_PERI_REG_MASK(0x60000D48, BIT1); + CLEAR_PERI_REG_MASK(0x60000D48, BIT1); + + REG_WRITE(INT_ENA_WDEV, 0); + _xt_isr_mask(UINT32_MAX); + + const uint32_t sp = DRAM_BASE + DRAM_SIZE - 16; + + Cache_Read_Disable(); + Cache_Read_Enable(sub_region, region, SOC_CACHE_SIZE); + + __asm__ __volatile__( + "mov a1, %0\n" + : : "a"(sp) : "memory" + ); + + void (*user_start)(size_t start_addr); + user_start = (void *)entry_addr; + user_start(image_start); + } diff --git a/Sming/Core/SmingCore.h b/Sming/Core/SmingCore.h index 928e339938..d5bf98265a 100644 --- a/Sming/Core/SmingCore.h +++ b/Sming/Core/SmingCore.h @@ -30,6 +30,7 @@ #include "Platform/RTC.h" #include "Platform/System.h" +#include "Platform/Timers.h" #include "Platform/WDT.h" #ifndef DISABLE_WIFI diff --git a/samples/Basic_Storage/app/application.cpp b/samples/Basic_Storage/app/application.cpp index 55d2bb63a2..405f8b0acc 100644 --- a/samples/Basic_Storage/app/application.cpp +++ b/samples/Basic_Storage/app/application.cpp @@ -16,8 +16,12 @@ void listDevices() Serial.print(toString(dev.getType())); Serial.print(_F(", size = 0x")); Serial.print(dev.getSize(), HEX); +#ifndef DISABLE_WIFI + // KNOWN ISSUE: Call to `spi_flash_get_id()` hangs in application code Serial.print(_F(", ID = 0x")); - Serial.println(dev.getId(), HEX); + Serial.print(dev.getId(), HEX); +#endif + Serial.println(); } Serial.println(); } From 2f0fa74f81b7e183a08990eadc337fda5e691f25 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 12 Jul 2021 09:29:42 +0100 Subject: [PATCH 040/130] Minor host updates (#2352) * Add `CThread::isCurrent()` method * Merge host gdbinit into gdbcmds * Fix malloc_count logging so pointer values are displayed consistently --- Sming/Arch/Host/Components/gdbstub/gdbcmds | 2 ++ Sming/Arch/Host/Components/gdbstub/gdbinit | 1 - Sming/Arch/Host/Components/hostlib/threads.h | 5 +++++ Sming/Components/malloc_count/malloc_count.cpp | 7 ++++--- 4 files changed, 11 insertions(+), 4 deletions(-) delete mode 100644 Sming/Arch/Host/Components/gdbstub/gdbinit diff --git a/Sming/Arch/Host/Components/gdbstub/gdbcmds b/Sming/Arch/Host/Components/gdbstub/gdbcmds index 362a5e18da..ce3dfcdbb9 100644 --- a/Sming/Arch/Host/Components/gdbstub/gdbcmds +++ b/Sming/Arch/Host/Components/gdbstub/gdbcmds @@ -1,3 +1,5 @@ +handle SIGUSR1 nostop noprint + # Enable this if you want to log all traffic between GDB and the stub #set remotelogfile gdb_rsp_logfile.txt diff --git a/Sming/Arch/Host/Components/gdbstub/gdbinit b/Sming/Arch/Host/Components/gdbstub/gdbinit deleted file mode 100644 index f74fcb3a81..0000000000 --- a/Sming/Arch/Host/Components/gdbstub/gdbinit +++ /dev/null @@ -1 +0,0 @@ -handle SIGUSR1 nostop noprint diff --git a/Sming/Arch/Host/Components/hostlib/threads.h b/Sming/Arch/Host/Components/hostlib/threads.h index 8bccfc14db..2bb57111ca 100644 --- a/Sming/Arch/Host/Components/hostlib/threads.h +++ b/Sming/Arch/Host/Components/hostlib/threads.h @@ -70,6 +70,11 @@ class CThread HOST_THREAD_DEBUG("Thread '%s' complete", name); } + bool isCurrent() const + { + return pthread_equal(pthread_self(), m_thread) != 0; + } + /* * Called at the start of any code which affects framework variables. * Will block if any another thread is running in interrupt context. diff --git a/Sming/Components/malloc_count/malloc_count.cpp b/Sming/Components/malloc_count/malloc_count.cpp index e3947e7ec3..315cca5f65 100644 --- a/Sming/Components/malloc_count/malloc_count.cpp +++ b/Sming/Components/malloc_count/malloc_count.cpp @@ -275,7 +275,7 @@ extern "C" void mc_free(void* ptr) dec_count(size); if(size >= logThreshold) { - log("free(%p) -> %u (cur %u)", ptr, size, stats.current); + log("free(%p) -> %u (cur %u)", (char*)ptr + alignment, size, stats.current); } } @@ -328,9 +328,10 @@ extern "C" void* mc_realloc(void* ptr, size_t size) if(size >= logThreshold) { if(newptr == ptr) { - log("realloc(%u -> %u) = %p (cur %u)", oldsize, size, newptr, stats.current); + log("realloc(%u -> %u) = %p (cur %u)", oldsize, size, (char*)newptr + alignment, stats.current); } else { - log("realloc(%u -> %u) = %p -> %p (cur %u)", oldsize, size, ptr, newptr, stats.current); + log("realloc(%u -> %u) = %p -> %p (cur %u)", oldsize, size, (char*)ptr + alignment, + (char*)newptr + alignment, stats.current); } } From 7f71c4745b60120b263c99d02e06acf0778444e3 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 12 Jul 2021 09:30:44 +0100 Subject: [PATCH 041/130] Add `remainingTicks()` and `remainingTime()` methods to PolledTimer. (#2351) --- Sming/Core/PolledTimer.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Sming/Core/PolledTimer.h b/Sming/Core/PolledTimer.h index e09ca98d8d..1200e0df9f 100644 --- a/Sming/Core/PolledTimer.h +++ b/Sming/Core/PolledTimer.h @@ -188,6 +188,23 @@ class Timer : public NanoTime::TimeSource return this->template ticksToTime(elapsedTicks()); } + /** + * @brief Get ticks remaining until expiry + */ + __forceinline TickType remainingTicks() const + { + auto ticks = elapsedTicks(); + return (ticks < interval) ? interval - ticks : 0; + } + + /** + * @brief Get time remaining until expiry + */ + __forceinline NanoTime::Time remainingTime() const + { + return this->template ticksToTime(remainingTicks()); + } + __forceinline bool canExpire() const { return !neverExpires; From d9e0214f0d9e3686f270662fd604b70f1da128ac Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 12 Jul 2021 09:32:14 +0100 Subject: [PATCH 042/130] Add `Stream::readBytes(uint8_t*, size_t)` overload (#2349) Consistent with arduino --- Sming/Core/Data/Stream/DataSourceStream.h | 2 ++ Sming/Core/Data/Stream/IFS/FileStream.h | 2 ++ Sming/Wiring/Stream.h | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/Sming/Core/Data/Stream/DataSourceStream.h b/Sming/Core/Data/Stream/DataSourceStream.h index d0f961cbe8..8c77cd559d 100644 --- a/Sming/Core/Data/Stream/DataSourceStream.h +++ b/Sming/Core/Data/Stream/DataSourceStream.h @@ -62,6 +62,8 @@ class IDataSourceStream : public Stream return getStreamType() != eSST_Invalid; } + using Stream::readBytes; + size_t readBytes(char* buffer, size_t length) override; /** @brief Read a block of memory diff --git a/Sming/Core/Data/Stream/IFS/FileStream.h b/Sming/Core/Data/Stream/IFS/FileStream.h index e6669a63cd..567c24e118 100644 --- a/Sming/Core/Data/Stream/IFS/FileStream.h +++ b/Sming/Core/Data/Stream/IFS/FileStream.h @@ -69,6 +69,8 @@ class FileStream : public FsBase, public ReadWriteStream return readBytes(&c, 1) ? static_cast(c) : -1; } + using ReadWriteStream::readBytes; + size_t readBytes(char* buffer, size_t length) override; uint16_t readMemoryBlock(char* data, int bufSize) override; diff --git a/Sming/Wiring/Stream.h b/Sming/Wiring/Stream.h index bcec65344f..92d763ed5b 100644 --- a/Sming/Wiring/Stream.h +++ b/Sming/Wiring/Stream.h @@ -105,6 +105,11 @@ class Stream : public Print */ virtual size_t readBytes(char* buffer, size_t length); + size_t readBytes(uint8_t* buffer, size_t length) + { + return readBytes(reinterpret_cast(buffer), length); + } + /** * @brief As `readBytes()` with terminator character * From d59d297f3de1d90621a56d944151ddf7eaa2364d Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 12 Jul 2021 09:33:36 +0100 Subject: [PATCH 043/130] FSTR::Array can break if used with some structure types. Make readValue() safe. (#2348) Unused symbols not discarded as expected: use unique section names for `IMPORT_FSTR` --- Sming/Components/FlashString | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sming/Components/FlashString b/Sming/Components/FlashString index e0e95dbe53..de5a90ea74 160000 --- a/Sming/Components/FlashString +++ b/Sming/Components/FlashString @@ -1 +1 @@ -Subproject commit e0e95dbe5399c15a4637eee9dbf46a7cb212ef36 +Subproject commit de5a90ea74a8214a4b3b2001bcf64be38d40071f From 9fe9cb5cf9e0b2afa16fcf8a56325eb3000f2420 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 12 Jul 2021 12:19:33 +0100 Subject: [PATCH 044/130] Fix `FILO`, add `empty()` method also to `FIFO` (#2350) --- Sming/Wiring/FIFO.h | 5 +++++ Sming/Wiring/FILO.h | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/Sming/Wiring/FIFO.h b/Sming/Wiring/FIFO.h index a86a35d9a9..ffd6df5728 100644 --- a/Sming/Wiring/FIFO.h +++ b/Sming/Wiring/FIFO.h @@ -39,6 +39,11 @@ template class FIFO : public Countable return numberOfElements; } + bool empty() const + { + return numberOfElements == 0; + } + bool full() const { return (count() >= rawSize); diff --git a/Sming/Wiring/FILO.h b/Sming/Wiring/FILO.h index 2e01aec495..15253633a8 100644 --- a/Sming/Wiring/FILO.h +++ b/Sming/Wiring/FILO.h @@ -32,12 +32,32 @@ template class FILO : public Countable T peek() const; // get the next element without releasing it from the FILO void flush(); // reset to default state + bool empty() const + { + return numberOfElements == 0; + } + + bool full() const + { + return numberOfElements >= rawSize; + } + // how many elements are currently in the FILO? unsigned int count() const override { return numberOfElements; } + const T& operator[](unsigned int index) const override + { + return raw[index]; /* unsafe */ + } + + T& operator[](unsigned int index) override + { + return raw[index]; /* unsafe */ + } + private: volatile int numberOfElements; int nextIn; From 0a448d1d2652089dca0927c1968f15dbd428cc1a Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 20 Jul 2021 08:00:45 +0100 Subject: [PATCH 045/130] No-WiFi flash fixes (#2353) Fixes the following issues: - Call to `spi_flash_get_id()` hangs in application code - ROMs must be located below 1M Both due to SDK implementation of `Cache_Read_Enable_New()` which requires a variable to be set to 0 at startup. Also, `system_os_post` must be in IRAM. * Update README --- Sming/Arch/Esp8266/Components/esp_no_wifi/README.rst | 4 ---- Sming/Arch/Esp8266/Components/esp_no_wifi/app_main.c | 12 ++++++++++++ .../Esp8266/Components/esp_no_wifi/user_interface.c | 2 +- samples/Basic_Storage/app/application.cpp | 3 --- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Sming/Arch/Esp8266/Components/esp_no_wifi/README.rst b/Sming/Arch/Esp8266/Components/esp_no_wifi/README.rst index a63473d36c..a81ca6fc75 100644 --- a/Sming/Arch/Esp8266/Components/esp_no_wifi/README.rst +++ b/Sming/Arch/Esp8266/Components/esp_no_wifi/README.rst @@ -74,10 +74,6 @@ If you want to disassemble other SDK libraries, do this:: Known issues ------------ -- Call to `spi_flash_get_id()` hangs in application code -- ROMs must be located below 1M - SDK implementation of `Cache_Read_Enable_New()` has internal flag which doesn't get initialised (default is 0xff, must be 1 or the function does nothing). - Further work is required to implement the following (list incomplete): - Sleep/power saving modes diff --git a/Sming/Arch/Esp8266/Components/esp_no_wifi/app_main.c b/Sming/Arch/Esp8266/Components/esp_no_wifi/app_main.c index 63d9bb1cb4..1ae71df6bf 100644 --- a/Sming/Arch/Esp8266/Components/esp_no_wifi/app_main.c +++ b/Sming/Arch/Esp8266/Components/esp_no_wifi/app_main.c @@ -36,12 +36,24 @@ void set_pll(void) } } +static void init_flash_code() +{ + spi_flash_set_read_func(NULL); + // Variable resets to 0xff, must be 0 or Cache_Read_Enable_New hangs + uint32_t addr = (uint32_t)&Cache_Read_Enable_New; + addr -= 8; + uint32_t** varptr = (uint32_t**)addr; + **varptr = 0; +} + static void user_start(void) { ets_isr_mask(0x3FE); // disable interrupts 1..9 sleep_reset_analog_rtcreg_8266(); // spoils the PLL! set_pll(); + init_flash_code(); + memset(&_bss_start, 0, &_bss_end - &_bss_start); ets_timer_init(); diff --git a/Sming/Arch/Esp8266/Components/esp_no_wifi/user_interface.c b/Sming/Arch/Esp8266/Components/esp_no_wifi/user_interface.c index 579bd01dc2..a1dd80bd21 100644 --- a/Sming/Arch/Esp8266/Components/esp_no_wifi/user_interface.c +++ b/Sming/Arch/Esp8266/Components/esp_no_wifi/user_interface.c @@ -34,7 +34,7 @@ void system_print_meminfo(void) #undef ADDR } -bool system_os_post(uint8_t prio, os_signal_t sig, os_param_t par) +bool IRAM_ATTR system_os_post(uint8_t prio, os_signal_t sig, os_param_t par) { if(prio >= USER_TASK_PRIO_MAX) { os_printf("err: post prio >= %u\n", USER_TASK_PRIO_MAX); diff --git a/samples/Basic_Storage/app/application.cpp b/samples/Basic_Storage/app/application.cpp index 405f8b0acc..37ed842b1a 100644 --- a/samples/Basic_Storage/app/application.cpp +++ b/samples/Basic_Storage/app/application.cpp @@ -16,11 +16,8 @@ void listDevices() Serial.print(toString(dev.getType())); Serial.print(_F(", size = 0x")); Serial.print(dev.getSize(), HEX); -#ifndef DISABLE_WIFI - // KNOWN ISSUE: Call to `spi_flash_get_id()` hangs in application code Serial.print(_F(", ID = 0x")); Serial.print(dev.getId(), HEX); -#endif Serial.println(); } Serial.println(); From 7e05f7846c568de10ee036999457a9cd807ff295 Mon Sep 17 00:00:00 2001 From: slaff Date: Tue, 27 Jul 2021 11:38:34 +0200 Subject: [PATCH 046/130] LinkedObjectList revisions (#2355) Add LinkedObject::insertAfter() Add LinkedObjectList::insert() Fix LinkedObjectList::count() Delete copy constructor for OwnedLinkedObjectList Add PriorityList. Co-authored-by: mikee47 --- Sming/Core/Data/LinkedObject.h | 15 +++++++ Sming/Core/Data/LinkedObjectList.h | 61 ++++++++++++++++++++++++++-- Sming/Core/Data/PriorityList.h | 64 ++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 Sming/Core/Data/PriorityList.h diff --git a/Sming/Core/Data/LinkedObject.h b/Sming/Core/Data/LinkedObject.h index 7b4cecbd86..a5e5ca64ed 100644 --- a/Sming/Core/Data/LinkedObject.h +++ b/Sming/Core/Data/LinkedObject.h @@ -35,6 +35,16 @@ class LinkedObject return mNext; } + bool insertAfter(LinkedObject* object) + { + if(object == nullptr) { + return false; + } + object->mNext = mNext; + mNext = object; + return true; + } + bool operator==(const LinkedObject& other) const { return this == &other; @@ -122,6 +132,11 @@ template class LinkedObjectTemplate : public LinkedObject return reinterpret_cast(this->next()); } + bool insertAfter(ObjectType* object) + { + return LinkedObject::insertAfter(object); + } + Iterator begin() const { return Iterator(this); diff --git a/Sming/Core/Data/LinkedObjectList.h b/Sming/Core/Data/LinkedObjectList.h index 1d5908f10f..4c31b02308 100644 --- a/Sming/Core/Data/LinkedObjectList.h +++ b/Sming/Core/Data/LinkedObjectList.h @@ -33,8 +33,35 @@ class LinkedObjectList return add(const_cast(object)); } + bool insert(LinkedObject* object) + { + if(object == nullptr) { + return false; + } + + object->mNext = mHead; + mHead = object; + return true; + } + + bool insert(const LinkedObject* object) + { + return insert(const_cast(object)); + } + bool remove(LinkedObject* object); + LinkedObject* pop() + { + if(mHead == nullptr) { + return nullptr; + } + auto obj = mHead; + mHead = mHead->mNext; + obj->mNext = nullptr; + return obj; + } + void clear() { mHead = nullptr; @@ -98,12 +125,34 @@ template class LinkedObjectListTemplate : public LinkedObj return nullptr; } + bool add(ObjectType* object) + { + return LinkedObjectList::add(object); + } + + bool add(const ObjectType* object) + { + return LinkedObjectList::add(object); + } + + bool insert(ObjectType* object) + { + return LinkedObjectList::insert(object); + } + + bool insert(const ObjectType* object) + { + return LinkedObjectList::insert(object); + } + + ObjectType* pop() + { + return reinterpret_cast(LinkedObjectList::pop()); + } + size_t count() const { - size_t n{0}; - for(auto p = mHead; p != nullptr; ++n, p = p->next()) { - } - return n; + return std::count_if(begin(), end(), [](const ObjectType&) { return true; }); } bool contains(const ObjectType& object) const @@ -119,6 +168,10 @@ template class LinkedObjectListTemplate : public LinkedObj template class OwnedLinkedObjectListTemplate : public LinkedObjectListTemplate { public: + OwnedLinkedObjectListTemplate() = default; + + OwnedLinkedObjectListTemplate(const OwnedLinkedObjectListTemplate& other) = delete; + ~OwnedLinkedObjectListTemplate() { clear(); diff --git a/Sming/Core/Data/PriorityList.h b/Sming/Core/Data/PriorityList.h new file mode 100644 index 0000000000..4ae8e9d471 --- /dev/null +++ b/Sming/Core/Data/PriorityList.h @@ -0,0 +1,64 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * + * @author: 2021 - Slavey Karadzhov + * + ****/ +#pragma once + +#include "LinkedObjectList.h" + +template class PriorityNode : public LinkedObjectTemplate> +{ +public: + PriorityNode(ObjectType data, int priority) : data(data), priority(priority) + { + } + + ObjectType data; + int priority; +}; + +template class PriorityList : public OwnedLinkedObjectListTemplate> +{ +public: + using List = OwnedLinkedObjectListTemplate>; + + /** + * @brief Adds and element and orders it according to its priority. Order is: High to low. + * @param object + * @param priority + * + * @retval bool true on success + */ + bool add(ObjectType object, int priority) + { + auto node = new PriorityNode{object, priority}; + if(node == nullptr) { + return false; + } + + if(List::isEmpty()) { + List::add(node); + return true; + } + + auto current = this->head(); + if(current->priority < node->priority) { + List::insert(node); + return true; + } + + while(current->next() != nullptr && current->getNext()->priority > node->priority) { + current = current->getNext(); + } + + node->insertAfter(current); + + return true; + } +}; From 41f339490d7b9470407768a3c40f67bd88a06ff9 Mon Sep 17 00:00:00 2001 From: slaff Date: Sun, 1 Aug 2021 15:26:24 +0200 Subject: [PATCH 047/130] Feature: HttpServer plugins (#2354) Co-authored-by: mikee47 --- .../src/Network/Http/HttpHeaderFields.h | 1 + .../Network/src/Network/Http/HttpResource.cpp | 87 ++++++++++ .../Network/src/Network/Http/HttpResource.h | 45 +++++- .../src/Network/Http/HttpResourceTree.cpp | 15 +- .../src/Network/Http/HttpResourceTree.h | 68 ++++++-- .../src/Network/Http/HttpServerConnection.cpp | 28 ++-- .../Http/Resource/Auth/ResourceBasicAuth.h | 66 ++++++++ .../Http/Resource/Auth/ResourceIpAuth.h | 40 +++++ .../src/Network/Http/Resource/HttpAuth.h | 15 ++ .../Http/Resource/HttpResourcePlugin.h | 81 ++++++++++ Sming/Core/Data/LinkedObject.h | 4 +- samples/HttpServer_Plugins/.cproject | 151 ++++++++++++++++++ samples/HttpServer_Plugins/.project | 28 ++++ samples/HttpServer_Plugins/Makefile | 9 ++ samples/HttpServer_Plugins/README.rst | 10 ++ .../HttpServer_Plugins/app/ContentDecoder.cpp | 49 ++++++ .../HttpServer_Plugins/app/application.cpp | 95 +++++++++++ samples/HttpServer_Plugins/component.mk | 1 + .../include/ContentDecoder.h | 22 +++ 19 files changed, 784 insertions(+), 31 deletions(-) create mode 100644 Sming/Components/Network/src/Network/Http/HttpResource.cpp create mode 100644 Sming/Components/Network/src/Network/Http/Resource/Auth/ResourceBasicAuth.h create mode 100644 Sming/Components/Network/src/Network/Http/Resource/Auth/ResourceIpAuth.h create mode 100644 Sming/Components/Network/src/Network/Http/Resource/HttpAuth.h create mode 100644 Sming/Components/Network/src/Network/Http/Resource/HttpResourcePlugin.h create mode 100644 samples/HttpServer_Plugins/.cproject create mode 100644 samples/HttpServer_Plugins/.project create mode 100644 samples/HttpServer_Plugins/Makefile create mode 100644 samples/HttpServer_Plugins/README.rst create mode 100644 samples/HttpServer_Plugins/app/ContentDecoder.cpp create mode 100644 samples/HttpServer_Plugins/app/application.cpp create mode 100644 samples/HttpServer_Plugins/component.mk create mode 100644 samples/HttpServer_Plugins/include/ContentDecoder.h diff --git a/Sming/Components/Network/src/Network/Http/HttpHeaderFields.h b/Sming/Components/Network/src/Network/Http/HttpHeaderFields.h index 2ec1c14431..618fcf6971 100644 --- a/Sming/Components/Network/src/Network/Http/HttpHeaderFields.h +++ b/Sming/Components/Network/src/Network/Http/HttpHeaderFields.h @@ -34,6 +34,7 @@ */ #define HTTP_HEADER_FIELDNAME_MAP(XX) \ XX(ACCEPT, "Accept", 0, "Limit acceptable response types") \ + XX(ACCEPT_ENCODING, "Accept-Encoding", 0, "Limit acceptable content encoding types") \ XX(ACCESS_CONTROL_ALLOW_ORIGIN, "Access-Control-Allow-Origin", 0, "") \ XX(AUTHORIZATION, "Authorization", 0, "Basic user agent authentication") \ XX(CC, "Cc", 0, "email field") \ diff --git a/Sming/Components/Network/src/Network/Http/HttpResource.cpp b/Sming/Components/Network/src/Network/Http/HttpResource.cpp new file mode 100644 index 0000000000..c8adbba2a0 --- /dev/null +++ b/Sming/Components/Network/src/Network/Http/HttpResource.cpp @@ -0,0 +1,87 @@ +#include "HttpResource.h" +#include "HttpServerConnection.h" + +namespace +{ +// Sequence to mark failed request, normally never occurs in headers +constexpr char SKIP_HEADER[]{2, 1, 0}; + +bool shouldSkip(const HttpRequest& request) +{ + return request.headers[SKIP_HEADER] == "1"; +} + +int requestFailed(HttpRequest& request, int err) +{ + debug_e("REQUEST FAILED"); + request.headers[SKIP_HEADER] = "1"; + return err; +} + +} // namespace + +#define FUNCTION_TEMPLATE(delegate, method, ...) \ + if(shouldSkip(request)) { \ + return 0; \ + } \ + \ + bool resourceHandled = !delegate; \ + for(auto& plugin : plugins) { \ + if(!resourceHandled && plugin->getPriority() == 0) { \ + int err = delegate(connection, request, ##__VA_ARGS__); \ + if(err != 0) { \ + return requestFailed(request, err); \ + } \ + resourceHandled = true; \ + } \ + if(!plugin->method(connection, request, ##__VA_ARGS__)) { \ + return requestFailed(request, 0); \ + } \ + } \ + return resourceHandled ? 0 : delegate(connection, request, ##__VA_ARGS__); + +int HttpResource::handleUrl(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response) +{ + FUNCTION_TEMPLATE(onUrlComplete, urlComplete, response) +} + +int HttpResource::handleHeaders(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response) +{ + FUNCTION_TEMPLATE(onHeadersComplete, headersComplete, response) +} + +int HttpResource::handleUpgrade(HttpServerConnection& connection, HttpRequest& request, char* data, size_t length) +{ + FUNCTION_TEMPLATE(onUpgrade, upgradeReceived, data, length) +} + +int HttpResource::handleBody(HttpServerConnection& connection, HttpRequest& request, char*& data, size_t& length) +{ + FUNCTION_TEMPLATE(onBody, bodyReceived, data, length) +} + +int HttpResource::handleRequest(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response) +{ + FUNCTION_TEMPLATE(onRequestComplete, requestComplete, response) +} + +void HttpResource::addPlugin(HttpResourcePlugin* plugin) +{ + if(plugin == nullptr) { + return; + } + + auto ref = new PluginRef{plugin}; + auto priority = plugin->getPriority(); + auto current = plugins.head(); + if(current == nullptr || (*current)->getPriority() < priority) { + plugins.insert(ref); + return; + } + + while(current->next() != nullptr && (*current->getNext())->getPriority() > priority) { + current = current->getNext(); + } + + ref->insertAfter(current); +} diff --git a/Sming/Components/Network/src/Network/Http/HttpResource.h b/Sming/Components/Network/src/Network/Http/HttpResource.h index 6e4089fb43..60824fb635 100644 --- a/Sming/Components/Network/src/Network/Http/HttpResource.h +++ b/Sming/Components/Network/src/Network/Http/HttpResource.h @@ -12,11 +12,10 @@ #pragma once -#include "WString.h" -#include "Data/ObjectMap.h" +#include +#include -#include "HttpResponse.h" -#include "HttpRequest.h" +#include "Resource/HttpResourcePlugin.h" class HttpServerConnection; @@ -46,8 +45,46 @@ class HttpResource } public: + class PluginRef : public LinkedObjectTemplate + { + public: + using OwnedList = OwnedLinkedObjectListTemplate; + + PluginRef(HttpResourcePlugin* plugin) : plugin(plugin) + { + } + + HttpResourcePlugin* operator->() const + { + return plugin; + } + + private: + HttpResourcePlugin* plugin; + }; + + HttpResourceDelegate onUrlComplete = nullptr; ///< URL is ready. Path and status code are available HttpServerConnectionBodyDelegate onBody = nullptr; ///< resource wants to process the raw body data HttpResourceDelegate onHeadersComplete = nullptr; ///< headers are ready HttpResourceDelegate onRequestComplete = nullptr; ///< request is complete OR upgraded HttpServerConnectionUpgradeDelegate onUpgrade = nullptr; ///< request is upgraded and raw data is passed to it + + void addPlugin(HttpResourcePlugin* plugin); + + template void addPlugin(HttpResourcePlugin* plugin, Tail... plugins) + { + addPlugin(plugin); + addPlugin(plugins...); + } + +private: + friend class HttpServerConnection; + + PluginRef::OwnedList plugins; + + int handleUrl(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response); + int handleHeaders(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response); + int handleUpgrade(HttpServerConnection& connection, HttpRequest& request, char* data, size_t length); + int handleBody(HttpServerConnection& connection, HttpRequest& request, char*& data, size_t& length); + int handleRequest(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response); }; diff --git a/Sming/Components/Network/src/Network/Http/HttpResourceTree.cpp b/Sming/Components/Network/src/Network/Http/HttpResourceTree.cpp index d512a0c3a1..18fbe81d2c 100644 --- a/Sming/Components/Network/src/Network/Http/HttpResourceTree.cpp +++ b/Sming/Components/Network/src/Network/Http/HttpResourceTree.cpp @@ -33,12 +33,23 @@ class HttpCompatResource : public HttpResource /* HttpResourceTree */ -void HttpResourceTree::set(String path, const HttpPathDelegate& callback) +HttpResource* HttpResourceTree::set(const String& path, const HttpResourceDelegate& onRequestComplete) +{ + auto resource = new HttpResource; + resource->onRequestComplete = onRequestComplete; + set(path, resource); + return resource; +} + +HttpResource* HttpResourceTree::set(String path, const HttpPathDelegate& callback) { if(path.length() > 1 && path.endsWith("/")) { path.remove(path.length() - 1); } + debug_i("'%s' registered", path.c_str()); - set(path, new HttpCompatResource(callback)); + auto res = new HttpCompatResource(callback); + set(path, res); + return res; } diff --git a/Sming/Components/Network/src/Network/Http/HttpResourceTree.h b/Sming/Components/Network/src/Network/Http/HttpResourceTree.h index 870ab45dd5..fb7aee3370 100644 --- a/Sming/Components/Network/src/Network/Http/HttpResourceTree.h +++ b/Sming/Components/Network/src/Network/Http/HttpResourceTree.h @@ -29,23 +29,24 @@ class HttpResourceTree : public ObjectMap /** @brief Set the default resource handler * @param resource The default resource handler */ - void setDefault(HttpResource* resource) + HttpResource* setDefault(HttpResource* resource) { set(RESOURCE_PATH_DEFAULT, resource); + return resource; } /** @brief Set the default resource handler, identified by "*" wildcard * @param onRequestComplete The default resource handler */ - void setDefault(const HttpResourceDelegate& onRequestComplete) + HttpResource* setDefault(const HttpResourceDelegate& onRequestComplete) { - set(RESOURCE_PATH_DEFAULT, onRequestComplete); + return set(RESOURCE_PATH_DEFAULT, onRequestComplete); } /** @brief Set the default resource handler, identified by "*" wildcard */ - void setDefault(const HttpPathDelegate& callback) + HttpResource* setDefault(const HttpPathDelegate& callback) { - set(RESOURCE_PATH_DEFAULT, callback); + return set(RESOURCE_PATH_DEFAULT, callback); } /** @brief Get the current default resource handler, if any @@ -62,14 +63,29 @@ class HttpResourceTree : public ObjectMap * @brief Set a callback to handle the given path * @param path URL path * @param onRequestComplete Delegate to handle this path + * @retval HttpResource* The created resource object * @note Path should start with slash. Trailing slashes will be removed. * @note Any existing handler for this path is replaced */ - void set(const String& path, const HttpResourceDelegate& onRequestComplete) + HttpResource* set(const String& path, const HttpResourceDelegate& onRequestComplete); + + /** + * @brief Set a callback to handle the given path, with one or more plugins + * @param path URL path + * @param onRequestComplete Delegate to handle this path + * @param plugin Plugins to register for the resource + * @retval HttpResource* The created resource object + * @note Path should start with slash. Trailing slashes will be removed. + * @note Any existing handler for this path is replaced + */ + template + HttpResource* set(const String& path, const HttpResourceDelegate& onRequestComplete, HttpResourcePlugin* plugin, + Tail... plugins) { - HttpResource* resource = new HttpResource; - resource->onRequestComplete = onRequestComplete; - set(path, resource); + registerPlugin(plugin, plugins...); + auto res = set(path, onRequestComplete); + res->addPlugin(plugin, plugins...); + return res; } /** @@ -79,5 +95,37 @@ class HttpResourceTree : public ObjectMap * @note Path should start with slash. Trailing slashes will be removed * @note Any existing handler for this path is replaced */ - void set(String path, const HttpPathDelegate& callback); + HttpResource* set(String path, const HttpPathDelegate& callback); + + /** + * @brief Add a new path resource with callback and one or more plugins + * @param path URL path + * @param callback The callback that will handle this path + * @param plugin - optional resource plugin + * @retval HttpResource* The created resource object + * @note Path should start with slash. Trailing slashes will be removed + * @note Any existing handler for this path is replaced + */ + template + HttpResource* set(const String& path, const HttpPathDelegate& callback, HttpResourcePlugin* plugin, Tail... plugins) + { + registerPlugin(plugin, plugins...); + auto res = set(path, callback); + res->addPlugin(plugin, plugins...); + return res; + } + +private: + void registerPlugin(HttpResourcePlugin* plugin) + { + loadedPlugins.add(plugin); + } + + template void registerPlugin(HttpResourcePlugin* plugin, Tail... plugins) + { + registerPlugin(plugin); + registerPlugin(plugins...); + } + + HttpResourcePlugin::OwnedList loadedPlugins; }; diff --git a/Sming/Components/Network/src/Network/Http/HttpServerConnection.cpp b/Sming/Components/Network/src/Network/Http/HttpServerConnection.cpp index c3161bfca6..c6156fd1a5 100644 --- a/Sming/Components/Network/src/Network/Http/HttpServerConnection.cpp +++ b/Sming/Components/Network/src/Network/Http/HttpServerConnection.cpp @@ -53,7 +53,7 @@ int HttpServerConnection::onPath(const Url& uri) resource = resourceTree->getDefault(); } - return 0; + return resource ? resource->handleUrl(*this, request, response) : 0; } int HttpServerConnection::onMessageComplete(http_parser* parser) @@ -69,8 +69,8 @@ int HttpServerConnection::onMessageComplete(http_parser* parser) response.code = HTTP_STATUS_BAD_REQUEST; } - if(resource != nullptr && resource->onRequestComplete) { - hasError = resource->onRequestComplete(*this, request, response); + if(resource != nullptr) { + hasError = resource->handleRequest(*this, request, response); } if(request.responseStream != nullptr) { @@ -104,8 +104,8 @@ int HttpServerConnection::onHeadersComplete(const HttpHeaders& headers) int error = 0; request.setHeaders(headers); - if(resource != nullptr && resource->onHeadersComplete) { - error = resource->onHeadersComplete(*this, request, response); + if(resource != nullptr) { + error = resource->handleHeaders(*this, request, response); if(error != 0) { return error; } @@ -161,22 +161,24 @@ int HttpServerConnection::onBody(const char* at, size_t length) return 0; } - if(bodyParser) { - const size_t consumed = bodyParser(request, at, length); - if(consumed != length) { + char* data = const_cast(at); + size_t dataLength = length; + if(resource != nullptr) { + int result = resource->handleBody(*this, request, data, dataLength); + if(result != 0) { hasContentError = true; if(closeOnContentError) { - return -1; + return result; } } } - if(resource != nullptr && resource->onBody) { - const int result = resource->onBody(*this, request, at, length); - if(result != 0) { + if(bodyParser) { + const size_t consumed = bodyParser(request, data, dataLength); + if(consumed != length) { hasContentError = true; if(closeOnContentError) { - return result; + return -1; } } } diff --git a/Sming/Components/Network/src/Network/Http/Resource/Auth/ResourceBasicAuth.h b/Sming/Components/Network/src/Network/Http/Resource/Auth/ResourceBasicAuth.h new file mode 100644 index 0000000000..a88c4b2643 --- /dev/null +++ b/Sming/Components/Network/src/Network/Http/Resource/Auth/ResourceBasicAuth.h @@ -0,0 +1,66 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * + * @author: 2021 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include "../HttpResourcePlugin.h" +#include + +class ResourceBasicAuth : public HttpPreFilter +{ +public: + ResourceBasicAuth(const String& realm, const String& username, const String& password) + : realm(realm), username(username), password(password) + { + } + + bool headersComplete(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response) override + { + auto& headers = request.headers; + auto authorization = headers[HTTP_HEADER_AUTHORIZATION]; + if(authorization) { + // check the authorization + authorization.trim(); + auto pos = authorization.indexOf(' '); + if(pos < 0) { + debug_w("Invalid authorization header"); + return true; + } + + auto type = authorization.substring(0, pos); + auto token = authorization.substring(pos + 1, authorization.length()); + if(!type.equalsIgnoreCase(F("Basic"))) { + return true; + } + + String text = base64_decode(token.c_str(), token.length()); + pos = text.indexOf(':'); + if(pos > 0) { + auto providedUsername = text.substring(0, pos); + auto providedPassword = text.substring(pos + 1, text.length()); + if(providedUsername == username && providedPassword == password) { + return true; + } + } + } + + // specify that the resource is protected... + response.code = HTTP_STATUS_UNAUTHORIZED; + response.headers[HTTP_HEADER_WWW_AUTHENTICATE] = F("Basic realm=\"") + realm + "\""; + + return false; + } + +private: + String realm; + String username; + String password; +}; diff --git a/Sming/Components/Network/src/Network/Http/Resource/Auth/ResourceIpAuth.h b/Sming/Components/Network/src/Network/Http/Resource/Auth/ResourceIpAuth.h new file mode 100644 index 0000000000..540e9ccd6a --- /dev/null +++ b/Sming/Components/Network/src/Network/Http/Resource/Auth/ResourceIpAuth.h @@ -0,0 +1,40 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * + * @author: 2021 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include "../HttpResourcePlugin.h" +#include + +class ResourceIpAuth : public HttpPreFilter +{ +public: + ResourceIpAuth(IpAddress ip, IpAddress netmask) : ip(ip), netmask(netmask) + { + } + + bool urlComplete(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response) override + { + auto remoteIp = connection.getRemoteIp(); + if(remoteIp.compare(ip, netmask)) { + // This IP is allowed to proceed + return true; + } + + // specify that the resource is protected... + response.code = HTTP_STATUS_UNAUTHORIZED; + return false; + } + +private: + IpAddress ip; + IpAddress netmask; +}; diff --git a/Sming/Components/Network/src/Network/Http/Resource/HttpAuth.h b/Sming/Components/Network/src/Network/Http/Resource/HttpAuth.h new file mode 100644 index 0000000000..0d0d7f035e --- /dev/null +++ b/Sming/Components/Network/src/Network/Http/Resource/HttpAuth.h @@ -0,0 +1,15 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * + * @author: 2021 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include "Auth/ResourceBasicAuth.h" +#include "Auth/ResourceIpAuth.h" diff --git a/Sming/Components/Network/src/Network/Http/Resource/HttpResourcePlugin.h b/Sming/Components/Network/src/Network/Http/Resource/HttpResourcePlugin.h new file mode 100644 index 0000000000..d7fbd73085 --- /dev/null +++ b/Sming/Components/Network/src/Network/Http/Resource/HttpResourcePlugin.h @@ -0,0 +1,81 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * + * @author: 2021 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include +#include "../HttpRequest.h" +#include "../HttpResponse.h" + +class HttpServerConnection; + +/** + * @brief Base plugin class. Implementations should be based on either `HttpPreFilter` or `HttpPostFilter` + */ +class HttpResourcePlugin : public LinkedObjectTemplate +{ +public: + using OwnedList = OwnedLinkedObjectListTemplate; + +protected: + friend class HttpResource; + + virtual int getPriority() const = 0; + + virtual bool urlComplete(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response) + { + return true; + } + + virtual bool headersComplete(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response) + { + return true; + } + + virtual bool upgradeReceived(HttpServerConnection& connection, HttpRequest&, char* data, size_t length) + { + return true; + } + + virtual bool bodyReceived(HttpServerConnection& connection, HttpRequest& request, char*& data, size_t& length) + { + return true; + } + + virtual bool requestComplete(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response) + { + return true; + } +}; + +/** + * @brief Filter plugins run *before* the resource is invoked + */ +class HttpPreFilter : public HttpResourcePlugin +{ +private: + int getPriority() const override + { + return 1; + } +}; + +/** + * @brief Filter plugins run *after* the resource is invoked + */ +class HttpPostFilter : public HttpResourcePlugin +{ +private: + int getPriority() const override + { + return -1; + } +}; diff --git a/Sming/Core/Data/LinkedObject.h b/Sming/Core/Data/LinkedObject.h index a5e5ca64ed..95bd20379e 100644 --- a/Sming/Core/Data/LinkedObject.h +++ b/Sming/Core/Data/LinkedObject.h @@ -40,8 +40,8 @@ class LinkedObject if(object == nullptr) { return false; } - object->mNext = mNext; - mNext = object; + mNext = object->mNext; + object->mNext = this; return true; } diff --git a/samples/HttpServer_Plugins/.cproject b/samples/HttpServer_Plugins/.cproject new file mode 100644 index 0000000000..e6469c9e2f --- /dev/null +++ b/samples/HttpServer_Plugins/.cproject @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + make + -f ${ProjDirPath}/Makefile + all + true + true + true + + + make + -f ${ProjDirPath}/Makefile + clean + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flash + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flashonefile + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flashinit + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flashboot + true + true + true + + + make + -f ${ProjDirPath}/Makefile + rebuild + true + true + true + + + + + + + + + + + + + + + + + + + + diff --git a/samples/HttpServer_Plugins/.project b/samples/HttpServer_Plugins/.project new file mode 100644 index 0000000000..67c872c77a --- /dev/null +++ b/samples/HttpServer_Plugins/.project @@ -0,0 +1,28 @@ + + + HttpServer_Plugins + + + SmingFramework + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/samples/HttpServer_Plugins/Makefile b/samples/HttpServer_Plugins/Makefile new file mode 100644 index 0000000000..ff51b6c3a7 --- /dev/null +++ b/samples/HttpServer_Plugins/Makefile @@ -0,0 +1,9 @@ +##################################################################### +#### Please don't change this file. Use component.mk instead #### +##################################################################### + +ifndef SMING_HOME +$(error SMING_HOME is not set: please configure it as an environment variable) +endif + +include $(SMING_HOME)/project.mk diff --git a/samples/HttpServer_Plugins/README.rst b/samples/HttpServer_Plugins/README.rst new file mode 100644 index 0000000000..e694a3db72 --- /dev/null +++ b/samples/HttpServer_Plugins/README.rst @@ -0,0 +1,10 @@ +HttpServer Plugins +===================== + +Simple example that demonstrate extending the HttpServer functionality with additional plugins. +Using such examples one can add: + + - URL Rewriting + - Authentication. Sming has already existing plugins for Basic Authentication, IP Limiting + - Content extracting/processing. For example a gzip decoder plugin can be implemented. + diff --git a/samples/HttpServer_Plugins/app/ContentDecoder.cpp b/samples/HttpServer_Plugins/app/ContentDecoder.cpp new file mode 100644 index 0000000000..2333b6444d --- /dev/null +++ b/samples/HttpServer_Plugins/app/ContentDecoder.cpp @@ -0,0 +1,49 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * + * @author: 2021 - Slavey Karadzhov + * + ****/ + +#include "ContentDecoder.h" + +// We register two events - one is executed as soon as the client request comes to the server +// and in it we add a header to the response that inform the http client that +// we support our own content encoding called "test" +DEFINE_FSTR_LOCAL(ENCODING_NAME, "test") + +bool ContentDecoder::headersComplete(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response) +{ + if(request.headers[HTTP_HEADER_CONTENT_ENCODING] == ENCODING_NAME) { + response.headers[HTTP_HEADER_CONTENT_ENCODING] = ENCODING_NAME; + } + + return true; +} + +bool ContentDecoder::urlComplete(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response) +{ + String content = response.headers[HTTP_HEADER_ACCEPT_ENCODING]; + if(content.length() > 0) { + content += ", "; + } + content += ENCODING_NAME; + response.headers[HTTP_HEADER_ACCEPT_ENCODING] = content; + + return true; +} + +bool ContentDecoder::bodyReceived(HttpServerConnection& connection, HttpRequest& request, char*& data, size_t& length) +{ + if(request.headers[HTTP_HEADER_CONTENT_ENCODING] == ENCODING_NAME) { + for(unsigned i = 0; i < length; i++) { + data[i]++; + } + } + + return true; +} diff --git a/samples/HttpServer_Plugins/app/application.cpp b/samples/HttpServer_Plugins/app/application.cpp new file mode 100644 index 0000000000..529933e8eb --- /dev/null +++ b/samples/HttpServer_Plugins/app/application.cpp @@ -0,0 +1,95 @@ +#include +#include +#include +#include "ContentDecoder.h" + +// If you want, you can define WiFi settings globally in Eclipse Environment Variables +#ifndef WIFI_SSID +#define WIFI_SSID "PleaseEnterSSID" // Put your SSID and password here +#define WIFI_PWD "PleaseEnterPass" +#endif + +namespace +{ +HttpServer* server; + +void echoContentBody(HttpRequest& request, HttpResponse& response) +{ + auto body = request.getBody(); + debug_d("Got content (after modifications): %s", body.c_str()); + + response.headers[HTTP_HEADER_CONTENT_TYPE] = request.headers[HTTP_HEADER_CONTENT_TYPE]; + response.sendString(body); +} + +void startWebServer() +{ + server = new HttpServer; + + server->listen(80); + server->paths.set("/", echoContentBody); + server->paths.setDefault(echoContentBody); + + /* + * By default the server does not store the incoming information. + * There has to be either a plugin that does this or a body parser. + * In this sample we use a body parser that stores the information into memory. + */ + server->setBodyParser(MIME_FORM_URL_ENCODED, bodyToStringParser); + + // Here we use a simple plugin that protects the access to a resource using HTTP Basic Authentication + auto pluginBasicAuth = new ResourceBasicAuth("realm", "username", "password"); + // You can add one or more authentication methods or other plugins... + server->paths.set("/auth", echoContentBody, pluginBasicAuth); + + /* + * The plugins will be registered in the order in which they are provided. + * For example in the command below the IP restriction plugin will be registered first + * followed by the basic authentication plugin. + * You can run the following curl command to test these plugins: + * + * curl -vvv http://username:password@192.168.13.10/ip-n-auth + * + * make sure to replace the IP address with the IP address of your HttpServer + */ + server->paths.set("/ip-n-auth", echoContentBody, + new ResourceIpAuth(IpAddress("192.168.13.0"), IpAddress("255.255.255.0")), pluginBasicAuth); + + /* + * This content coming to this resource is modified on the fly + * using our ContentDecoder plugin. See the source code of ContentDecoder + * to get an idea how to create your own plugin. + * You can run the following curl command to test this plugin: + * + * curl -vvv http://username@192.168.13.10/test -d "1234" -H "Content-Encoding: test" + * + * make sure to replace the IP address with the IP address of your HttpServer + */ + server->paths.set("/test", echoContentBody, new ContentDecoder()); + + Serial.println(F("\r\n=== WEB SERVER STARTED ===")); + Serial.println(WifiStation.getIP()); + Serial.println(F("==============================\r\n")); +} + +// Will be called when WiFi station becomes fully operational +void gotIP(IpAddress ip, IpAddress netmask, IpAddress gateway) +{ + startWebServer(); + debug_i("free heap = %u", system_get_free_heap_size()); +} + +} // namespace + +void init() +{ + Serial.begin(SERIAL_BAUD_RATE); // 115200 by default + Serial.systemDebugOutput(true); // Enable debug output to serial + + WifiStation.enable(true); + WifiStation.config(WIFI_SSID, WIFI_PWD); + WifiAccessPoint.enable(false); + + // Run our method when station was connected to AP + WifiEvents.onStationGotIP(gotIP); +} diff --git a/samples/HttpServer_Plugins/component.mk b/samples/HttpServer_Plugins/component.mk new file mode 100644 index 0000000000..6a0c019953 --- /dev/null +++ b/samples/HttpServer_Plugins/component.mk @@ -0,0 +1 @@ +HWCONFIG := standard diff --git a/samples/HttpServer_Plugins/include/ContentDecoder.h b/samples/HttpServer_Plugins/include/ContentDecoder.h new file mode 100644 index 0000000000..5d0f948ef6 --- /dev/null +++ b/samples/HttpServer_Plugins/include/ContentDecoder.h @@ -0,0 +1,22 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * + * @author: 2021 - Slavey Karadzhov + * + ****/ + +#pragma once + +#include + +class ContentDecoder : public HttpPreFilter +{ +public: + bool headersComplete(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response) override; + bool urlComplete(HttpServerConnection& connection, HttpRequest& request, HttpResponse& response) override; + bool bodyReceived(HttpServerConnection& connection, HttpRequest& request, char*& data, size_t& length) override; +}; From e0257cdca40b17ba033835cc0a7af6a200b16d65 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 2 Aug 2021 07:23:11 +0100 Subject: [PATCH 048/130] Fix PartitionStream::seek for SeekOrigin::Start (#2356) --- Sming/Components/Storage/src/PartitionStream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sming/Components/Storage/src/PartitionStream.cpp b/Sming/Components/Storage/src/PartitionStream.cpp index 86ad9b037c..d2cea3cec6 100644 --- a/Sming/Components/Storage/src/PartitionStream.cpp +++ b/Sming/Components/Storage/src/PartitionStream.cpp @@ -23,7 +23,7 @@ int PartitionStream::seekFrom(int offset, SeekOrigin origin) size_t newPos; switch(origin) { case SeekOrigin::Start: - newPos = 0; + newPos = offset; break; case SeekOrigin::Current: newPos = readPos + offset; From 7872602fde7077fd9405f646382f6d2ae978be05 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 12 Aug 2021 07:55:22 +0100 Subject: [PATCH 049/130] Asynchronous Graphics Library (#2357) An asynchronous graphics management library intended for SPI-based displays. On first use several required python modules must be installed. Try building a sample application: ``` cd $SMING_HOME make fetch Graphics cd Libraries/Graphics/samples/Basic_Graphics make python-requirements make ``` Currently only the ILI9341 display has a driver, but you can use the virtual screen for testing. This is a display device for use by the Host Emulator. The 'display' is a python application which communicates with the Sming :cpp:class:`Graphics::Display::Virtual` display driver using a TCP socket. Graphics processing is handled using SDL2. To start the virtual screen server type ``make virtual-screen`` from your project directory. The default TCP port is 7780. If you need to change this, use:: make virtual-screen VSPORT=7780 The screen server's IP address is shown in the caption bar. Build and run your project in host mode as follows:: make SMING_ARCH=Host make run VSADDR=192.1.2.3 --- .gitmodules | 4 ++++ Sming/Components/FlashString | 2 +- Sming/Components/Storage/Tools/hwconfig/common.py | 6 ++++-- Sming/Core/Data/Range.h | 5 +++++ Sming/Libraries/Graphics | 1 + Sming/Libraries/HardwareSPI | 2 +- Sming/Libraries/TFT_S1D13781 | 2 +- Tools/Python/README.rst | 7 +++++++ .../Storage/Tools/hwconfig => Tools/Python}/rjsmin.py | 0 Tools/install.sh | 7 ++++++- Tools/vscode/setup.py | 2 +- 11 files changed, 31 insertions(+), 7 deletions(-) create mode 160000 Sming/Libraries/Graphics create mode 100644 Tools/Python/README.rst rename {Sming/Components/Storage/Tools/hwconfig => Tools/Python}/rjsmin.py (100%) diff --git a/.gitmodules b/.gitmodules index 24fe182d73..f0fd7431a8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -199,6 +199,10 @@ path = Sming/Libraries/GoogleCast url = https://github.com/slaff/Sming-GoogleCast.git ignore = dirty +[submodule "Libraries.Graphics"] + path = Sming/Libraries/Graphics + url = https://github.com/mikee47/Sming-Graphics + ignore = dirty [submodule "Libraries.HardwareSPI"] path = Sming/Libraries/HardwareSPI url = https://github.com/mikee47/HardwareSPI diff --git a/Sming/Components/FlashString b/Sming/Components/FlashString index de5a90ea74..a443c0bc49 160000 --- a/Sming/Components/FlashString +++ b/Sming/Components/FlashString @@ -1 +1 @@ -Subproject commit de5a90ea74a8214a4b3b2001bcf64be38d40071f +Subproject commit a443c0bc49fbc74300d1082e1270400d7f6a9010 diff --git a/Sming/Components/Storage/Tools/hwconfig/common.py b/Sming/Components/Storage/Tools/hwconfig/common.py index 37ee625a89..8b644def8d 100644 --- a/Sming/Components/Storage/Tools/hwconfig/common.py +++ b/Sming/Components/Storage/Tools/hwconfig/common.py @@ -2,10 +2,12 @@ # Common functions and definitions # -import sys, json, platform -from rjsmin import jsmin +import os, sys, json, platform from collections import OrderedDict +sys.path.insert(1, os.path.expandvars('${SMING_HOME}/../Tools/Python')) +from rjsmin import jsmin + quiet = False def status(msg): diff --git a/Sming/Core/Data/Range.h b/Sming/Core/Data/Range.h index 136deb05e5..2a07c238b2 100644 --- a/Sming/Core/Data/Range.h +++ b/Sming/Core/Data/Range.h @@ -105,3 +105,8 @@ template struct TRange { return s; } }; + +template inline String toString(TRange range) +{ + return range.toString(); +} diff --git a/Sming/Libraries/Graphics b/Sming/Libraries/Graphics new file mode 160000 index 0000000000..2375f5fa3c --- /dev/null +++ b/Sming/Libraries/Graphics @@ -0,0 +1 @@ +Subproject commit 2375f5fa3c8cc92727968fa2c983a6d799220373 diff --git a/Sming/Libraries/HardwareSPI b/Sming/Libraries/HardwareSPI index 83d9b42dd2..7a5e89bf2d 160000 --- a/Sming/Libraries/HardwareSPI +++ b/Sming/Libraries/HardwareSPI @@ -1 +1 @@ -Subproject commit 83d9b42dd258ab41193aca47b7020c1ebd6458a9 +Subproject commit 7a5e89bf2dfcb189f7a4cc1ace86706aa5b0ead0 diff --git a/Sming/Libraries/TFT_S1D13781 b/Sming/Libraries/TFT_S1D13781 index 4940599366..673acb185c 160000 --- a/Sming/Libraries/TFT_S1D13781 +++ b/Sming/Libraries/TFT_S1D13781 @@ -1 +1 @@ -Subproject commit 49405993668fc4dfbd51109e836f3eaa7b0b838f +Subproject commit 673acb185c8e9a9d68e3d27367b39fa45e168dd3 diff --git a/Tools/Python/README.rst b/Tools/Python/README.rst new file mode 100644 index 0000000000..a8847b925a --- /dev/null +++ b/Tools/Python/README.rst @@ -0,0 +1,7 @@ +This directory is for commonly-used python modules. Example usage: + + import os, sys + + sys.path.insert(1, os.path.expandvars('${SMING_HOME}/../Tools/Python')) + + from rjsmin import jsmin diff --git a/Sming/Components/Storage/Tools/hwconfig/rjsmin.py b/Tools/Python/rjsmin.py similarity index 100% rename from Sming/Components/Storage/Tools/hwconfig/rjsmin.py rename to Tools/Python/rjsmin.py diff --git a/Tools/install.sh b/Tools/install.sh index 562035069a..e998967777 100755 --- a/Tools/install.sh +++ b/Tools/install.sh @@ -93,7 +93,12 @@ if [ -n "$APPVEYOR" ] || [ -n "$GITHUB_ACTION" ]; then $PKG_INSTALL \ clang-format-8 \ g++-9-multilib \ - python3-setuptools + python3-setuptools \ + fonts-ubuntu \ + fonts-noto-mono \ + xfonts-base \ + fonts-urw-base35 \ + fonts-droid-fallback sudo update-alternatives --set gcc /usr/bin/gcc-9 diff --git a/Tools/vscode/setup.py b/Tools/vscode/setup.py index 3576b36451..a183d6f18d 100644 --- a/Tools/vscode/setup.py +++ b/Tools/vscode/setup.py @@ -210,7 +210,7 @@ def main(): sys.exit(1) # So we can find rjsmin.py - sys.path.append(os.path.join(env['SMING_HOME'], 'Components/Storage/Tools/hwconfig')) + sys.path.append(os.path.join(env['SMING_HOME'], '../Tools/Python')) update_intellisense() update_tasks() From 858c3684ccad7f3b560ed73c78c409470004f6fa Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 26 Aug 2021 07:22:17 +0100 Subject: [PATCH 050/130] Add `SPIClass::setup` method for ESP32 (#2360) --- Sming/Arch/Esp32/Core/SPI.cpp | 18 ++++++++++++++++++ Sming/Arch/Esp32/Core/SPI.h | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/Sming/Arch/Esp32/Core/SPI.cpp b/Sming/Arch/Esp32/Core/SPI.cpp index a7e36530b2..79ea43fe18 100644 --- a/Sming/Arch/Esp32/Core/SPI.cpp +++ b/Sming/Arch/Esp32/Core/SPI.cpp @@ -235,6 +235,24 @@ void spi_set_clock(SpiDevice& dev, SPISpeed& speed) } // namespace +bool SPIClass::setup(SpiBus busId, SpiPins pins) +{ + if(busId < SpiBus::MIN || busId > SpiBus::MAX) { + debug_e("[SPI] Invalid bus"); + return false; + } + + auto& bus = busInfo[unsigned(busId) - 1]; + if(bus.assigned) { + debug_e("[SPI] Bus #%u already assigned", busId); + return false; + } + + this->busId = busId; + this->pins = pins; + return true; +} + SPIClass::BusInfo& SPIClass::getBusInfo() { return busInfo[unsigned(busId) - 1]; diff --git a/Sming/Arch/Esp32/Core/SPI.h b/Sming/Arch/Esp32/Core/SPI.h index 9f7622829f..57dae526e7 100644 --- a/Sming/Arch/Esp32/Core/SPI.h +++ b/Sming/Arch/Esp32/Core/SPI.h @@ -67,6 +67,14 @@ class SPIClass : public SPIBase { } + /** + * @brief Alternative to defining bus and pin set in constructor. + * Use this method to change global `SPI` instance setup. + * + * IMPORTANT: Must be called *before* begin(). + */ + bool setup(SpiBus id, SpiPins pins); + bool begin() override; void end() override; uint8_t read8() override; From 8a448c56bc55266b06d72c2160f1326a2852af53 Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 31 Aug 2021 07:10:17 +0100 Subject: [PATCH 051/130] Update ESP32 to build with IDF v4.3 (#2358) * Support building with ESP IDF release/v4.3 * Working to fix building for all supported variants (esp32, esp32c3, esp32s2 and esp32s3) * Update install script to IDF v4.3, apply patches * Update flashmem, UART to build for esp32s2 * Get builds working for RISCV arch. (esp32c3) * Use unique SDK project name for each ESP variant Avoids problems when changing ESP_VARIANT setting Allows each variant to have customised settings, which we're likely going to need * Use HAL calls for interrupts * Revise SPIClass to use more HAL definitions * Fix Timer for ESP32 * Set SDK compiler optimisation for size * Configure ADC width according to ADC_WIDTH_MAX * Patch IDF HAL files to build in C++ --- README.md | 2 +- .../driver/include/driver/hw_timer.h | 20 ++ .../driver/include/driver/os_timer.h | 2 +- .../Components/driver/include/driver/uart.h | 2 +- Sming/Arch/Esp32/Components/driver/uart.cpp | 48 ++++- .../Components/esp32/{project => }/.gitignore | 0 .../Arch/Esp32/Components/esp32/component.mk | 141 +++++++++---- .../Arch/Esp32/Components/esp32/ld/README.rst | 1 - .../Arch/Esp32/Components/esp32/ld/common.ld | 174 ---------------- .../Components/esp32/ld/standalone.rom.ld | 13 -- .../{project => project.esp32}/CMakeLists.txt | 2 + .../main/CMakeLists.txt | 0 .../{project => project.esp32}/main/main.c | 0 .../sdkconfig.defaults.debug | 7 +- .../sdkconfig.defaults.release | 7 +- .../esp32/project.esp32c3/CMakeLists.txt | 8 + .../esp32/project.esp32c3/main/CMakeLists.txt | 2 + .../esp32/project.esp32c3/main/main.c | 3 + .../project.esp32c3/sdkconfig.defaults.debug | 37 ++++ .../sdkconfig.defaults.release | 34 ++++ .../esp32/project.esp32s2/CMakeLists.txt | 8 + .../esp32/project.esp32s2/main/CMakeLists.txt | 2 + .../esp32/project.esp32s2/main/main.c | 3 + .../project.esp32s2/sdkconfig.defaults.debug | 37 ++++ .../sdkconfig.defaults.release | 34 ++++ .../esp32/project.esp32s3/CMakeLists.txt | 8 + .../esp32/project.esp32s3/main/CMakeLists.txt | 2 + .../esp32/project.esp32s3/main/main.c | 3 + .../project.esp32s3/sdkconfig.defaults.debug | 37 ++++ .../sdkconfig.defaults.release | 34 ++++ .../Esp32/Components/esp32/project/Makefile | 12 -- .../Components/esp32/src/include/esp_clk.h | 6 +- .../Components/esp32/src/include/esp_libc.h | 2 +- .../esp32/src/include/esp_systemapi.h | 9 +- .../Components/esp32/src/include/ets_sys.h | 2 +- Sming/Arch/Esp32/Components/esp32/src/sleep.c | 1 + .../Esp32/Components/esp32/src/startup.cpp | 2 +- .../Esp32/Components/esp32/src/system.cpp | 7 + .../Esp32/Components/libc/include/esp_attr.h | 2 +- .../Components/libc/include/sys/pgmspace.h | 54 ++--- .../Esp32/Components/spi_flash/flashmem.cpp | 12 +- .../spi_flash/include/esp_spi_flash.h | 2 +- Sming/Arch/Esp32/Core/Digital.cpp | 5 + Sming/Arch/Esp32/Core/Interrupts.cpp | 13 +- Sming/Arch/Esp32/Core/SPI.cpp | 185 ++++++++---------- Sming/Arch/Esp32/Core/SPI.h | 23 ++- Sming/Arch/Esp32/Core/adc.cpp | 9 +- Sming/Arch/Esp32/Platform/RTC.cpp | 5 +- Sming/Arch/Esp32/README.rst | 27 ++- Sming/Arch/Esp32/Tools/ci/build.run.sh | 12 ++ Sming/Arch/Esp32/Tools/ci/install.cmd | 9 +- Sming/Arch/Esp32/Tools/install.sh | 29 +-- Sming/Arch/Esp32/Tools/memanalyzer.py | 44 +++-- Sming/Arch/Esp32/app.mk | 9 +- Sming/Arch/Esp32/build.mk | 27 ++- Sming/Arch/Esp8266/Core/SPI.h | 7 + Sming/Arch/Esp8266/Tools/ci/build.run.cmd | 2 +- Sming/Arch/Host/Core/SPI.h | 7 + Sming/Components/IFS | 2 +- .../Arch/Esp32/Platform/AccessPointImpl.cpp | 10 +- .../Arch/Esp32/Platform/StationImpl.cpp | 12 +- .../Arch/Esp32/Platform/StationListImpl.h | 2 +- .../Storage/src/PartitionStream.cpp | 2 +- Sming/Components/esptool/esptool | 2 +- Sming/Core/Data/Stream/SectionStream.cpp | 2 +- Sming/Core/Timer.h | 22 ++- Sming/System/include/c_types.h | 2 +- Sming/build.mk | 5 + Sming/project.mk | 3 +- docs/source/about.rst | 7 +- docs/source/upgrading/4.3-4.4.rst | 6 + tests/HostTests/modules/BitSet.cpp | 2 +- tests/HostTests/modules/Clocks.cpp | 2 +- 73 files changed, 775 insertions(+), 501 deletions(-) rename Sming/Arch/Esp32/Components/esp32/{project => }/.gitignore (100%) delete mode 100644 Sming/Arch/Esp32/Components/esp32/ld/README.rst delete mode 100644 Sming/Arch/Esp32/Components/esp32/ld/common.ld delete mode 100644 Sming/Arch/Esp32/Components/esp32/ld/standalone.rom.ld rename Sming/Arch/Esp32/Components/esp32/{project => project.esp32}/CMakeLists.txt (86%) rename Sming/Arch/Esp32/Components/esp32/{project => project.esp32}/main/CMakeLists.txt (100%) rename Sming/Arch/Esp32/Components/esp32/{project => project.esp32}/main/main.c (100%) rename Sming/Arch/Esp32/Components/esp32/{project => project.esp32}/sdkconfig.defaults.debug (88%) rename Sming/Arch/Esp32/Components/esp32/{project => project.esp32}/sdkconfig.defaults.release (86%) create mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32c3/CMakeLists.txt create mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/CMakeLists.txt create mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/main.c create mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.debug create mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.release create mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s2/CMakeLists.txt create mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/CMakeLists.txt create mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/main.c create mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s2/sdkconfig.defaults.debug create mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s2/sdkconfig.defaults.release create mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s3/CMakeLists.txt create mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/CMakeLists.txt create mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/main.c create mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.debug create mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.release delete mode 100644 Sming/Arch/Esp32/Components/esp32/project/Makefile mode change 100755 => 100644 Sming/Arch/Esp32/Tools/install.sh diff --git a/README.md b/README.md index 514e6da443..00a487b03d 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ Linux and Windows OSes with gcc compilers are supported. Clang is NOT supported. ### Architecture: ESP32 (Experimental) -Supported SDK: ESP-IDF v4.1 +Supported SDK: ESP-IDF v4.3. See https://sming.readthedocs.io/en/latest/_inc/Sming/Arch/Esp32/README.html. ## Releases diff --git a/Sming/Arch/Esp32/Components/driver/include/driver/hw_timer.h b/Sming/Arch/Esp32/Components/driver/include/driver/hw_timer.h index 178a26c91c..5006b6c2f0 100644 --- a/Sming/Arch/Esp32/Components/driver/include/driver/hw_timer.h +++ b/Sming/Arch/Esp32/Components/driver/include/driver/hw_timer.h @@ -10,8 +10,16 @@ #pragma once +#if defined(SUBARCH_ESP32) || defined(SUBARCH_ESP32S2) +#define FRC_TIMER_ENABLED +#endif + #include +#ifdef FRC_TIMER_ENABLED #include +#else +#include +#endif #define HW_TIMER_BASE_CLK APB_CLK_FREQ @@ -85,6 +93,7 @@ void IRAM_ATTR hw_timer1_attach_interrupt(hw_timer_source_type_t source_type, hw */ inline void IRAM_ATTR hw_timer1_enable(hw_timer_clkdiv_t div, hw_timer_intr_type_t intr_type, bool auto_load) { +#ifdef FRC_TIMER_ENABLED uint32_t ctrl = (div & 0x0C) | (intr_type & 0x01) | FRC_TIMER_ENABLE; if(auto_load) { ctrl |= FRC_TIMER_AUTOLOAD; @@ -93,6 +102,7 @@ inline void IRAM_ATTR hw_timer1_enable(hw_timer_clkdiv_t div, hw_timer_intr_type REG_WRITE(FRC_TIMER_CTRL_REG(0), ctrl); // TM1_EDGE_INT_ENABLE(); // ETS_FRC1_INTR_ENABLE(); +#endif } /** @@ -101,7 +111,9 @@ inline void IRAM_ATTR hw_timer1_enable(hw_timer_clkdiv_t div, hw_timer_intr_type */ __forceinline void IRAM_ATTR hw_timer1_write(uint32_t ticks) { +#ifdef FRC_TIMER_ENABLED REG_WRITE(FRC_TIMER_LOAD_REG(0), ticks); +#endif } /** @@ -129,7 +141,11 @@ __forceinline void IRAM_ATTR hw_timer1_detach_interrupt(void) */ __forceinline uint32_t hw_timer1_read(void) { +#ifdef FRC_TIMER_ENABLED return REG_READ(FRC_TIMER_COUNT_REG(0)); +#else + return 0; +#endif } /************************************* @@ -151,7 +167,11 @@ constexpr uint32_t HW_TIMER2_CLK = HW_TIMER_BASE_CLK >> HW_TIMER2_CLKDIV; */ __forceinline uint32_t hw_timer2_read(void) { +#ifdef FRC_TIMER_ENABLED return REG_READ(FRC_TIMER_COUNT_REG(1)); +#else + return esp_timer_get_time(); +#endif } #define NOW() hw_timer2_read() diff --git a/Sming/Arch/Esp32/Components/driver/include/driver/os_timer.h b/Sming/Arch/Esp32/Components/driver/include/driver/os_timer.h index d426adbdd6..74eae9f135 100644 --- a/Sming/Arch/Esp32/Components/driver/include/driver/os_timer.h +++ b/Sming/Arch/Esp32/Components/driver/include/driver/os_timer.h @@ -15,7 +15,7 @@ #pragma once -#include +#include #ifdef __cplusplus extern "C" { diff --git a/Sming/Arch/Esp32/Components/driver/include/driver/uart.h b/Sming/Arch/Esp32/Components/driver/include/driver/uart.h index dd8501aad8..ba4c6e3664 100644 --- a/Sming/Arch/Esp32/Components/driver/include/driver/uart.h +++ b/Sming/Arch/Esp32/Components/driver/include/driver/uart.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #define UART_PHYSICAL_COUNT SOC_UART_NUM ///< Number of physical UARTs on the system #define UART_COUNT SOC_UART_NUM ///< Number of UARTs on the system, virtual or otherwise diff --git a/Sming/Arch/Esp32/Components/driver/uart.cpp b/Sming/Arch/Esp32/Components/driver/uart.cpp index 2c60239523..20acf62d43 100644 --- a/Sming/Arch/Esp32/Components/driver/uart.cpp +++ b/Sming/Arch/Esp32/Components/driver/uart.cpp @@ -7,6 +7,15 @@ #include #include #include + +// These conflict with enumerated types defined in IDF - values are same though +#undef UART_PARITY_NONE +#undef UART_PARITY_EVEN +#undef UART_PARITY_ODD + +// #define typeof(x) std::remove_volatile::type +#define typeof(x) decltype(x) +#include #include /* @@ -45,7 +54,9 @@ struct smg_uart_hardware_t { constexpr smg_uart_hardware_t uartHardware[UART_COUNT] = { {UART0, uart_periph_signal[0], 1, 3}, {UART1, uart_periph_signal[1], 10, 9}, +#if UART_COUNT > 2 {UART2, uart_periph_signal[2], 17, 16}, +#endif }; // Keep a reference to all created UARTS @@ -57,6 +68,12 @@ struct smg_uart_instance_t { static smg_uart_instance_t uartInstances[UART_COUNT]; +#if SUBARCH_ESP32 +#define FIFO(dev) dev.fifo.rw_byte +#else +#define FIFO(dev) dev.ahb_fifo.rw_byte +#endif + // Get number of characters in receive FIFO __forceinline static uint8_t uart_rxfifo_count(uint8_t nr) { @@ -249,7 +266,7 @@ size_t smg_uart_read(smg_uart_t* uart, void* buffer, size_t size) if(is_physical(uart)) { auto& hw = uartHardware[uart->uart_nr]; while(read < size && uart_rxfifo_count(uart->uart_nr) != 0) { - buf[read++] = hw.dev.fifo.rw_byte; + buf[read++] = FIFO(hw.dev); } // FIFO full may have been disabled if buffer overflowed, re-enabled it now @@ -324,7 +341,7 @@ static void IRAM_ATTR uart_isr(smg_uart_instance_t* inst) read = (avail <= space) ? avail : space; space -= read; while(read-- != 0) { - uint8_t c = hw.dev.fifo.rw_byte; + uint8_t c = FIFO(hw.dev); uart->rx_buffer->writeChar(c); } @@ -357,7 +374,7 @@ static void IRAM_ATTR uart_isr(smg_uart_instance_t* inst) size_t avail = uart->tx_buffer->available(); size_t count = (avail <= space) ? avail : space; while(count-- != 0) { - hw.dev.fifo.rw_byte = uart->tx_buffer->readChar(); + FIFO(hw.dev) = uart->tx_buffer->readChar(); } } @@ -392,9 +409,15 @@ void smg_uart_start_isr(smg_uart_t* uart) decltype(uart_dev_t::conf1) conf1{}; decltype(uart_dev_t::int_ena) int_ena{}; + auto& hw = uartHardware[uart->uart_nr]; + if(smg_uart_rx_enabled(uart)) { conf1.rxfifo_full_thrhd = 120; +#if SUBARCH_ESP32 conf1.rx_tout_thrhd = 2; +#else + hw.dev.mem_conf.rx_tout_thrhd = 2; +#endif conf1.rx_tout_en = true; /* @@ -421,7 +444,6 @@ void smg_uart_start_isr(smg_uart_t* uart) // conf1.txfifo_empty_thrhd = 0; } - auto& hw = uartHardware[uart->uart_nr]; hw.dev.conf1.val = conf1.val; hw.dev.int_clr.val = 0x0007ffff; hw.dev.int_ena.val = int_ena.val; @@ -451,7 +473,7 @@ size_t smg_uart_write(smg_uart_t* uart, const void* buffer, size_t size) if(uart->tx_buffer == nullptr || uart->tx_buffer->isEmpty()) { auto& hw = uartHardware[uart->uart_nr]; while(written < size && !uart_txfifo_full(uart->uart_nr)) { - hw.dev.fifo.rw_byte = buf[written++]; + FIFO(hw.dev) = buf[written++]; } // Enable TX FIFO EMPTY interrupt hw.dev.int_clr.txfifo_empty = true; @@ -583,7 +605,7 @@ void smg_uart_flush(smg_uart_t* uart, smg_uart_mode_t mode) // If receive overflow occurred then these interrupts will be masked if(flushRx) { -#if CONFIG_IDF_TARGET_ESP32 +#if SUBARCH_ESP32 // Hardware issue: we can not use `rxfifo_rst` to reset the hw rxfifo while(true) { auto fifo_cnt = hw.dev.status.rxfifo_cnt; @@ -593,7 +615,7 @@ void smg_uart_flush(smg_uart_t* uart, smg_uart_mode_t mode) break; } - (void)hw.dev.fifo.rw_byte; + (void)FIFO(hw.dev); } #else hw.dev.conf0.rxfifo_rst = true; @@ -625,14 +647,22 @@ uint32_t smg_uart_set_baudrate_reg(int uart_nr, uint32_t baud_rate) auto& hw = uartHardware[uart_nr]; - uint32_t sclk_freq = uart_use_apb_clock ? APB_CLK_FREQ : REF_CLK_FREQ; +#if SOC_UART_SUPPORT_XTAL_CLK +#define UART_ALT_CLK UART_SCLK_XTAL +#define UART_ALT_CLK_FREQ XTAL_CLK_FREQ +#else +#define UART_ALT_CLK UART_SCLK_REF_TICK +#define UART_ALT_CLK_FREQ REF_CLK_FREQ +#endif + + uint32_t sclk_freq = uart_use_apb_clock ? APB_CLK_FREQ : UART_ALT_CLK_FREQ; uint32_t clk_div = 16U * sclk_freq / baud_rate; // The baud-rate configuration register is divided into // an integer part and a fractional part. hw.dev.clk_div.div_int = clk_div / 16U; hw.dev.clk_div.div_frag = clk_div % 16U; // Configure the UART source clock. - hw.dev.conf0.tick_ref_always_on = uart_use_apb_clock; + uart_ll_set_sclk(&hw.dev, uart_use_apb_clock ? UART_SCLK_APB : UART_ALT_CLK); // Return the actual baud rate in use return 16U * sclk_freq / clk_div; diff --git a/Sming/Arch/Esp32/Components/esp32/project/.gitignore b/Sming/Arch/Esp32/Components/esp32/.gitignore similarity index 100% rename from Sming/Arch/Esp32/Components/esp32/project/.gitignore rename to Sming/Arch/Esp32/Components/esp32/.gitignore diff --git a/Sming/Arch/Esp32/Components/esp32/component.mk b/Sming/Arch/Esp32/Components/esp32/component.mk index bb207b3ad9..7ee639dc2c 100644 --- a/Sming/Arch/Esp32/Components/esp32/component.mk +++ b/Sming/Arch/Esp32/Components/esp32/component.mk @@ -1,11 +1,6 @@ SDK_COMPONENTS_PATH := $(IDF_PATH)/components # See build.mk for default ESP_VARIANT - determines toolchain -ifeq (esp32s2,$(ESP_VARIANT)) -SDK_BUILD_VARIANT := esp32s2beta -else -SDK_BUILD_VARIANT := $(ESP_VARIANT) -endif COMPONENT_DEPENDS := libc @@ -16,35 +11,39 @@ COMPONENT_INCDIRS := src/include include CACHE_VARS += SDK_FULL_BUILD SDK_FULL_BUILD ?= 0 -SDK_BUILD_BASE := $(COMPONENT_BUILD_DIR)/sdk -SDK_COMPONENT_LIBDIR := $(COMPONENT_BUILD_DIR)/lib +SDK_BUILD_BASE := $(COMPONENT_BUILD_DIR)/sdk.$(ESP_VARIANT) +SDK_COMPONENT_LIBDIR := $(COMPONENT_BUILD_DIR)/lib.$(ESP_VARIANT) SDKCONFIG_H := $(SDK_BUILD_BASE)/config/sdkconfig.h SDK_LIBDIRS := \ - esp_wifi/lib/$(SDK_BUILD_VARIANT) \ - xtensa/$(SDK_BUILD_VARIANT)/ \ - esp32/ld \ - esp_rom/esp32/ld + esp_wifi/lib/$(ESP_VARIANT) \ + xtensa/$(ESP_VARIANT)/ \ + hal/$(ESP_VARIANT)/ \ + $(ESP_VARIANT)/ld \ + esp_rom/$(ESP_VARIANT)/ld LIBDIRS += \ $(SDK_COMPONENT_LIBDIR) \ - $(SDK_BUILD_BASE)/esp-idf/$(SDK_BUILD_VARIANT) \ - $(SDK_BUILD_BASE)/esp-idf/$(SDK_BUILD_VARIANT)/ld \ + $(SDK_BUILD_BASE)/esp-idf/$(ESP_VARIANT) \ + $(SDK_BUILD_BASE)/esp-idf/$(ESP_VARIANT)/ld \ $(COMPONENT_PATH)/ld \ $(addprefix $(SDK_COMPONENTS_PATH)/,$(SDK_LIBDIRS)) SDK_INCDIRS := \ bootloader_support/include \ bootloader_support/include_bootloader \ + driver/$(ESP_VARIANT)/include \ driver/include \ - driver/include/driver \ efuse/include \ - efuse/esp32/include \ - esp32/include \ + efuse/$(ESP_VARIANT)/include \ + esp_rom/include/$(ESP_VARIANT) \ + esp_rom/include \ + $(ESP_VARIANT)/include \ espcoredump/include \ + esp_timer/include \ soc/include \ - soc/esp32/include \ + soc/$(ESP_VARIANT)/include \ heap/include \ log/include \ nvs_flash/include \ @@ -68,7 +67,11 @@ SDK_INCDIRS := \ wpa_supplicant/port/include \ app_trace/include \ app_update/include \ - smartconfig_ack/include + smartconfig_ack/include \ + esp_hw_support/include \ + hal/include \ + hal/$(ESP_VARIANT)/include \ + esp_system/include ifeq ($(SDK_FULL_BUILD),1) SDK_INCDIRS += \ @@ -83,15 +86,23 @@ SDK_INCDIRS += \ esp_netif/include \ esp_eth/include \ esp_event/private_include \ - esp_rom/include \ esp_wifi/include \ esp_wifi/esp32/include \ lwip/include/apps/sntp \ spi_flash/private_include \ - wpa_supplicant/include/esp_supplicant \ + wpa_supplicant/include/esp_supplicant + +ifdef IDF_TARGET_ARCH_RISCV +SDK_INCDIRS += \ + freertos/port/riscv/include \ + riscv/include +else +SDK_INCDIRS += \ xtensa/include \ - xtensa/esp32/include - + xtensa/$(ESP_VARIANT)/include \ + freertos/port/xtensa/include +endif + ifeq ($(CONFIG_BT_NIMBLE_ENABLED),y) SDK_INCDIRS += \ bt/include \ @@ -132,15 +143,21 @@ SDK_COMPONENTS := \ driver \ efuse \ esp-tls \ - esp32 \ - esp_adc_cal \ + $(ESP_VARIANT) \ esp_common \ esp_event \ esp_gdbstub \ + esp_hw_support \ + esp_ipc \ + esp_pm \ esp_ringbuf \ + esp_rom \ + esp_system \ + esp_timer \ esp_wifi \ espcoredump \ freertos \ + hal \ heap \ log \ lwip \ @@ -159,8 +176,17 @@ SDK_COMPONENTS := \ tcpip_adapter \ vfs \ wifi_provisioning \ - wpa_supplicant \ - xtensa + wpa_supplicant + +ifneq ($(ESP_VARIANT),esp32s3) +SDK_COMPONENTS += esp_adc_cal +endif + +ifdef IDF_TARGET_ARCH_RISCV +SDK_COMPONENTS += riscv +else +SDK_COMPONENTS += xtensa +endif ifeq ($(SDK_FULL_BUILD),1) SDK_COMPONENTS += \ @@ -199,44 +225,73 @@ SDK_ESP_WIFI_LIBS := \ net80211 \ phy \ pp \ - rtc \ smartconfig +ifeq ($(ESP_VARIANT),esp32) +SDK_ESP_WIFI_LIBS += rtc +endif + SDK_NEWLIB_LIBS := \ c \ m \ stdc++ -SDK_XTENSA_LIBS := \ - hal +ifdef IDF_TARGET_ARCH_RISCV +SDK_TARGET_ARCH_LIBS := hal +else +SDK_TARGET_ARCH_LIBS := hal xt_hal +endif EXTRA_LIBS := \ gcc \ $(SDK_COMPONENTS) \ $(SDK_ESP_WIFI_LIBS) \ $(SDK_NEWLIB_LIBS) \ - $(SDK_XTENSA_LIBS) + $(SDK_TARGET_ARCH_LIBS) + +LinkerScript = -T $(ESP_VARIANT).$1.ld + +LDFLAGS_esp32 := \ + $(call LinkerScript,rom.newlib-funcs) \ + $(call LinkerScript,rom.newlib-data) \ + $(call LinkerScript,rom.syscalls) \ + $(call LinkerScript,rom.newlib-time) \ + $(call LinkerScript,rom.eco3) + +LDFLAGS_esp32s2 := \ + $(call LinkerScript,rom.newlib-funcs) \ + $(call LinkerScript,rom.newlib-data) \ + $(call LinkerScript,rom.spiflash) + +LDFLAGS_esp32c3 := \ + $(call LinkerScript,rom.newlib) \ + $(call LinkerScript,rom.version) \ + $(call LinkerScript,rom.eco3) + +LDFLAGS_esp32s3 := \ + $(call LinkerScript,rom.newlib-funcs) \ + $(call LinkerScript,rom.newlib-data) \ + $(call LinkerScript,rom.spiflash) EXTRA_LDFLAGS := \ -u esp_app_desc \ -u __cxa_guard_dummy -u __cxx_fatal_exception \ - -T esp32_out.ld \ + -T $(ESP_VARIANT)_out.ld \ -u ld_include_panic_highint_hdl \ - -T esp32.project.ld \ - -T esp32.peripherals.ld \ - -T esp32.rom.ld \ - -T esp32.rom.libgcc.ld \ - -T esp32.rom.syscalls.ld \ - -T esp32.rom.newlib-data.ld \ - -T esp32.rom.newlib-funcs.ld \ + $(call LinkerScript,project) \ + $(call LinkerScript,peripherals) \ + $(call LinkerScript,rom) \ + $(call LinkerScript,rom.api) \ + $(call LinkerScript,rom.libgcc) \ -u newlib_include_locks_impl \ -u newlib_include_heap_impl \ -u newlib_include_syscalls_impl \ -u pthread_include_pthread_impl \ -u pthread_include_pthread_cond_impl \ -u pthread_include_pthread_local_storage_impl \ - -Wl,--undefined=uxTopUsedPriority - + -Wl,--undefined=uxTopUsedPriority \ + $(LDFLAGS_$(ESP_VARIANT)) + FLASH_BOOT_LOADER := $(SDK_BUILD_BASE)/bootloader/bootloader.bin FLASH_BOOT_CHUNKS := 0x1000=$(FLASH_BOOT_LOADER) @@ -248,10 +303,10 @@ SDK_PARTITION_PATH := $(SDK_DEFAULT_PATH)/partitions ##@SDK -SDK_PROJECT_PATH := $(COMPONENT_PATH)/project +SDK_PROJECT_PATH := $(COMPONENT_PATH)/project.$(ESP_VARIANT) SDK_CONFIG_DEFAULTS := $(SDK_PROJECT_PATH)/sdkconfig.defaults -SDKCONFIG_MAKEFILE ?= $(SDK_PROJECT_PATH)/sdkconfig +SDKCONFIG_MAKEFILE := $(SDK_PROJECT_PATH)/sdkconfig ifeq ($(MAKE_DOCS),) -include $(SDKCONFIG_MAKEFILE) endif @@ -296,7 +351,7 @@ sdk-menuconfig: $(SDK_CONFIG_DEFAULTS) | $(SDK_BUILD_BASE) ##Configure SDK optio sdk-defconfig: $(SDK_BUILD_BASE)/include/sdkconfig.h ##Create default SDK config files .PHONY: sdk-menuconfig-clean -sdk-menuconfig-clean: +sdk-menuconfig-clean: esp32-clean ##Wipe SDK configuration and revert to defaults $(Q) rm -f $(SDKCONFIG_MAKEFILE) $(SDK_CONFIG_DEFAULTS) .PHONY: sdk-help diff --git a/Sming/Arch/Esp32/Components/esp32/ld/README.rst b/Sming/Arch/Esp32/Components/esp32/ld/README.rst deleted file mode 100644 index 57c98641e3..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/ld/README.rst +++ /dev/null @@ -1 +0,0 @@ -Esp32 linker files \ No newline at end of file diff --git a/Sming/Arch/Esp32/Components/esp32/ld/common.ld b/Sming/Arch/Esp32/Components/esp32/ld/common.ld deleted file mode 100644 index 4ba32ec5e3..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/ld/common.ld +++ /dev/null @@ -1,174 +0,0 @@ -/* - common.ld -*/ - -SECTIONS -{ - /* - IRAM is split into .text and .text1 to allow for moving specific - functions into IRAM that would be matched by the irom0.text matcher - */ - .text : ALIGN(4) - { - /* - GCC silently ignores section attributes on templated code. - The only practical workaround is enforcing sections in the linker script. - */ - *(.literal._ZN13CallbackTimer*) - *(.text._ZN13CallbackTimer*) - *(.text._ZNKSt8functionIF*EE*) /* std::function::operator()() const */ - *(.text._ZN9Profiling6MinMaxIjE6updateEj) - - } >iram1_0_seg :iram1_0_phdr - - .irom0.text : ALIGN(4) - { - _irom0_text_start = ABSOLUTE(.); - - *libc.a:(.literal .text .literal.* .text.*) - *libm.a:(.literal .text .literal.* .text.*) - *gcc.a:_divsf3.o(.literal .text) - *gcc.a:_fixsfsi.o(.literal .text) - *gcc.a:_cmpdf2.o(.literal .text) - *gcc.a:_cmpsf2.o(.literal .text) - *libstdc++.a:(.literal .text .literal.* .text.*) - - *libsmartconfig.a:(.literal .text .literal.* .text.*) - *libat.a:(.literal.* .text.*) - *libcrypto.a:(.literal.* .text.*) - *libespnow.a:(.literal.* .text.*) - *libjson.a:(.literal.* .text.*) - *liblwip.a:(.literal.* .text.*) - *libmesh.a:(.literal.* .text.*) - *libnet80211.a:(.literal.* .text.*) - *libsmartconfig.a:(.literal.* .text.*) - *libssl.a:(.literal.* .text.*) - *libupgrade.a:(.literal.* .text.*) - *libwpa.a:(.literal.* .text.*) - *libwpa2.a:(.literal.* .text.*) - *libwps.a:(.literal.* .text.*) - - *libmbedtls.a:(.literal.* .text.*) - - /* C++ vtables */ - *(.rodata._ZTV*) - - *(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom0.text.* .irom.text .irom.text.* .irom.debug.*) - - /* Generated libraries */ - *liblwip2.a:(.literal .text .literal.* .text.*) - */clib-*.a:*(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.* .irom.debug.*) - - /* Constant strings in flash (PSTRs) */ - *(.irom0.pstr.*) - - /* __FUNCTION__ locals */ - *(.rodata._ZZ*__FUNCTION__) - *(.rodata._ZZ*__PRETTY_FUNCTION__) - *(.rodata._ZZ*__func__) - *(.rodata.__func__*) - - /* Inline flash strings, including those within templated code */ - *(*__pstr__*) - *(*__fstr__*) - - /* Templated code */ - *(.rodata._ZN8NanoTimeL9unitTicksE) - - _irom0_text_end = ABSOLUTE(.); - _flash_code_end = ABSOLUTE(.); - } >irom0_0_seg :irom0_0_phdr - - - /* Pick up any remaining IRAM objects and close the text segment */ - .text1 : ALIGN(4) - { - *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) - - *(.fini.literal) - *(.fini) - *(.gnu.version) - _text_end = ABSOLUTE(.); - _etext = .; - } >iram1_0_seg :iram1_0_phdr - - .rodata : ALIGN(4) - { - _rodata_start = ABSOLUTE(.); - *(.sdk.version) - *(.rodata) - *(.rodata.*) - *(.gnu.linkonce.r.*) - *(.rodata1) - __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); - *(.xt_except_table) - *(.gcc_except_table) - *(.gnu.linkonce.e.*) - *(.gnu.version_r) - *(.eh_frame) - . = (. + 3) & ~ 3; - /* C++ constructor and destructor tables, properly ordered: */ - __dso_handle = ABSOLUTE(.); - __init_array_start = ABSOLUTE(.); - KEEP (*crtbegin.o(.ctors)) - KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) - KEEP (*(SORT(.ctors.*))) - KEEP (*(.ctors)) - __init_array_end = ABSOLUTE(.); - KEEP (*crtbegin.o(.dtors)) - KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) - KEEP (*(SORT(.dtors.*))) - KEEP (*(.dtors)) - /* C++ exception handlers table: */ - __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); - *(.xt_except_desc) - *(.gnu.linkonce.h.*) - __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); - *(.xt_except_desc_end) - *(.dynamic) - *(.gnu.version_d) - . = ALIGN(4); /* this table MUST be 4-byte aligned */ - _bss_table_start = ABSOLUTE(.); - LONG(_bss_start) - LONG(_bss_end) - _bss_table_end = ABSOLUTE(.); - _rodata_end = ABSOLUTE(.); - } >dram0_0_seg :dram0_0_phdr - - - .bss ALIGN(8) (NOLOAD) : ALIGN(4) - { - . = ALIGN (8); - _bss_start = ABSOLUTE(.); - *(.dynsbss) - *(.sbss) - *(.sbss.*) - *(.gnu.linkonce.sb.*) - *(.scommon) - *(.sbss2) - *(.sbss2.*) - *(.gnu.linkonce.sb2.*) - *(.dynbss) - *(.bss) - *(.bss.*) - *(.gnu.linkonce.b.*) - *(COMMON) - . = ALIGN (8); - _bss_end = ABSOLUTE(.); - _heap_start = ABSOLUTE(.); -/* _stack_sentry = ALIGN(0x8); */ - } >dram0_0_seg :dram0_0_bss_phdr -/* __stack = 0x3ffc8000; */ - - .lit4 : ALIGN(4) - { - _lit4_start = ABSOLUTE(.); - *(*.lit4) - *(.lit4.*) - *(.gnu.linkonce.lit4.*) - _lit4_end = ABSOLUTE(.); - } >iram1_0_seg :iram1_0_phdr -} - -/* get ROM code address */ -INCLUDE "eagle.rom.addr.v6.ld" diff --git a/Sming/Arch/Esp32/Components/esp32/ld/standalone.rom.ld b/Sming/Arch/Esp32/Components/esp32/ld/standalone.rom.ld deleted file mode 100644 index 0ce72ca5cf..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/ld/standalone.rom.ld +++ /dev/null @@ -1,13 +0,0 @@ -/* linker script to bind symbols to their correct segments */ - -/* -MEMORY -{ - dport0_0_seg : org = 0x3FF00000, len = 0x10 - dram0_0_seg : org = 0x3FFE8000, len = 0x14000 - iram1_0_seg : org = 0x40100000, len = 0x8000 - irom0_0_seg : org = 0x4020a000, len = (1M - 0x0a000) -} - -INCLUDE "common.ld" -*/ \ No newline at end of file diff --git a/Sming/Arch/Esp32/Components/esp32/project/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/project.esp32/CMakeLists.txt similarity index 86% rename from Sming/Arch/Esp32/Components/esp32/project/CMakeLists.txt rename to Sming/Arch/Esp32/Components/esp32/project.esp32/CMakeLists.txt index 75c10c7dcf..bd18c8ced4 100644 --- a/Sming/Arch/Esp32/Components/esp32/project/CMakeLists.txt +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32/CMakeLists.txt @@ -2,5 +2,7 @@ # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) +set(IDF_TARGET "$ENV{ESP_VARIANT}") + include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(Sming) diff --git a/Sming/Arch/Esp32/Components/esp32/project/main/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/project.esp32/main/CMakeLists.txt similarity index 100% rename from Sming/Arch/Esp32/Components/esp32/project/main/CMakeLists.txt rename to Sming/Arch/Esp32/Components/esp32/project.esp32/main/CMakeLists.txt diff --git a/Sming/Arch/Esp32/Components/esp32/project/main/main.c b/Sming/Arch/Esp32/Components/esp32/project.esp32/main/main.c similarity index 100% rename from Sming/Arch/Esp32/Components/esp32/project/main/main.c rename to Sming/Arch/Esp32/Components/esp32/project.esp32/main/main.c diff --git a/Sming/Arch/Esp32/Components/esp32/project/sdkconfig.defaults.debug b/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.debug similarity index 88% rename from Sming/Arch/Esp32/Components/esp32/project/sdkconfig.defaults.debug rename to Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.debug index cb3c9cab5c..4f07843fa8 100644 --- a/Sming/Arch/Esp32/Components/esp32/project/sdkconfig.defaults.debug +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.debug @@ -1,23 +1,22 @@ # -# Automatically generated file. DO NOT EDIT. # Espressif IoT Development Framework (ESP-IDF) Project Configuration # # SMING DEBUG SETTINGS # -CONFIG_IDF_TARGET_ESP32=y -CONFIG_IDF_TARGET="esp32" -CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 # # SDK tool configuration # +CONFIG_COMPILER_OPTIMIZATION_SIZE=y + # Mandatory LWIP changes CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 # Mandatory Sming framework changes CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 +CONFIG_ESP_TIMER_IMPL_FRC2=y # The bootloader logs all type of messages CONFIG_BOOTLOADER_LOG_LEVEL_NONE= diff --git a/Sming/Arch/Esp32/Components/esp32/project/sdkconfig.defaults.release b/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.release similarity index 86% rename from Sming/Arch/Esp32/Components/esp32/project/sdkconfig.defaults.release rename to Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.release index 255cb2c690..400963c451 100644 --- a/Sming/Arch/Esp32/Components/esp32/project/sdkconfig.defaults.release +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.release @@ -1,23 +1,22 @@ # -# Automatically generated file. DO NOT EDIT. # Espressif IoT Development Framework (ESP-IDF) Project Configuration # # SMING RELEASE SETTINGS # -CONFIG_IDF_TARGET_ESP32=y -CONFIG_IDF_TARGET="esp32" -CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 # # SDK tool configuration # +CONFIG_COMPILER_OPTIMIZATION_SIZE=y + # Mandatory LWIP changes CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 # Mandatory Sming framework changes CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 +CONFIG_ESP_TIMER_IMPL_FRC2=y # The bootloader logs only errors CONFIG_BOOTLOADER_LOG_LEVEL_NONE= diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32c3/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/project.esp32c3/CMakeLists.txt new file mode 100644 index 0000000000..bd18c8ced4 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32c3/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(IDF_TARGET "$ENV{ESP_VARIANT}") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(Sming) diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/CMakeLists.txt new file mode 100644 index 0000000000..cf2c455cb5 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/main.c b/Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/main.c new file mode 100644 index 0000000000..cb05851571 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/main.c @@ -0,0 +1,3 @@ +void app_main(void) +{ +} diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.debug b/Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.debug new file mode 100644 index 0000000000..8d660b03c2 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.debug @@ -0,0 +1,37 @@ +# +# Espressif IoT Development Framework (ESP-IDF) Project Configuration +# +# SMING DEBUG SETTINGS +# + +# +# SDK tool configuration +# + +# Mandatory LWIP changes +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 + +# Mandatory Sming framework changes +CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 + +# The bootloader logs all type of messages +CONFIG_BOOTLOADER_LOG_LEVEL_NONE= +CONFIG_BOOTLOADER_LOG_LEVEL_ERROR= +CONFIG_BOOTLOADER_LOG_LEVEL_WARN= +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y +CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= +CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= +CONFIG_BOOTLOADER_LOG_LEVEL=3 + +# ESP-IDF logs all type of messages +CONFIG_LOG_DEFAULT_LEVEL_NONE= +CONFIG_LOG_DEFAULT_LEVEL_ERROR= +CONFIG_LOG_DEFAULT_LEVEL_WARN= +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +CONFIG_LOG_DEFAULT_LEVEL_DEBUG= +CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= +CONFIG_LOG_DEFAULT_LEVEL=3 + +# Sets watchpoint index 1 (the second of two) at the end of any task stack. This is the most accurate way to debug task stack overflows. +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.release b/Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.release new file mode 100644 index 0000000000..c0346b7594 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.release @@ -0,0 +1,34 @@ +# +# Espressif IoT Development Framework (ESP-IDF) Project Configuration +# +# SMING RELEASE SETTINGS +# + +# +# SDK tool configuration +# + +# Mandatory LWIP changes +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 + +# Mandatory Sming framework changes +CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 + +# The bootloader logs only errors +CONFIG_BOOTLOADER_LOG_LEVEL_NONE= +CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y +CONFIG_BOOTLOADER_LOG_LEVEL_WARN= +CONFIG_BOOTLOADER_LOG_LEVEL_INFO= +CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= +CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= +CONFIG_BOOTLOADER_LOG_LEVEL=1 + +# ESP-IDF logs only errors +CONFIG_LOG_DEFAULT_LEVEL_NONE= +CONFIG_LOG_DEFAULT_LEVEL_ERROR=y +CONFIG_LOG_DEFAULT_LEVEL_WARN= +CONFIG_LOG_DEFAULT_LEVEL_INFO= +CONFIG_LOG_DEFAULT_LEVEL_DEBUG= +CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= +CONFIG_LOG_DEFAULT_LEVEL=1 diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s2/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/project.esp32s2/CMakeLists.txt new file mode 100644 index 0000000000..bd18c8ced4 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32s2/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(IDF_TARGET "$ENV{ESP_VARIANT}") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(Sming) diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/CMakeLists.txt new file mode 100644 index 0000000000..cf2c455cb5 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/main.c b/Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/main.c new file mode 100644 index 0000000000..cb05851571 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/main.c @@ -0,0 +1,3 @@ +void app_main(void) +{ +} diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s2/sdkconfig.defaults.debug b/Sming/Arch/Esp32/Components/esp32/project.esp32s2/sdkconfig.defaults.debug new file mode 100644 index 0000000000..8d660b03c2 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32s2/sdkconfig.defaults.debug @@ -0,0 +1,37 @@ +# +# Espressif IoT Development Framework (ESP-IDF) Project Configuration +# +# SMING DEBUG SETTINGS +# + +# +# SDK tool configuration +# + +# Mandatory LWIP changes +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 + +# Mandatory Sming framework changes +CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 + +# The bootloader logs all type of messages +CONFIG_BOOTLOADER_LOG_LEVEL_NONE= +CONFIG_BOOTLOADER_LOG_LEVEL_ERROR= +CONFIG_BOOTLOADER_LOG_LEVEL_WARN= +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y +CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= +CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= +CONFIG_BOOTLOADER_LOG_LEVEL=3 + +# ESP-IDF logs all type of messages +CONFIG_LOG_DEFAULT_LEVEL_NONE= +CONFIG_LOG_DEFAULT_LEVEL_ERROR= +CONFIG_LOG_DEFAULT_LEVEL_WARN= +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +CONFIG_LOG_DEFAULT_LEVEL_DEBUG= +CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= +CONFIG_LOG_DEFAULT_LEVEL=3 + +# Sets watchpoint index 1 (the second of two) at the end of any task stack. This is the most accurate way to debug task stack overflows. +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s2/sdkconfig.defaults.release b/Sming/Arch/Esp32/Components/esp32/project.esp32s2/sdkconfig.defaults.release new file mode 100644 index 0000000000..c0346b7594 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32s2/sdkconfig.defaults.release @@ -0,0 +1,34 @@ +# +# Espressif IoT Development Framework (ESP-IDF) Project Configuration +# +# SMING RELEASE SETTINGS +# + +# +# SDK tool configuration +# + +# Mandatory LWIP changes +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 + +# Mandatory Sming framework changes +CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 + +# The bootloader logs only errors +CONFIG_BOOTLOADER_LOG_LEVEL_NONE= +CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y +CONFIG_BOOTLOADER_LOG_LEVEL_WARN= +CONFIG_BOOTLOADER_LOG_LEVEL_INFO= +CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= +CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= +CONFIG_BOOTLOADER_LOG_LEVEL=1 + +# ESP-IDF logs only errors +CONFIG_LOG_DEFAULT_LEVEL_NONE= +CONFIG_LOG_DEFAULT_LEVEL_ERROR=y +CONFIG_LOG_DEFAULT_LEVEL_WARN= +CONFIG_LOG_DEFAULT_LEVEL_INFO= +CONFIG_LOG_DEFAULT_LEVEL_DEBUG= +CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= +CONFIG_LOG_DEFAULT_LEVEL=1 diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s3/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/project.esp32s3/CMakeLists.txt new file mode 100644 index 0000000000..bd18c8ced4 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32s3/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(IDF_TARGET "$ENV{ESP_VARIANT}") + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(Sming) diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/CMakeLists.txt new file mode 100644 index 0000000000..cf2c455cb5 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/main.c b/Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/main.c new file mode 100644 index 0000000000..cb05851571 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/main.c @@ -0,0 +1,3 @@ +void app_main(void) +{ +} diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.debug b/Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.debug new file mode 100644 index 0000000000..8d660b03c2 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.debug @@ -0,0 +1,37 @@ +# +# Espressif IoT Development Framework (ESP-IDF) Project Configuration +# +# SMING DEBUG SETTINGS +# + +# +# SDK tool configuration +# + +# Mandatory LWIP changes +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 + +# Mandatory Sming framework changes +CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 + +# The bootloader logs all type of messages +CONFIG_BOOTLOADER_LOG_LEVEL_NONE= +CONFIG_BOOTLOADER_LOG_LEVEL_ERROR= +CONFIG_BOOTLOADER_LOG_LEVEL_WARN= +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y +CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= +CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= +CONFIG_BOOTLOADER_LOG_LEVEL=3 + +# ESP-IDF logs all type of messages +CONFIG_LOG_DEFAULT_LEVEL_NONE= +CONFIG_LOG_DEFAULT_LEVEL_ERROR= +CONFIG_LOG_DEFAULT_LEVEL_WARN= +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +CONFIG_LOG_DEFAULT_LEVEL_DEBUG= +CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= +CONFIG_LOG_DEFAULT_LEVEL=3 + +# Sets watchpoint index 1 (the second of two) at the end of any task stack. This is the most accurate way to debug task stack overflows. +CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.release b/Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.release new file mode 100644 index 0000000000..c0346b7594 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.release @@ -0,0 +1,34 @@ +# +# Espressif IoT Development Framework (ESP-IDF) Project Configuration +# +# SMING RELEASE SETTINGS +# + +# +# SDK tool configuration +# + +# Mandatory LWIP changes +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 + +# Mandatory Sming framework changes +CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 + +# The bootloader logs only errors +CONFIG_BOOTLOADER_LOG_LEVEL_NONE= +CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y +CONFIG_BOOTLOADER_LOG_LEVEL_WARN= +CONFIG_BOOTLOADER_LOG_LEVEL_INFO= +CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= +CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= +CONFIG_BOOTLOADER_LOG_LEVEL=1 + +# ESP-IDF logs only errors +CONFIG_LOG_DEFAULT_LEVEL_NONE= +CONFIG_LOG_DEFAULT_LEVEL_ERROR=y +CONFIG_LOG_DEFAULT_LEVEL_WARN= +CONFIG_LOG_DEFAULT_LEVEL_INFO= +CONFIG_LOG_DEFAULT_LEVEL_DEBUG= +CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= +CONFIG_LOG_DEFAULT_LEVEL=1 diff --git a/Sming/Arch/Esp32/Components/esp32/project/Makefile b/Sming/Arch/Esp32/Components/esp32/project/Makefile deleted file mode 100644 index 35eaf80772..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# -# This is a project Makefile. It is assumed the directory this Makefile resides in is a -# project subdirectory. -# - -PROJECT_NAME := Sming - -include $(IDF_PATH)/make/project.mk - -Application.elf: - echo "Done!" - diff --git a/Sming/Arch/Esp32/Components/esp32/src/include/esp_clk.h b/Sming/Arch/Esp32/Components/esp32/src/include/esp_clk.h index 13ca0e73db..eaed0750d9 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/include/esp_clk.h +++ b/Sming/Arch/Esp32/Components/esp32/src/include/esp_clk.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #ifdef __cplusplus extern "C" { @@ -28,9 +28,9 @@ __forceinline uint32_t system_get_cpu_freq(void) return ets_get_cpu_frequency(); } -__forceinline uint32_t esp_get_ccount() +__forceinline static uint32_t esp_get_ccount() { - return XTHAL_GET_CCOUNT(); + return cpu_hal_get_cycle_count(); } #ifdef __cplusplus diff --git a/Sming/Arch/Esp32/Components/esp32/src/include/esp_libc.h b/Sming/Arch/Esp32/Components/esp32/src/include/esp_libc.h index 41850ec966..4f4728c247 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/include/esp_libc.h +++ b/Sming/Arch/Esp32/Components/esp32/src/include/esp_libc.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #ifdef __cplusplus extern "C" { diff --git a/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h b/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h index 25e620cd37..1d785f83b9 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h +++ b/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h @@ -27,7 +27,7 @@ #include "esp_tasks.h" #include #include -#include +#include #include #include #include @@ -35,7 +35,6 @@ #include #include #include -#include #define __ESP32_EX__ // System definition ESP8266 SOC @@ -47,12 +46,12 @@ * @retval Current interrupt level * @note Hardware timer is unaffected if operating in non-maskable mode */ -#define noInterrupts() XTOS_SET_INTLEVEL(XCHAL_EXCM_LEVEL) +#define noInterrupts() portENTER_CRITICAL_NESTED() /** @brief Enable interrupts */ -#define interrupts() XTOS_SET_INTLEVEL(0) +#define interrupts() portEXIT_CRITICAL_NESTED(0) /** @brief Restore interrupts to level saved from previous noInterrupts() call */ -#define restoreInterrupts(level) XTOS_RESTORE_INTLEVEL(level) +#define restoreInterrupts(state) portEXIT_CRITICAL_NESTED(state) diff --git a/Sming/Arch/Esp32/Components/esp32/src/include/ets_sys.h b/Sming/Arch/Esp32/Components/esp32/src/include/ets_sys.h index fd7231dadb..e6555b6f82 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/include/ets_sys.h +++ b/Sming/Arch/Esp32/Components/esp32/src/include/ets_sys.h @@ -1,3 +1,3 @@ #pragma once -#include +#include diff --git a/Sming/Arch/Esp32/Components/esp32/src/sleep.c b/Sming/Arch/Esp32/Components/esp32/src/sleep.c index 6d61f4d604..924e8f12b8 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/sleep.c +++ b/Sming/Arch/Esp32/Components/esp32/src/sleep.c @@ -1,4 +1,5 @@ #include "include/esp_sleep.h" +#include void system_deep_sleep(uint32_t time_in_us) { diff --git a/Sming/Arch/Esp32/Components/esp32/src/startup.cpp b/Sming/Arch/Esp32/Components/esp32/src/startup.cpp index cdb1ff521b..917276e375 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/startup.cpp +++ b/Sming/Arch/Esp32/Components/esp32/src/startup.cpp @@ -79,7 +79,7 @@ void main(void*) extern "C" void app_main(void) { #ifdef CONFIG_FREERTOS_UNICORE - xTaskCreate(loop, "Sming", ESP32_STACK_SIZE, nullptr, 1, nullptr); + xTaskCreate(main, "Sming", ESP32_STACK_SIZE, nullptr, 1, nullptr); #else esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(1)); xTaskCreatePinnedToCore(main, "Sming", ESP32_STACK_SIZE, nullptr, 1, nullptr, 1); diff --git a/Sming/Arch/Esp32/Components/esp32/src/system.cpp b/Sming/Arch/Esp32/Components/esp32/src/system.cpp index 7cd407ffa8..29cc7a3333 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/system.cpp +++ b/Sming/Arch/Esp32/Components/esp32/src/system.cpp @@ -2,6 +2,13 @@ #include #include +extern "C" uint32_t system_get_time(void) +{ + struct timeval tv; + gettimeofday(&tv, nullptr); + return tv.tv_sec * 1000000U + tv.tv_usec; +} + struct rst_info* system_get_rst_info(void) { static rst_info info{}; diff --git a/Sming/Arch/Esp32/Components/libc/include/esp_attr.h b/Sming/Arch/Esp32/Components/libc/include/esp_attr.h index 98a1749d09..ddae2e56eb 100644 --- a/Sming/Arch/Esp32/Components/libc/include/esp_attr.h +++ b/Sming/Arch/Esp32/Components/libc/include/esp_attr.h @@ -22,7 +22,7 @@ #define ICACHE_FLASH_ATTR \ __attribute__((section(ICACHE_FLASH_SECTION "." __FILE__ "." MACROQUOTE(__LINE__) "." MACROQUOTE(__COUNTER__)))) -#define ICACHE_RODATA_SECTION ".irom1.text" +#define ICACHE_RODATA_SECTION ".rodata" #undef ICACHE_RODATA_ATTR #define ICACHE_RODATA_ATTR \ diff --git a/Sming/Arch/Esp32/Components/libc/include/sys/pgmspace.h b/Sming/Arch/Esp32/Components/libc/include/sys/pgmspace.h index efc215c826..9315325204 100644 --- a/Sming/Arch/Esp32/Components/libc/include/sys/pgmspace.h +++ b/Sming/Arch/Esp32/Components/libc/include/sys/pgmspace.h @@ -32,49 +32,27 @@ extern "C" { #define PGM_P const char* #define PGM_VOID_P const void* -// flash memory must be read using 32 bit aligned addresses else a processor exception will be triggered -// order within the 32 bit values are -// -------------- -// b3, b2, b1, b0 -// w1, w0 - -#define pgm_read_with_offset(addr, res) \ - __asm__("extui %0, %1, 0, 2\n" /* Extract offset within word (in bytes) */ \ - "sub %1, %1, %0\n" /* Subtract offset from addr, yielding an aligned address */ \ - "l32i.n %1, %1, 0x0\n" /* Load word from aligned address */ \ - "ssa8l %0\n" /* Prepare to shift by offset (in bits) */ \ - "src %0, %1, %1\n" /* Shift right; now the requested byte is the first one */ \ - : "=r"(res), "=r"(addr) \ - : "1"(addr) \ - :); - -static inline uint8_t pgm_read_byte_inlined(const void* addr) -{ - uint32_t res; - pgm_read_with_offset(addr, res); - return (uint8_t)res; /* This masks the lower byte from the returned word */ -} - -/* Although this says "word", it's actually 16 bit, i.e. half word on Xtensa */ -static inline uint16_t pgm_read_word_inlined(const void* addr) -{ - uint32_t res; - pgm_read_with_offset(addr, res); - return (uint16_t)res; /* This masks the lower half-word from the returned word */ -} - /** * @name Macros to safely read PROGMEM locations * @{ */ -// Make sure, that libraries checking existence of this macro are not failing -#define pgm_read_byte(addr) pgm_read_byte_inlined(addr) -#define pgm_read_word(addr) pgm_read_word_inlined(addr) - -// No translation necessary (provided address is aligned) -#define pgm_read_dword(addr) (*(const unsigned long*)(addr)) -#define pgm_read_float(addr) (*(const float*)(addr)) +#define pgm_read_byte(addr) (*(const unsigned char*)(addr)) +#define pgm_read_word(addr) \ + ({ \ + uint32_t tmp_addr = (uint32_t)(addr); \ + *(const uint16_t*)tmp_addr; \ + }) +#define pgm_read_dword(addr) \ + ({ \ + uint32_t tmp_addr = (uint32_t)(addr); \ + *(const uint32_t*)tmp_addr; \ + }) +#define pgm_read_float(addr) \ + ({ \ + uint32_t tmp_addr = (uint32_t)(addr); \ + *(const float*)tmp_addr; \ + }) /** @} */ diff --git a/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp b/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp index 43c6c19fcb..d77efdd403 100644 --- a/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp +++ b/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp @@ -8,13 +8,19 @@ * ****/ +#include #include #include -#include +#include +#include #include #include #include +#ifndef CONFIG_IDF_TARGET_ESP32C3 +#include +#endif + /* * Physical <-> Virtual address mapping is handled in `$IDF_COMPONENTS/spi_flash/flash_mmap.c`. * @@ -37,7 +43,11 @@ uint32_t flashmem_get_address(const void* memptr) } else { return 0; } +#if CONFIG_IDF_TARGET_ESP32 && !CONFIG_FREERTOS_UNICORE uint32_t entry = DPORT_SEQUENCE_REG_READ(uint32_t(&DPORT_APP_FLASH_MMU_TABLE[page])); +#else + uint32_t entry = DPORT_SEQUENCE_REG_READ(uint32_t(&SOC_MMU_DPORT_PRO_FLASH_MMU_TABLE[page])); +#endif uint32_t paddr = (entry * SPI_FLASH_MMU_PAGE_SIZE) + (vaddr & (SPI_FLASH_MMU_PAGE_SIZE - 1)); return (entry & 0x0100) ? 0 : paddr; } diff --git a/Sming/Arch/Esp32/Components/spi_flash/include/esp_spi_flash.h b/Sming/Arch/Esp32/Components/spi_flash/include/esp_spi_flash.h index aaa03d1370..6cf2883115 100644 --- a/Sming/Arch/Esp32/Components/spi_flash/include/esp_spi_flash.h +++ b/Sming/Arch/Esp32/Components/spi_flash/include/esp_spi_flash.h @@ -12,7 +12,7 @@ #pragma once #include_next -#include +#include #include #ifdef __cplusplus diff --git a/Sming/Arch/Esp32/Core/Digital.cpp b/Sming/Arch/Esp32/Core/Digital.cpp index 591c81b92d..6469e6707c 100644 --- a/Sming/Arch/Esp32/Core/Digital.cpp +++ b/Sming/Arch/Esp32/Core/Digital.cpp @@ -13,7 +13,9 @@ #define gpio_drive_cap_t uint32_t #include #include +#if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED #include +#endif void pinMode(uint16_t pin, uint8_t mode) { @@ -22,6 +24,8 @@ void pinMode(uint16_t pin, uint8_t mode) } auto gpio = gpio_num_t(pin); + +#if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED auto rtc = rtc_io_number_get(gpio); if(mode == ANALOG) { @@ -52,6 +56,7 @@ void pinMode(uint16_t pin, uint8_t mode) rtcio_ll_pullup_disable(rtc); } } +#endif gpio_set_level(gpio, 0); gpio_ll_input_enable(&GPIO, gpio); diff --git a/Sming/Arch/Esp32/Core/Interrupts.cpp b/Sming/Arch/Esp32/Core/Interrupts.cpp index 0e643251c3..b34d20d09d 100644 --- a/Sming/Arch/Esp32/Core/Interrupts.cpp +++ b/Sming/Arch/Esp32/Core/Interrupts.cpp @@ -16,6 +16,9 @@ #include #include +#define gpio_drive_cap_t uint32_t +#include + constexpr unsigned MAX_INTERRUPTS = 40; static intr_handle_t interruptHandle = nullptr; @@ -58,17 +61,19 @@ static void IRAM_ATTR interruptHandler(void* arg) int cpuId = esp_intr_get_cpu(interruptHandle); //read status to get interrupt status for GPIO0-31 - const uint32_t statusLow = (cpuId == 0) ? GPIO.pcpu_int : GPIO.acpu_int; + uint32_t statusLow; + gpio_ll_get_intr_status(&GPIO, cpuId, &statusLow); if(statusLow) { interruptProcessor(statusLow, 0); - GPIO.status_w1tc = statusLow; + gpio_ll_clear_intr_status(&GPIO, statusLow); } //read status1 to get interrupt status for GPIO32-39 - const uint32_t statusHigh = (cpuId == 0) ? GPIO.pcpu_int1.intr : GPIO.acpu_int1.intr; + uint32_t statusHigh; + gpio_ll_get_intr_status_high(&GPIO, cpuId, &statusHigh); if(statusHigh) { interruptProcessor(statusHigh, 32); - GPIO.status1_w1tc.intr_st = statusHigh; + gpio_ll_clear_intr_status_high(&GPIO, statusHigh); } } diff --git a/Sming/Arch/Esp32/Core/SPI.cpp b/Sming/Arch/Esp32/Core/SPI.cpp index 79ea43fe18..c79317fc82 100644 --- a/Sming/Arch/Esp32/Core/SPI.cpp +++ b/Sming/Arch/Esp32/Core/SPI.cpp @@ -16,10 +16,12 @@ #include "SPI.h" #include -#include -#include -#include -#include +#undef FLAG_ATTR +#define FLAG_ATTR(TYPE) +#define typeof decltype +#include +#include +#include #include // define the static singleton @@ -27,49 +29,14 @@ SPIClass SPI; using SpiDevice = volatile spi_dev_t; -struct SPIClass::BusInfo { - SpiDevice& dev; - const char* name; - SpiPins defpin; - struct { - uint32_t clk_en; - uint32_t rst; - } dport; - struct { - uint8_t sck; - uint8_t miso; - uint8_t mosi; - uint8_t ss[3]; - } pinIdx; - bool assigned; -}; - -SPIClass::BusInfo SPIClass::busInfo[]{ - { - SPI1, - "FSPI", - {6, 7, 8, SPI_PIN_DEFAULT}, - {DPORT_SPI01_CLK_EN, DPORT_SPI01_RST}, - {SPICLK_OUT_IDX, SPIQ_IN_IDX, SPID_OUT_IDX, {SPICS0_OUT_IDX, SPICS1_OUT_IDX, SPICS2_OUT_IDX}}, - }, - { - SPI2, - "HSPI", - {14, 12, 13, SPI_PIN_DEFAULT}, - {DPORT_SPI2_CLK_EN, DPORT_SPI2_RST}, - {HSPICLK_OUT_IDX, HSPIQ_IN_IDX, HSPID_OUT_IDX, {HSPICS0_OUT_IDX, HSPICS1_OUT_IDX, HSPICS2_OUT_IDX}}, - }, - { - SPI3, - "VSPI", - {18, 19, 23, SPI_PIN_DEFAULT}, - {DPORT_SPI3_CLK_EN, DPORT_SPI3_RST}, - {VSPICLK_OUT_IDX, VSPIQ_IN_IDX, VSPID_OUT_IDX, {VSPICS0_OUT_IDX, VSPICS1_OUT_IDX, VSPICS2_OUT_IDX}}, - }, -}; - namespace { +const SpiPins defaultPins[] = { + {6, 7, 8, SPI_PIN_DEFAULT}, + {14, 12, 13, SPI_PIN_DEFAULT}, + {18, 19, 23, SPI_PIN_DEFAULT}, +}; + // Used internally to calculate optimum SPI speed struct SpiPreDiv { unsigned freq; @@ -108,8 +75,7 @@ __forceinline void spi_send(SpiDevice& dev) */ void spi_mode(SpiDevice& dev, uint8_t mode) { - dev.pin.ck_idle_edge = (mode == SPI_MODE2 || mode == SPI_MODE3); - dev.user.ck_out_edge = (mode == SPI_MODE1 || mode == SPI_MODE2); + spi_ll_master_set_mode(&dev, mode); #ifdef SPI_DEBUG debug_i("[SPI] spi_mode(mode %x) cpha %X, cpol %X)", mode, mode & 0x0F, mode & 0xF0); @@ -130,8 +96,14 @@ void spi_byte_order(SpiDevice& dev, uint8_t byte_order) debug_i("[SPI] spi_byte_order(byte_order %u)", byte_order); #endif +// No HAL definition for this +#if SUBARCH_ESP32 || SUBARCH_ESP32S2 dev.user.rd_byte_order = (byte_order == MSBFIRST); dev.user.wr_byte_order = (byte_order == MSBFIRST); +#else +// No definition in datasheet for esp32-c3, perhaps it's just missing? +#warning "SPI byte order unsupported" +#endif } /** @@ -213,10 +185,10 @@ void checkSpeed(SPISpeed& speed, unsigned apbFreq) uint32_t getApbFrequency() { - constexpr uint32_t MHZ{1000000}; + constexpr uint32_t DIV_MHZ{1000000}; rtc_cpu_freq_config_t conf; rtc_clk_cpu_freq_get_config(&conf); - return (conf.freq_mhz >= 80) ? (80 * MHZ) : ((conf.source_freq_mhz * MHZ) / conf.div); + return (conf.freq_mhz >= 80) ? (80 * DIV_MHZ) : ((conf.source_freq_mhz * DIV_MHZ) / conf.div); } void spi_set_clock(SpiDevice& dev, SPISpeed& speed) @@ -233,6 +205,27 @@ void spi_set_clock(SpiDevice& dev, SPISpeed& speed) dev.clock.val = speed.regVal; } +const spi_signal_conn_t& getBusInfo(SpiBus busId) +{ + return spi_periph_signal[unsigned(busId) - 1]; +} + +SpiDevice& getDevice(SpiBus busId) +{ + return *getBusInfo(busId).hw; +} + +struct BusState { + bool assigned : 1; +}; + +BusState busState[SOC_SPI_PERIPH_NUM]; + +BusState& getBusState(SpiBus busId) +{ + return busState[unsigned(busId) - 1]; +} + } // namespace bool SPIClass::setup(SpiBus busId, SpiPins pins) @@ -242,8 +235,7 @@ bool SPIClass::setup(SpiBus busId, SpiPins pins) return false; } - auto& bus = busInfo[unsigned(busId) - 1]; - if(bus.assigned) { + if(getBusState(busId).assigned) { debug_e("[SPI] Bus #%u already assigned", busId); return false; } @@ -253,11 +245,6 @@ bool SPIClass::setup(SpiBus busId, SpiPins pins) return true; } -SPIClass::BusInfo& SPIClass::getBusInfo() -{ - return busInfo[unsigned(busId) - 1]; -} - bool SPIClass::begin() { if(busId < SpiBus::MIN || busId > SpiBus::MAX) { @@ -265,59 +252,51 @@ bool SPIClass::begin() return false; } - auto& bus = getBusInfo(); - - if(bus.assigned) { + auto& state = getBusState(busId); + if(state.assigned) { debug_e("[SPI] Bus #%u already assigned", busId); return false; } - DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, bus.dport.clk_en); - DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, bus.dport.rst); + auto& bus = getBusInfo(busId); + periph_ll_enable_clk_clear_rst(bus.module); - auto& dev = bus.dev; + auto& dev = getDevice(busId); // Initialise bus - dev.slave.trans_done = 0; - dev.slave.slave_mode = 0; - dev.pin.val = 0; - dev.user.val = 0; - dev.user1.val = 0; - dev.ctrl.val = 0; - dev.ctrl1.val = 0; - dev.ctrl2.val = 0; - dev.clock.val = 0; + spi_ll_master_init(&dev); + spi_ll_clear_int_stat(&dev); + // - dev.user.usr_mosi = true; - dev.user.usr_miso = true; - dev.user.doutdin = true; - dev.user.ck_i_edge = true; + spi_ll_enable_mosi(&dev, true); + spi_ll_enable_miso(&dev, true); + spi_ll_set_half_duplex(&dev, false); // Not using any auto. chip selects - dev.pin.cs0_dis = true; - dev.pin.cs1_dis = true; - dev.pin.cs2_dis = true; + spi_ll_master_select_cs(&dev, -1); + + auto& defPins = defaultPins[unsigned(busId) - 1]; // Clock pin if(pins.sck == SPI_PIN_DEFAULT) { - pins.sck = bus.defpin.sck; + pins.sck = defPins.sck; } pinMode(pins.sck, OUTPUT); - gpio_matrix_out(pins.sck, bus.pinIdx.sck, false, false); + gpio_matrix_out(pins.sck, bus.spiclk_out, false, false); // MISO if(pins.miso == SPI_PIN_DEFAULT) { - pins.miso = bus.defpin.miso; + pins.miso = defPins.miso; } pinMode(pins.miso, INPUT); - gpio_matrix_in(pins.miso, bus.pinIdx.miso, false); + gpio_matrix_in(pins.miso, bus.spiq_in, false); // MOSI if(pins.mosi == SPI_PIN_DEFAULT) { - pins.mosi = bus.defpin.mosi; + pins.mosi = defPins.mosi; } pinMode(pins.mosi, OUTPUT); - gpio_matrix_out(pins.mosi, bus.pinIdx.mosi, false, false); + gpio_matrix_out(pins.mosi, bus.spid_out, false, false); debug_i("[SPI] SCK = %u, MISO = %u, MOSI = %u", pins.sck, pins.miso, pins.mosi); @@ -339,7 +318,7 @@ bool SPIClass::begin() checkSpeed(SPIDefaultSettings.speed, getApbFrequency()); prepare(SPIDefaultSettings); - bus.assigned = true; + state.assigned = true; return true; } @@ -349,27 +328,33 @@ void SPIClass::end() return; } - auto& bus = getBusInfo(); - if(!bus.assigned) { + auto& state = getBusState(busId); + if(!state.assigned) { return; } - // TODO + auto& info = getBusInfo(busId); + periph_ll_disable_clk_set_rst(info.module); + + state.assigned = false; } uint32_t SPIClass::transfer32(uint32_t data, uint8_t bits) { - auto& dev = getBusInfo().dev; + auto& dev = getDevice(busId); spi_wait(dev); - dev.mosi_dlen.usr_mosi_dbitlen = bits - 1; - dev.miso_dlen.usr_miso_dbitlen = bits - 1; + spi_ll_set_mosi_bitlen(&dev, bits); + spi_ll_set_miso_bitlen(&dev, bits); // copy data to W0 +#if SUBARCH_ESP32 || SUBARCH_ESP32S2 if(dev.user.wr_byte_order) { dev.data_buf[0] = data << (32 - bits); - } else { + } else +#endif + { dev.data_buf[0] = data; } @@ -377,15 +362,17 @@ uint32_t SPIClass::transfer32(uint32_t data, uint8_t bits) spi_wait(dev); auto res = dev.data_buf[0]; +#if SUBARCH_ESP32 || SUBARCH_ESP32S2 if(dev.user.rd_byte_order) { res >>= (32 - bits); } +#endif return res; } uint8_t SPIClass::read8() { - auto& dev = getBusInfo().dev; + auto& dev = getDevice(busId); spi_wait(dev); @@ -395,17 +382,19 @@ uint8_t SPIClass::read8() spi_wait(dev); auto res = dev.data_buf[0]; +#if SUBARCH_ESP32 || SUBARCH_ESP32S2 if(dev.user.rd_byte_order) { res >>= 24; } +#endif return res; } void SPIClass::transfer(uint8_t* buffer, size_t numberBytes) { - constexpr uint32_t BLOCKSIZE{64}; // the max length of the ESP SPI_W0 registers + constexpr size_t BLOCKSIZE{64}; // the max length of the ESP SPI_W0 registers - auto& dev = getBusInfo().dev; + auto& dev = getDevice(busId); unsigned bufIndx = 0; @@ -427,8 +416,8 @@ void SPIClass::transfer(uint8_t* buffer, size_t numberBytes) // setup bit length auto num_bits = bufLength * 8; - dev.mosi_dlen.usr_mosi_dbitlen = num_bits - 1; - dev.miso_dlen.usr_miso_dbitlen = num_bits - 1; + spi_ll_set_mosi_bitlen(&dev, num_bits); + spi_ll_set_miso_bitlen(&dev, num_bits); // copy the registers starting from last index position if(IS_ALIGNED(buffer)) { @@ -456,7 +445,7 @@ void SPIClass::prepare(SPISettings& settings) settings.print("settings"); #endif - auto& dev = getBusInfo().dev; + auto& dev = getDevice(busId); spi_set_clock(dev, settings.speed); spi_byte_order(dev, settings.byteOrder); diff --git a/Sming/Arch/Esp32/Core/SPI.h b/Sming/Arch/Esp32/Core/SPI.h index 57dae526e7..ee83fd9ceb 100644 --- a/Sming/Arch/Esp32/Core/SPI.h +++ b/Sming/Arch/Esp32/Core/SPI.h @@ -20,6 +20,7 @@ #include "SPIBase.h" #include "SPISettings.h" +#include //#define SPI_DEBUG 1 @@ -40,10 +41,20 @@ static constexpr uint8_t SPI_PIN_DEFAULT{0xff}; enum class SpiBus { INVALID = 0, MIN = 1, + SPI1 = 1, FSPI = 1, // Attached to the flash (can use the same data lines but different SS) + SPI2 = 2, HSPI = 2, // Normally mapped to pins 12 - 15, but can be matrixed to any pins +#if SOC_SPI_PERIPH_NUM > 2 + SPI3 = 3, VSPI = 3, // Normally attached to pins 5, 18, 19 and 23, but can be matrixed to any pins - MAX = 3, +#endif + MAX = SOC_SPI_PERIPH_NUM, +#ifdef SUBARCH_ESP32C3 + DEFAULT = SPI1, +#else + DEFAULT = VSPI, +#endif }; /** @@ -59,7 +70,10 @@ struct SpiPins { class SPIClass : public SPIBase { public: - SPIClass(SpiBus id = SpiBus::VSPI) : busId(id) + SPIClass(const SPIClass&) = delete; + SPIClass& operator=(const SPIClass&) = delete; + + SPIClass(SpiBus id = SpiBus::DEFAULT) : busId(id) { } @@ -86,11 +100,6 @@ class SPIClass : public SPIBase protected: void prepare(SPISettings& settings) override; - struct BusInfo; - static BusInfo busInfo[]; - - BusInfo& getBusInfo(); - SpiBus busId; SpiPins pins; }; diff --git a/Sming/Arch/Esp32/Core/adc.cpp b/Sming/Arch/Esp32/Core/adc.cpp index d395d7757d..6e81de448a 100644 --- a/Sming/Arch/Esp32/Core/adc.cpp +++ b/Sming/Arch/Esp32/Core/adc.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -42,21 +43,23 @@ uint16_t analogRead(uint16_t pin) constexpr adc_atten_t attenuation{ADC_ATTEN_DB_0}; esp_adc_cal_characteristics_t adcChars{}; + auto adcWidth = adc_bits_width_t(ADC_WIDTH_MAX - 1); + // Configure if(info.adc == ADC_UNIT_2) { adc2_config_channel_atten(adc2_channel_t(info.channel), attenuation); } else { - adc1_config_width(ADC_WIDTH_BIT_12); + adc1_config_width(adcWidth); adc1_config_channel_atten(adc1_channel_t(info.channel), attenuation); } // Characterize - esp_adc_cal_characterize(info.adc, attenuation, ADC_WIDTH_BIT_12, DEFAULT_VREF, &adcChars); + esp_adc_cal_characterize(info.adc, attenuation, adcWidth, DEFAULT_VREF, &adcChars); // Sample if(info.adc == ADC_UNIT_2) { int value{0}; - esp_err_t r = adc2_get_raw(adc2_channel_t(info.channel), ADC_WIDTH_BIT_12, &value); + esp_err_t r = adc2_get_raw(adc2_channel_t(info.channel), adcWidth, &value); if(r == ESP_OK) { return value; } else if(r == ESP_ERR_INVALID_STATE) { diff --git a/Sming/Arch/Esp32/Platform/RTC.cpp b/Sming/Arch/Esp32/Platform/RTC.cpp index 7cb159957a..b5b721f806 100644 --- a/Sming/Arch/Esp32/Platform/RTC.cpp +++ b/Sming/Arch/Esp32/Platform/RTC.cpp @@ -9,8 +9,9 @@ ****/ #include -#include -#include + +// #include +extern "C" uint64_t esp_clk_rtc_time(void); RtcClass RTC; diff --git a/Sming/Arch/Esp32/README.rst b/Sming/Arch/Esp32/README.rst index 5c249ba8a9..abcc82a6a2 100644 --- a/Sming/Arch/Esp32/README.rst +++ b/Sming/Arch/Esp32/README.rst @@ -14,10 +14,10 @@ Build variables Requirements ------------ -In order to be able to compile for the ESP32 architecture you should have ESP-IDF v4.1 installed. +In order to be able to compile for the ESP32 architecture you should have ESP-IDF v4.3 installed. The Sming installers can do this for you - see :doc:`/getting-started/index`. -You can find further details in the `ESP-IDF documentation `__. +You can find further details in the `ESP-IDF documentation `__. Building -------- @@ -63,6 +63,29 @@ If you want to revert to using the default pre-compiled SDK then issue the follo See :component-esp32:`esp32` for further details. + +Processor variants +------------------ + +Sming leverages the `ESP IDF HAL `__ +to support multiple processor variants. + +This is still at an early stage of development however basic applications should build for the following variants: + +- esp32 (default) +- esp32s2 +- esp32c3 +- esp32s3 + +If changing variant the project must be cleaned first. You can change variants like this: + +``` +make SMING_ARCH=Esp32 clean components-clean +make ESP_VARIANT=esp32c3 +``` + + + Components ---------- diff --git a/Sming/Arch/Esp32/Tools/ci/build.run.sh b/Sming/Arch/Esp32/Tools/ci/build.run.sh index 01209dfe4c..1d1aa4aa39 100755 --- a/Sming/Arch/Esp32/Tools/ci/build.run.sh +++ b/Sming/Arch/Esp32/Tools/ci/build.run.sh @@ -2,3 +2,15 @@ $MAKE_PARALLEL Basic_Blink Basic_WiFi HttpServer_ConfigNetwork DEBUG_VERBOSE_LEVEL=3 STRICT=1 $MAKE_PARALLEL Basic_Ssl ENABLE_SSL=Bearssl DEBUG_VERBOSE_LEVEL=3 STRICT=1 + +# esp32s2 +make clean components-clean +$MAKE_PARALLEL Basic_Blink ESP_VARIANT=esp32s2 + +# esp32c3 +make clean components-clean +$MAKE_PARALLEL Basic_Blink ESP_VARIANT=esp32c3 + +# esp32s3 +make clean components-clean +$MAKE_PARALLEL Basic_Blink ESP_VARIANT=esp32s3 diff --git a/Sming/Arch/Esp32/Tools/ci/install.cmd b/Sming/Arch/Esp32/Tools/ci/install.cmd index 7007813206..6c633c55da 100644 --- a/Sming/Arch/Esp32/Tools/ci/install.cmd +++ b/Sming/Arch/Esp32/Tools/ci/install.cmd @@ -3,13 +3,8 @@ REM Esp32 install.cmd if "%IDF_PATH%"=="" goto :EOF if "%IDF_TOOLS_PATH%"=="" goto :EOF -git clone -b release/v4.1 https://github.com/espressif/esp-idf.git %IDF_PATH% - -REM Espressif downloads very slow, fetch from SmingTools -mkdir %IDF_TOOLS_PATH% -set ESPTOOLS=esp32-tools-windows-4.1.7z -curl -LO %SMINGTOOLS%/%ESPTOOLS% -7z -o%IDF_TOOLS_PATH%\dist x %ESPTOOLS% +git clone -b sming/release/v4.3 https://github.com/mikee47/esp-idf.git %IDF_PATH% +REM Install IDF tools and packages python %IDF_PATH%\tools\idf_tools.py install python -m pip install -r %IDF_PATH%\requirements.txt diff --git a/Sming/Arch/Esp32/Tools/install.sh b/Sming/Arch/Esp32/Tools/install.sh old mode 100755 new mode 100644 index 6639c15ffc..a617db2482 --- a/Sming/Arch/Esp32/Tools/install.sh +++ b/Sming/Arch/Esp32/Tools/install.sh @@ -28,23 +28,30 @@ esac $PKG_INSTALL ${PACKAGES[*]} -if [ -d "$IDF_PATH" ]; then - printf "\n\n** Skipping ESP-IDF clone: '$IDF_PATH' exists\n\n" +# If directory exists and isn't a symlink then rename it +if [ ! -L "$IDF_PATH" ] && [ -d "$IDF_PATH" ]; then + echo MOVING OLD + mv "$IDF_PATH" "$IDF_PATH-old" +fi + +IDF_CLONE_PATH="$(readlink -m "$IDF_PATH/..")/esp-idf-4.3" + +if [ -d "$IDF_CLONE_PATH" ]; then + printf "\n\n** Skipping ESP-IDF clone: '$IDF_CLONE_PATH' exists\n\n" else - git clone -b release/v4.1 https://github.com/espressif/esp-idf.git $IDF_PATH + git clone -b sming/release/v4.3 https://github.com/mikee47/esp-idf.git "$IDF_CLONE_PATH" fi -# Espressif downloads very slow, fetch from SmingTools -mkdir -p $IDF_TOOLS_PATH -ESPTOOLS=esp32-tools-linux-4.1.zip -$WGET $SMINGTOOLS/$ESPTOOLS -O $DOWNLOADS/$ESPTOOLS -unzip $DOWNLOADS/$ESPTOOLS -d $IDF_TOOLS_PATH/dist +# Create link to clone +rm -f "$IDF_PATH" +ln -s "$IDF_CLONE_PATH" "$IDF_PATH" -python3 $IDF_PATH/tools/idf_tools.py install -python3 -m pip install -r $IDF_PATH/requirements.txt +# Install IDF tools and packages +python3 "$IDF_PATH/tools/idf_tools.py" install +python3 -m pip install -r "$IDF_PATH/requirements.txt" if [ -z "$KEEP_DOWNLOADS" ]; then - rm -rf $IDF_TOOLS_PATH/dist + rm -rf "$IDF_TOOLS_PATH/dist" fi fi diff --git a/Sming/Arch/Esp32/Tools/memanalyzer.py b/Sming/Arch/Esp32/Tools/memanalyzer.py index 0117317b71..04d052f2e5 100644 --- a/Sming/Arch/Esp32/Tools/memanalyzer.py +++ b/Sming/Arch/Esp32/Tools/memanalyzer.py @@ -12,18 +12,38 @@ import subprocess import sys -# From soc/soc.h -SOC_DROM_LOW = 0x3F400000 -SOC_DROM_HIGH = 0x3F800000 -SOC_DRAM_LOW = 0x3FFAE000 -SOC_DRAM_HIGH = 0x40000000 -SOC_IROM_LOW = 0x400D0000 -SOC_IROM_HIGH = 0x40400000 -SOC_IRAM_LOW = 0x40080000 -SOC_IRAM_HIGH = 0x400A0000 - -TOTAL_DRAM = SOC_DRAM_HIGH - SOC_DRAM_LOW; -TOTAL_IRAM = SOC_IRAM_HIGH - SOC_IRAM_LOW; +# esptool defines a load of useful stuff +sys.path.insert(1, os.path.expandvars('${SMING_HOME}/Components/esptool/esptool')) +import esptool + +# Get expected memory sizes from loader tables +loader = esptool._chip_to_rom_loader(os.environ['ESP_VARIANT']) +TOTAL_DRAM = sum(end - start for (start, end, n) in loader.MEMORY_MAP if n in ['DRAM']) +TOTAL_IRAM = sum(end - start for (start, end, n) in loader.MEMORY_MAP if n in ['IRAM']) + +# TESTING +def printMemoryMaps(): + loaders = OrderedDict({ + 'esp8266': esptool.ESP8266ROM, + 'esp32': esptool.ESP32ROM, + 'esp32s2': esptool.ESP32S2ROM, + 'esp32s3beta2': esptool.ESP32S3BETA2ROM, + 'esp32s3': esptool.ESP32S3ROM, + 'esp32c3': esptool.ESP32C3ROM, + 'esp32c6beta': esptool.ESP32C6BETAROM, + 'esp32h2': esptool.ESP32H2ROM + }) + for k, v in loaders.items(): + print(k) + for (start, end, n) in v.MEMORY_MAP: + print(" %16s: %8u (0x%08x - 0x%08x)" % (n, end - start, start, end)) + dram = sum(end - start for (start, end, n) in v.MEMORY_MAP if n in ['DRAM']) + iram = sum(end - start for (start, end, n) in v.MEMORY_MAP if n in ['IRAM']) + print(" %16s: %8u" % ('TOTAL_DRAM', dram)) + print(" %16s: %8u" % ('TOTAL_IRAM', iram)) + print(" %16s: %8u" % ('DRAM+IRAM', dram + iram)) + +# printMemoryMaps() sections = OrderedDict([ ("data", "Initialized Data (RAM)"), diff --git a/Sming/Arch/Esp32/app.mk b/Sming/Arch/Esp32/app.mk index a4f06d5efb..6f4dc23d03 100644 --- a/Sming/Arch/Esp32/app.mk +++ b/Sming/Arch/Esp32/app.mk @@ -13,14 +13,9 @@ LDFLAGS += \ .PHONY: application application: $(TARGET_BIN) -# $1 -> Linker script -define LinkTarget - $(info $(notdir $(PROJECT_DIR)): Linking $@) - $(Q) $(LD) $(addprefix -L,$(LIBDIRS)) -T$1 $(LDFLAGS) -Wl,--start-group $(COMPONENTS_AR) $(addprefix -l,$(LIBS)) -Wl,--end-group -o $@ -endef - $(TARGET_OUT): $(COMPONENTS_AR) - $(call LinkTarget,standalone.rom.ld) + $(info $(notdir $(PROJECT_DIR)): Linking $@) + $(Q) $(LD) $(addprefix -L,$(LIBDIRS)) $(LDFLAGS) -Wl,--start-group $(COMPONENTS_AR) $(addprefix -l,$(LIBS)) -Wl,--end-group -o $@ $(Q) $(MEMANALYZER) $@ > $(FW_MEMINFO_NEW) diff --git a/Sming/Arch/Esp32/build.mk b/Sming/Arch/Esp32/build.mk index c3b9ef53ae..553911d981 100644 --- a/Sming/Arch/Esp32/build.mk +++ b/Sming/Arch/Esp32/build.mk @@ -26,7 +26,14 @@ ifndef ESP_VARIANT ESP_VARIANT := esp32 endif +export ESP_VARIANT + +ifeq ($(ESP_VARIANT),esp32c3) +ESP32_COMPILER_PREFIX := riscv32-esp-elf +IDF_TARGET_ARCH_RISCV := 1 +else ESP32_COMPILER_PREFIX := xtensa-$(ESP_VARIANT)-elf +endif # $1 => Root directory # $2 => Sub-directory @@ -37,7 +44,8 @@ endef DEBUG_VARS += ESP32_COMPILER_PATH ESP32_ULP_PATH ESP32_OPENOCD_PATH ESP32_PYTHON_PATH ifndef ESP32_COMPILER_PATH -ESP32_COMPILER_PATH := $(call FindTool,tools/$(ESP32_COMPILER_PREFIX),/$(ESP32_COMPILER_PREFIX)) +include $(IDF_PATH)/tools/toolchain_versions.mk +ESP32_COMPILER_PATH := $(IDF_TOOLS_PATH)/tools/$(ESP32_COMPILER_PREFIX)/$(CURRENT_TOOLCHAIN_COMMIT_DESC)-$(CURRENT_TOOLCHAIN_GCC_VERSION)/$(ESP32_COMPILER_PREFIX) endif ifndef ESP32_ULP_PATH @@ -150,7 +158,12 @@ SMING_C_STD := gnu99 # This variable stores the common C/C++ flags # CPPFLAGS used by C preprocessor # If any flags are defined in application Makefile, add them at the end. -CPPFLAGS += -DESP_PLATFORM -D IDF_VER=\"$(IDF_VER)\" -MMD -MP $(EXTRA_CPPFLAGS) +CPPFLAGS += \ + -DESP_PLATFORM \ + -D IDF_VER=\"$(IDF_VER)\" \ + -MMD \ + -MP \ + $(EXTRA_CPPFLAGS) # Sming specific CPPFLAGS CPPFLAGS += \ @@ -205,10 +218,16 @@ COMMON_FLAGS := \ -Wno-frame-address \ -ffunction-sections -fdata-sections \ -fstrict-volatile-bitfields \ - -mlongcalls \ - -mtext-section-literals \ -nostdlib +ifdef IDF_TARGET_ARCH_RISCV +COMMON_FLAGS += -DIDF_TARGET_ARCH_RISCV=1 +else +COMMON_FLAGS += \ + -mlongcalls \ + -mtext-section-literals +endif + ifndef IS_BOOTLOADER_BUILD # stack protection (only one option can be selected in menuconfig) ifdef CONFIG_COMPILER_STACK_CHECK_MODE_NORM diff --git a/Sming/Arch/Esp8266/Core/SPI.h b/Sming/Arch/Esp8266/Core/SPI.h index 402e290b98..e6ad930196 100644 --- a/Sming/Arch/Esp8266/Core/SPI.h +++ b/Sming/Arch/Esp8266/Core/SPI.h @@ -36,6 +36,13 @@ class SPIClass : public SPIBase { public: + SPIClass() + { + } + + SPIClass(const SPIClass&) = delete; + SPIClass& operator=(const SPIClass&) = delete; + bool begin() override; void end() override diff --git a/Sming/Arch/Esp8266/Tools/ci/build.run.cmd b/Sming/Arch/Esp8266/Tools/ci/build.run.cmd index 830f5d6c46..e0c217d5f0 100644 --- a/Sming/Arch/Esp8266/Tools/ci/build.run.cmd +++ b/Sming/Arch/Esp8266/Tools/ci/build.run.cmd @@ -1,7 +1,7 @@ REM Esp8266 build.run.cmd make -C "%SMING_PROJECTS_DIR%\samples\HttpServer_FirmwareUpload" python-requirements -%MAKE_PARALLEL% samples || goto :error +REM %MAKE_PARALLEL% samples || goto :error make clean samples-clean %MAKE_PARALLEL% Basic_Blink ENABLE_CUSTOM_HEAP=1 DEBUG_VERBOSE_LEVEL=3 || goto :error diff --git a/Sming/Arch/Host/Core/SPI.h b/Sming/Arch/Host/Core/SPI.h index f1c6287d9c..3097d03df8 100644 --- a/Sming/Arch/Host/Core/SPI.h +++ b/Sming/Arch/Host/Core/SPI.h @@ -30,6 +30,13 @@ class SPIClass : public SPIBase { public: + SPIClass() + { + } + + SPIClass(const SPIClass&) = delete; + SPIClass& operator=(const SPIClass&) = delete; + bool begin() override; void end() override diff --git a/Sming/Components/IFS b/Sming/Components/IFS index 8792db1144..c7365f6b08 160000 --- a/Sming/Components/IFS +++ b/Sming/Components/IFS @@ -1 +1 @@ -Subproject commit 8792db1144588a4732e44a561810730a37b8a48a +Subproject commit c7365f6b082c3c9b0da1be1940a9ec80e5960614 diff --git a/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.cpp b/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.cpp index a081708842..24f858e6b6 100644 --- a/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.cpp +++ b/Sming/Components/Network/Arch/Esp32/Platform/AccessPointImpl.cpp @@ -88,7 +88,7 @@ bool AccessPointImpl::config(const String& ssid, String password, WifiAuthMode m enable(true, false); - ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &config)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &config)); ESP_ERROR_CHECK(esp_wifi_start()); return true; @@ -152,19 +152,19 @@ bool AccessPointImpl::setIP(IpAddress address) MacAddress AccessPointImpl::getMacAddress() const { MacAddress addr; - ESP_ERROR_CHECK(esp_wifi_get_mac(ESP_IF_WIFI_AP, &addr[0])); + ESP_ERROR_CHECK(esp_wifi_get_mac(WIFI_IF_AP, &addr[0])); return addr; } bool AccessPointImpl::setMacAddress(const MacAddress& addr) const { - return esp_wifi_set_mac(ESP_IF_WIFI_AP, &const_cast(addr)[0]); + return esp_wifi_set_mac(WIFI_IF_AP, &const_cast(addr)[0]); } String AccessPointImpl::getSSID() const { wifi_config_t config{}; - if(esp_wifi_get_config(ESP_IF_WIFI_AP, &config) != ESP_OK) { + if(esp_wifi_get_config(WIFI_IF_AP, &config) != ESP_OK) { debug_w("Can't read station configuration!"); return nullptr; } @@ -176,7 +176,7 @@ String AccessPointImpl::getSSID() const String AccessPointImpl::getPassword() const { wifi_config_t config{}; - if(esp_wifi_get_config(ESP_IF_WIFI_AP, &config) != ESP_OK) { + if(esp_wifi_get_config(WIFI_IF_AP, &config) != ESP_OK) { debug_w("Can't read station configuration!"); return nullptr; } diff --git a/Sming/Components/Network/Arch/Esp32/Platform/StationImpl.cpp b/Sming/Components/Network/Arch/Esp32/Platform/StationImpl.cpp index fed72f220d..afc79494bc 100644 --- a/Sming/Components/Network/Arch/Esp32/Platform/StationImpl.cpp +++ b/Sming/Components/Network/Arch/Esp32/Platform/StationImpl.cpp @@ -115,7 +115,7 @@ bool StationImpl::config(const String& ssid, const String& password, bool autoCo enable(true, save); ESP_ERROR_CHECK(esp_wifi_set_storage(save ? WIFI_STORAGE_FLASH : WIFI_STORAGE_RAM)); - ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &config)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &config)); return connect(); } @@ -182,13 +182,13 @@ IpAddress StationImpl::getIP() const MacAddress StationImpl::getMacAddress() const { MacAddress addr; - ESP_ERROR_CHECK(esp_wifi_get_mac(ESP_IF_WIFI_STA, &addr[0])); + ESP_ERROR_CHECK(esp_wifi_get_mac(WIFI_IF_STA, &addr[0])); return addr; } bool StationImpl::setMacAddress(const MacAddress& addr) const { - return esp_wifi_set_mac(ESP_IF_WIFI_STA, &const_cast(addr)[0]); + return esp_wifi_set_mac(WIFI_IF_STA, &const_cast(addr)[0]); } IpAddress StationImpl::getNetworkBroadcast() const @@ -248,7 +248,7 @@ bool StationImpl::setIP(IpAddress address, IpAddress netmask, IpAddress gateway) String StationImpl::getSSID() const { wifi_config_t config{}; - if(esp_wifi_get_config(ESP_IF_WIFI_STA, &config) != ESP_OK) { + if(esp_wifi_get_config(WIFI_IF_STA, &config) != ESP_OK) { debug_e("Can't read station configuration!"); return nullptr; } @@ -268,7 +268,7 @@ int8_t StationImpl::getRssi() const uint8_t StationImpl::getChannel() const { wifi_config_t config; - if(esp_wifi_get_config(ESP_IF_WIFI_STA, &config) != ESP_OK) { + if(esp_wifi_get_config(WIFI_IF_STA, &config) != ESP_OK) { debug_e("Can't read station configuration!"); return 0; } @@ -279,7 +279,7 @@ uint8_t StationImpl::getChannel() const String StationImpl::getPassword() const { wifi_config_t config{}; - if(esp_wifi_get_config(ESP_IF_WIFI_STA, &config) != ESP_OK) { + if(esp_wifi_get_config(WIFI_IF_STA, &config) != ESP_OK) { debug_e("Can't read station configuration!"); return nullptr; } diff --git a/Sming/Components/Network/Arch/Esp32/Platform/StationListImpl.h b/Sming/Components/Network/Arch/Esp32/Platform/StationListImpl.h index f2c8de23fd..6134ba6d00 100644 --- a/Sming/Components/Network/Arch/Esp32/Platform/StationListImpl.h +++ b/Sming/Components/Network/Arch/Esp32/Platform/StationListImpl.h @@ -23,7 +23,7 @@ class StationListImpl : public StationList debug_w("Can't get list of connected stations"); return; } - for(unsigned i = 0; i < list.num; i++) { + for(int i = 0; i < list.num; i++) { add(new Info{list.sta[i]}); } } diff --git a/Sming/Components/Storage/src/PartitionStream.cpp b/Sming/Components/Storage/src/PartitionStream.cpp index d2cea3cec6..d89f43df1f 100644 --- a/Sming/Components/Storage/src/PartitionStream.cpp +++ b/Sming/Components/Storage/src/PartitionStream.cpp @@ -45,7 +45,7 @@ int PartitionStream::seekFrom(int offset, SeekOrigin origin) size_t PartitionStream::write(const uint8_t* data, size_t length) { - auto len = std::min(size - writePos, length); + auto len = std::min(size_t(size - writePos), length); if(len != 0) { if(!partition.write(startOffset + writePos, data, len)) { len = 0; diff --git a/Sming/Components/esptool/esptool b/Sming/Components/esptool/esptool index 4fa0bd7b0d..c04d0f42db 160000 --- a/Sming/Components/esptool/esptool +++ b/Sming/Components/esptool/esptool @@ -1 +1 @@ -Subproject commit 4fa0bd7b0d1f69f5ff22b043adc07c5e562a8931 +Subproject commit c04d0f42db9e85b73171a2030bc2e9842ec476ac diff --git a/Sming/Core/Data/Stream/SectionStream.cpp b/Sming/Core/Data/Stream/SectionStream.cpp index cb3951056b..7eef06378f 100644 --- a/Sming/Core/Data/Stream/SectionStream.cpp +++ b/Sming/Core/Data/Stream/SectionStream.cpp @@ -145,7 +145,7 @@ uint16_t SectionStream::readMemoryBlock(char* data, int bufSize) } } - bufSize = std::min(size_t(bufSize), sect->size - sectionOffset); + bufSize = std::min(uint32_t(bufSize), sect->size - sectionOffset); return stream->readMemoryBlock(data, bufSize); } diff --git a/Sming/Core/Timer.h b/Sming/Core/Timer.h index f3080b6cfc..e22c9581e4 100644 --- a/Sming/Core/Timer.h +++ b/Sming/Core/Timer.h @@ -50,12 +50,9 @@ template class OsTimer64Api : public CallbackTimerApi(arg); - self->longTick(); - }, - this); +#ifndef ARCH_ESP32 + initCallback(); +#endif } ~OsTimer64Api() @@ -102,6 +99,9 @@ template class OsTimer64Api : public CallbackTimerApirepeating = repeating; osTimer.arm(longIntervalCounterLimit || repeating); } @@ -127,6 +127,16 @@ template class OsTimer64Api : public CallbackTimerApi(arg); + self->longTick(); + }, + this); + } }; template void OsTimer64Api::setInterval(TickType interval) diff --git a/Sming/System/include/c_types.h b/Sming/System/include/c_types.h index 4ad07da6d7..b5d2b1535e 100644 --- a/Sming/System/include/c_types.h +++ b/Sming/System/include/c_types.h @@ -15,7 +15,7 @@ typedef uint16_t u16; typedef int16_t sint16; typedef int16_t s16; typedef uint32_t uint32; -typedef uint32_t u_int; +// typedef uint32_t u_int; typedef uint32_t u32; typedef int32_t sint32; typedef int32_t s32; diff --git a/Sming/build.mk b/Sming/build.mk index 9ad74cbaec..151f1411b2 100644 --- a/Sming/build.mk +++ b/Sming/build.mk @@ -226,6 +226,11 @@ endif # Component (user) libraries have a special prefix so linker script can identify them CLIB_PREFIX := clib- +# Convert string to upper/lower case +# 1 -> String +ToUpper = $(shell echo "$1" | tr 'a-z' 'A-Z') +ToLower = $(shell echo "$1" | tr 'A-Z' 'a-z') + # Apply coding style to list of files using clang-format # $1 -> List of files define ClangFormat diff --git a/Sming/project.mk b/Sming/project.mk index ae6f973e0e..2da18baf55 100644 --- a/Sming/project.mk +++ b/Sming/project.mk @@ -59,7 +59,7 @@ export PROJECT_DIR ifeq ($(MAKELEVEL),0) $(info ) -$(info $(notdir $(PROJECT_DIR)): Invoking '$(MAKECMDGOALS)' for $(SMING_ARCH) ($(BUILD_TYPE)) architecture) +$(info $(notdir $(PROJECT_DIR)): Invoking '$(MAKECMDGOALS)' for $(SMING_ARCH).$(ESP_VARIANT) ($(BUILD_TYPE)) architecture) endif # CFLAGS used for application and any custom targets @@ -75,6 +75,7 @@ DEBUG_VARS += GLOBAL_CFLAGS GLOBAL_CFLAGS = \ -DSMING_ARCH=$(SMING_ARCH) \ -DESP_VARIANT=$(ESP_VARIANT) \ + -DSUBARCH_$(call ToUpper,$(ESP_VARIANT))=1 \ -DPROJECT_DIR=\"$(PROJECT_DIR)\" \ -DSMING_HOME=\"$(SMING_HOME)\" \ $(USER_CFLAGS) diff --git a/docs/source/about.rst b/docs/source/about.rst index 06840557e3..e544b47270 100644 --- a/docs/source/about.rst +++ b/docs/source/about.rst @@ -29,8 +29,11 @@ Sming provides access to all ESP8266 functions such as GPIO, timers, WiFi config ESP32 ----- -`ESP32 `__ is the second microcontroller by `Espressif `__ Systems. -At the moment Sming provides partial support for ESP32 functions. +`ESP32 `__ is the second microcontroller by `Espressif `__ Systems. +There are also a number of more recent variants such as the esp32-s2 and esp32-c3. + +Sming currently provides support for these devices but is more limited and should be considered 'experimental'. + Licenses -------- diff --git a/docs/source/upgrading/4.3-4.4.rst b/docs/source/upgrading/4.3-4.4.rst index db0dd88adf..6a0d1912f8 100644 --- a/docs/source/upgrading/4.3-4.4.rst +++ b/docs/source/upgrading/4.3-4.4.rst @@ -17,3 +17,9 @@ Some support code has been moved into ``Core/Data/WebHelpers``: applications sho a compiler warning advising of the changes. Note that ``Network/WebHelpers/aw-sha1.h`` has been removed in favour of the :component:`crypto` library. + +ESP32 +~~~~~ + +If you have made use of Sming's experimental ESP32 support then you will need to update the ESP IDF SDK and tools to version 4.3. +See :doc:`/_inc/Sming/Arch/Esp32/README`. diff --git a/tests/HostTests/modules/BitSet.cpp b/tests/HostTests/modules/BitSet.cpp index df0b238ff1..6833578771 100644 --- a/tests/HostTests/modules/BitSet.cpp +++ b/tests/HostTests/modules/BitSet.cpp @@ -121,7 +121,7 @@ class BitSetTest : public TestGroup TEST_CASE("Number set") { using NumberSet = BitSet; - NumberSet numbers = 0x12345678U; + NumberSet numbers = uint32_t(0x12345678); Serial.print(_F("numbers = ")); Serial.println(toString(numbers)); REQUIRE(numbers.value() == 0x12345678U); diff --git a/tests/HostTests/modules/Clocks.cpp b/tests/HostTests/modules/Clocks.cpp index 99163d6433..8987fbec0e 100644 --- a/tests/HostTests/modules/Clocks.cpp +++ b/tests/HostTests/modules/Clocks.cpp @@ -296,7 +296,7 @@ class BenchmarkPolledTimer : public TestGroup Serial.print(" iterations, average loop time = "); using namespace NanoTime; constexpr auto nsTotal = convert(); - auto nsPerLoop = time(Nanoseconds, muldiv(nsTotal, 1U, loopCount)); + auto nsPerLoop = time(Nanoseconds, muldiv(nsTotal, uint32_t(1), loopCount)); Serial.print(nsPerLoop.toString()); Serial.print(" ("); auto cycles = CpuCycleClockNormal::template timeToTicks(uint32_t(nsPerLoop)); From 5a95a91e0a30fef651a4bb4f7cdc811814e7c25c Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 3 Sep 2021 16:40:26 +0100 Subject: [PATCH 052/130] Add Ethernet support for ESP32 (#2361) This PR aims to provide generic Ethernet support for Sming. Initially this is for the ESP32 using its embedded MAC. Note that Sming ESP32 requires a patched version of ESP IDF version 4.3. See #2358. --- .../Arch/Esp32/Components/esp32/component.mk | 13 +- .../project.esp32/sdkconfig.defaults.debug | 7 + .../project.esp32/sdkconfig.defaults.release | 7 + Sming/Arch/Esp32/Components/esp32/sdk/misc.mk | 2 +- .../Arch/Esp32/Platform/EmbeddedEthernet.cpp | 239 +++++++++++++++++ .../Arch/Esp32/Platform/EthernetPhy.cpp | 64 +++++ .../include/Platform/EmbeddedEthernet.h | 82 ++++++ Sming/Components/Network/component.mk | 4 +- .../Network/src/Network/Ethernet/Dp83848.h | 29 +++ .../Network/src/Network/Ethernet/Ip101.h | 29 +++ .../Network/src/Network/Ethernet/Ksz8041.h | 29 +++ .../Network/src/Network/Ethernet/Lan8720.h | 29 +++ .../Network/src/Network/Ethernet/Rtl8201.h | 29 +++ .../Network/src/Platform/Ethernet.cpp | 37 +++ .../Network/src/Platform/Ethernet.h | 244 ++++++++++++++++++ docs/source/framework/platform/ethernet.rst | 7 + docs/source/framework/platform/index.rst | 4 +- samples/Basic_Ethernet/.cproject | 151 +++++++++++ samples/Basic_Ethernet/.project | 28 ++ samples/Basic_Ethernet/Makefile | 9 + samples/Basic_Ethernet/README.rst | 16 ++ samples/Basic_Ethernet/app/application.cpp | 53 ++++ 22 files changed, 1103 insertions(+), 9 deletions(-) create mode 100644 Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp create mode 100644 Sming/Components/Network/Arch/Esp32/Platform/EthernetPhy.cpp create mode 100644 Sming/Components/Network/Arch/Esp32/Platform/include/Platform/EmbeddedEthernet.h create mode 100644 Sming/Components/Network/src/Network/Ethernet/Dp83848.h create mode 100644 Sming/Components/Network/src/Network/Ethernet/Ip101.h create mode 100644 Sming/Components/Network/src/Network/Ethernet/Ksz8041.h create mode 100644 Sming/Components/Network/src/Network/Ethernet/Lan8720.h create mode 100644 Sming/Components/Network/src/Network/Ethernet/Rtl8201.h create mode 100644 Sming/Components/Network/src/Platform/Ethernet.cpp create mode 100644 Sming/Components/Network/src/Platform/Ethernet.h create mode 100644 docs/source/framework/platform/ethernet.rst create mode 100644 samples/Basic_Ethernet/.cproject create mode 100644 samples/Basic_Ethernet/.project create mode 100644 samples/Basic_Ethernet/Makefile create mode 100644 samples/Basic_Ethernet/README.rst create mode 100644 samples/Basic_Ethernet/app/application.cpp diff --git a/Sming/Arch/Esp32/Components/esp32/component.mk b/Sming/Arch/Esp32/Components/esp32/component.mk index 7ee639dc2c..d973b6461c 100644 --- a/Sming/Arch/Esp32/Components/esp32/component.mk +++ b/Sming/Arch/Esp32/Components/esp32/component.mk @@ -25,6 +25,7 @@ SDK_LIBDIRS := \ LIBDIRS += \ $(SDK_COMPONENT_LIBDIR) \ + $(SDK_BUILD_BASE)/esp-idf/mbedtls/mbedtls/library \ $(SDK_BUILD_BASE)/esp-idf/$(ESP_VARIANT) \ $(SDK_BUILD_BASE)/esp-idf/$(ESP_VARIANT)/ld \ $(COMPONENT_PATH)/ld \ @@ -145,6 +146,7 @@ SDK_COMPONENTS := \ esp-tls \ $(ESP_VARIANT) \ esp_common \ + esp_eth \ esp_event \ esp_gdbstub \ esp_hw_support \ @@ -195,7 +197,6 @@ SDK_COMPONENTS += \ bt \ coap \ console \ - esp_eth \ esp_http_client \ esp_http_server \ esp_https_ota \ @@ -331,12 +332,14 @@ export SDK_BUILD_BASE export SDK_COMPONENT_LIBDIR export SDK_COMPONENTS -$(SDK_BUILD_COMPLETE): $(SDKCONFIG_H) $(SDK_COMPONENT_LIBS) +$(SDK_BUILD_COMPLETE): $(SDKCONFIG_H) $(SDKCONFIG_MAKEFILE) + $(Q) $(SDK_BUILD) bootloader app + $(Q) $(MAKE) --no-print-directory -C $(SDK_DEFAULT_PATH) -f misc.mk copylibs touch $(SDK_BUILD_COMPLETE) $(SDKCONFIG_H) $(SDKCONFIG_MAKEFILE) $(SDK_COMPONENT_LIBS): $(SDK_CONFIG_DEFAULTS) | $(SDK_BUILD_BASE) $(SDK_COMPONENT_LIBDIR) - $(Q) $(SDK_BUILD) bootloader app - $(Q) $(MAKE) --no-print-directory -C $(SDK_DEFAULT_PATH) -f misc.mk copylibs + +$(SDK_COMPONENT_LIBS): $(SDK_BUILD_COMPLETE) $(SDK_CONFIG_DEFAULTS): $(Q) cp $@.$(BUILD_TYPE) $@ @@ -348,7 +351,7 @@ sdk-menuconfig: $(SDK_CONFIG_DEFAULTS) | $(SDK_BUILD_BASE) ##Configure SDK optio @echo Now run 'make esp32-build' .PHONY: sdk-defconfig -sdk-defconfig: $(SDK_BUILD_BASE)/include/sdkconfig.h ##Create default SDK config files +sdk-defconfig: $(SDKCONFIG_H) ##Create default SDK config files .PHONY: sdk-menuconfig-clean sdk-menuconfig-clean: esp32-clean ##Wipe SDK configuration and revert to defaults diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.debug b/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.debug index 4f07843fa8..c7e8dc3728 100644 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.debug +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.debug @@ -13,6 +13,13 @@ CONFIG_COMPILER_OPTIMIZATION_SIZE=y # Mandatory LWIP changes CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 +# Ethernet +#CONFIG_ETH_USE_ESP32_EMAC=y +#CONFIG_ETH_PHY_INTERFACE_RMII=y +#CONFIG_ETH_RMII_CLK_INPUT=y +#CONFIG_ETH_RMII_CLK_IN_GPIO=0 +CONFIG_ETH_USE_SPI_ETHERNET=n + # Mandatory Sming framework changes CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.release b/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.release index 400963c451..14b10b5611 100644 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.release +++ b/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.release @@ -13,6 +13,13 @@ CONFIG_COMPILER_OPTIMIZATION_SIZE=y # Mandatory LWIP changes CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 +# Ethernet +#CONFIG_ETH_USE_ESP32_EMAC=y +#CONFIG_ETH_PHY_INTERFACE_RMII=y +#CONFIG_ETH_RMII_CLK_INPUT=y +#CONFIG_ETH_RMII_CLK_IN_GPIO=0 +CONFIG_ETH_USE_SPI_ETHERNET=n + # Mandatory Sming framework changes CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/misc.mk b/Sming/Arch/Esp32/Components/esp32/sdk/misc.mk index 0793f55563..e117fafdeb 100644 --- a/Sming/Arch/Esp32/Components/esp32/sdk/misc.mk +++ b/Sming/Arch/Esp32/Components/esp32/sdk/misc.mk @@ -7,7 +7,7 @@ include $(SMING_HOME)/build.mk all: # List of all generated SDK libraries -SDK_ARCHIVE_DIRS = $(foreach c,$(SDK_COMPONENTS),$(SDK_BUILD_BASE)/esp-idf/$c $(call ListAllSubDirs,$(SDK_BUILD_BASE)/esp-idf/$c)) +SDK_ARCHIVE_DIRS = $(foreach c,$(SDK_COMPONENTS),$(SDK_BUILD_BASE)/esp-idf/$c) SDK_ARCHIVE_LIST = $(sort $(foreach d,$(SDK_ARCHIVE_DIRS),$(wildcard $d/*.a))) # diff --git a/Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp b/Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp new file mode 100644 index 0000000000..826dfd9296 --- /dev/null +++ b/Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp @@ -0,0 +1,239 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * EmbeddedEthernet.cpp + * + ****/ + +#include +// #include +#include +#include +#include + +using namespace Ethernet; + +bool EmbeddedEthernet::begin(const Config& config) +{ +#if !CONFIG_ETH_USE_ESP32_EMAC + + debug_e("[ETH] Internal MAC not available"); + return false; + +#else + + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); + netif = esp_netif_new(&cfg); + + // Set default handlers to process TCP/IP stuffs + ESP_ERROR_CHECK(esp_eth_set_default_handlers(netif)); + + // And register our own event handlers + enableEventCallback(true); + enableGotIpCallback(true); + + eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); + if(config.mac.smiMdcPin != PIN_DEFAULT) { + mac_config.smi_mdc_gpio_num = config.mac.smiMdcPin; + } + if(config.mac.smiMdioPin != PIN_DEFAULT) { + mac_config.smi_mdio_gpio_num = config.mac.smiMdioPin; + } + mac = esp_eth_mac_new_esp32(&mac_config); + if(mac == nullptr) { + debug_e("[ETH] Failed to construct MAC"); + return false; + } + + phy = reinterpret_cast(phyFactory.create(config.phy)); + if(phy == nullptr) { + debug_e("[ETH] Failed to construct PHY"); + return false; + } + + esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy); + ESP_ERROR_CHECK(esp_eth_driver_install(ð_config, &handle)); + netif_glue = esp_eth_new_netif_glue(handle); + ESP_ERROR_CHECK(esp_netif_attach(netif, netif_glue)); + ESP_ERROR_CHECK(esp_eth_start(handle)); + + return true; +#endif +} + +void EmbeddedEthernet::end() +{ + if(handle == nullptr) { + return; + } + + ESP_ERROR_CHECK(esp_eth_stop(handle)); + ESP_ERROR_CHECK(esp_eth_del_netif_glue(netif_glue)); + netif_glue = nullptr; + ESP_ERROR_CHECK(esp_eth_clear_default_handlers(netif)); + + ESP_ERROR_CHECK(esp_eth_driver_uninstall(handle)); + handle = nullptr; + + phyFactory.destroy(reinterpret_cast(phy)); + phy = nullptr; + + ESP_ERROR_CHECK(mac->del(mac)); + mac = nullptr; + + esp_netif_destroy(netif); + netif = nullptr; + + enableEventCallback(false); + enableGotIpCallback(false); +} + +void EmbeddedEthernet::enableEventCallback(bool enable) +{ + auto handler = [](void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { + auto ethernet = static_cast(arg); + ethernet->state = Event(event_id); + if(!ethernet->eventCallback) { + return; + } + ethernet->eventCallback(Event(event_id)); + }; + + if(enable) { + auto err = esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, handler, this); + ESP_ERROR_CHECK(err); + } else { + esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, handler); + } +} + +void EmbeddedEthernet::enableGotIpCallback(bool enable) +{ + auto handler = [](void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { + auto ethernet = static_cast(arg); + if(!ethernet->gotIpCallback) { + return; + } + ip_event_got_ip_t* event = (ip_event_got_ip_t*)event_data; + auto& ip_info = event->ip_info; + ethernet->gotIpCallback(ip_info.ip.addr, ip_info.netmask.addr, ip_info.gw.addr); + }; + + if(enable) { + auto err = esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, handler, this); + ESP_ERROR_CHECK(err); + } else { + esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, handler); + } +} + +MacAddress EmbeddedEthernet::getMacAddress() const +{ + MacAddress addr; + if(mac != nullptr) { + mac->get_addr(mac, &addr[0]); + } + return addr; +} + +bool EmbeddedEthernet::setMacAddress(const MacAddress& addr) +{ + if(mac == nullptr) { + return false; + } + return mac->set_addr(mac, &const_cast(addr)[0]) == ESP_OK; +} + +bool EmbeddedEthernet::setSpeed(Speed speed) +{ + if(mac == nullptr) { + return false; + } + return mac->set_speed(mac, eth_speed_t(speed)) == ESP_OK; +} + +bool EmbeddedEthernet::setFullDuplex(bool enable) +{ + if(mac == nullptr) { + return false; + } + return mac->set_duplex(mac, enable ? ETH_DUPLEX_FULL : ETH_DUPLEX_HALF) == ESP_OK; +} + +bool EmbeddedEthernet::setLinkState(bool up) +{ + if(mac == nullptr) { + return false; + } + return mac->set_link(mac, up ? ETH_LINK_UP : ETH_LINK_DOWN) == ESP_OK; +} + +bool EmbeddedEthernet::setPromiscuous(bool enable) +{ + if(mac == nullptr) { + return false; + } + return mac->set_promiscuous(mac, enable) == ESP_OK; +} + +void EmbeddedEthernet::setHostname(const String& hostname) +{ + ESP_ERROR_CHECK(esp_netif_set_hostname(netif, hostname.c_str())); +} + +String EmbeddedEthernet::getHostname() const +{ + const char* hostName; + ESP_ERROR_CHECK(esp_netif_get_hostname(netif, &hostName)); + return hostName; +} + +IpAddress EmbeddedEthernet::getIP() const +{ + IpAddress addr; + esp_netif_ip_info_t info; + if(esp_netif_get_ip_info(netif, &info) == ESP_OK) { + addr = info.ip.addr; + } + return addr; +} + +extern "C" esp_err_t esp_netif_up(esp_netif_t* esp_netif); + +bool EmbeddedEthernet::setIP(IpAddress address, IpAddress netmask, IpAddress gateway) +{ + if(!enableDHCP(false)) { + return false; + } + esp_netif_ip_info_t ipinfo{address, netmask, gateway}; + if(esp_netif_set_ip_info(netif, &ipinfo) == ESP_OK) { + debug_i("Ethernet IP successfully updated"); + esp_netif_up(netif); + } else { + debug_e("Ethernet IP can't be updated"); + enableDHCP(true); + } + return true; +} + +bool EmbeddedEthernet::isEnabledDHCP() const +{ + esp_netif_dhcp_status_t status; + if(esp_netif_dhcps_get_status(netif, &status) != ESP_OK) { + return false; + } + + return status == ESP_NETIF_DHCP_STARTED; +} + +bool EmbeddedEthernet::enableDHCP(bool enable) +{ + if(enable) { + return esp_netif_dhcpc_start(netif) == ESP_OK; + } else { + return esp_netif_dhcpc_stop(netif) == ESP_OK; + } +} diff --git a/Sming/Components/Network/Arch/Esp32/Platform/EthernetPhy.cpp b/Sming/Components/Network/Arch/Esp32/Platform/EthernetPhy.cpp new file mode 100644 index 0000000000..339330a7ac --- /dev/null +++ b/Sming/Components/Network/Arch/Esp32/Platform/EthernetPhy.cpp @@ -0,0 +1,64 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * EthernetPhy.cpp + * + * Class implementations for all supported ESP32 Ethernet PHY devices + * + ****/ + +#include +#include +#include +#include +#include +#include +#include + +#define PHY_IMPL(class_name, func_name) \ + Ethernet::PhyInstance* class_name::create(const PhyConfig& config) \ + { \ + auto phy_config = get_esp_config(config); \ + auto phy = esp_eth_phy_new_##func_name(&phy_config); \ + return reinterpret_cast(phy); \ + } \ + \ + void class_name::destroy(PhyInstance* inst) \ + { \ + esp_phy_destroy(inst); \ + } + +namespace +{ +eth_phy_config_t get_esp_config(const Ethernet::PhyConfig& config) +{ + return eth_phy_config_t{ + .phy_addr = config.phyAddr, + .reset_timeout_ms = config.resetTimeout, + .autonego_timeout_ms = config.autoNegTimeout, + .reset_gpio_num = config.resetPin, + }; +} + +void esp_phy_destroy(Ethernet::PhyInstance* inst) +{ + auto phy = reinterpret_cast(inst); + if(phy != nullptr) { + phy->del(phy); + } +} + +} // namespace + +namespace Ethernet +{ +PHY_IMPL(Ip101, ip101) +PHY_IMPL(Rtl8201, rtl8201) +PHY_IMPL(Lan8720, lan8720) +PHY_IMPL(Dp83848, dp83848) +PHY_IMPL(Ksz8041, ksz8041) + +} // namespace Ethernet diff --git a/Sming/Components/Network/Arch/Esp32/Platform/include/Platform/EmbeddedEthernet.h b/Sming/Components/Network/Arch/Esp32/Platform/include/Platform/EmbeddedEthernet.h new file mode 100644 index 0000000000..8b11f3e64e --- /dev/null +++ b/Sming/Components/Network/Arch/Esp32/Platform/include/Platform/EmbeddedEthernet.h @@ -0,0 +1,82 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * EmbeddedEthernet.h + * + ****/ + +#pragma once + +#include + +struct esp_eth_mac_s; +struct esp_eth_phy_s; +struct esp_netif_obj; + +/** + * @brief Ethernet provider using ESP32 embedded MAC. Requires an external PHY. + * + * See https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32/api-reference/network/esp_eth.html. + * + * These are the reduced (RMII) PHY connections. + * Note that the high-speed signals may not be re-allocated. + * + * Signal GPIO Direction + * ------ ---- --------- + * TXD0 19 OUT + * TXD1 22 OUT + * TX_EN 21 OUT + * RXD0 25 IN + * RXD1 26 IN + * CRS_DV 27 IN + * Receive Data Valid + * MDC 23 OUT: configurable + * MDIO 18 OUT: configurable + * CLK_MII 0 IN: No other pins supported + * OUT: alternate pins: 16, 17 + * 50MHz clock provided either by the PHY or the MAC. Default is PHY. + * + * Note: Configuring clock options must be done via SDK (make sdk-menuconfig). + * ESP-IDF v4.4 will add the ability to override these in software. + * + * The following connections are optional: + * + * PHY_RESET - OUT (set via PhyConfig) + */ +class EmbeddedEthernet : public Ethernet::Service +{ +public: + EmbeddedEthernet(Ethernet::PhyFactory& phyFactory) : phyFactory(phyFactory) + { + } + + bool begin(const Ethernet::Config& config) override; + void end() override; + MacAddress getMacAddress() const override; + bool setMacAddress(const MacAddress& addr) override; + bool setSpeed(Ethernet::Speed speed) override; + bool setFullDuplex(bool enable) override; + bool setLinkState(bool up) override; + bool setPromiscuous(bool enable) override; + void setHostname(const String& hostname) override; + String getHostname() const override; + IpAddress getIP() const override; + bool setIP(IpAddress address, IpAddress netmask, IpAddress gateway) override; + bool isEnabledDHCP() const override; + bool enableDHCP(bool enable) override; + +private: + void enableEventCallback(bool enable); + void enableGotIpCallback(bool enable); + + Ethernet::PhyFactory& phyFactory; + void* handle{nullptr}; + esp_netif_obj* netif{nullptr}; + void* netif_glue{nullptr}; + esp_eth_mac_s* mac{nullptr}; + esp_eth_phy_s* phy{nullptr}; + Ethernet::Event state{Ethernet::Event::Disconnected}; +}; diff --git a/Sming/Components/Network/component.mk b/Sming/Components/Network/component.mk index 572ef6a501..268da4aca1 100644 --- a/Sming/Components/Network/component.mk +++ b/Sming/Components/Network/component.mk @@ -3,7 +3,9 @@ COMPONENT_SRCDIRS := \ $(call ListAllSubDirs,$(COMPONENT_PATH)/src) \ $(call ListAllSubDirs,$(COMPONENT_PATH)/Arch/$(SMING_ARCH)) -COMPONENT_INCDIRS := src +COMPONENT_INCDIRS := \ + src \ + Arch/$(SMING_ARCH)/Platform/include COMPONENT_DOXYGEN_INPUT := src diff --git a/Sming/Components/Network/src/Network/Ethernet/Dp83848.h b/Sming/Components/Network/src/Network/Ethernet/Dp83848.h new file mode 100644 index 0000000000..e79381c987 --- /dev/null +++ b/Sming/Components/Network/src/Network/Ethernet/Dp83848.h @@ -0,0 +1,29 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Dp83848.h + * + ****/ + +#pragma once + +#include + +namespace Ethernet +{ +/** + * @brief DP 83848 PHY interface + */ +class Dp83848 : public PhyFactory +{ +public: + using PhyFactory::PhyFactory; + + PhyInstance* create(const PhyConfig& config) override; + void destroy(PhyInstance* inst) override; +}; + +} // namespace Ethernet diff --git a/Sming/Components/Network/src/Network/Ethernet/Ip101.h b/Sming/Components/Network/src/Network/Ethernet/Ip101.h new file mode 100644 index 0000000000..a2caf1c1e2 --- /dev/null +++ b/Sming/Components/Network/src/Network/Ethernet/Ip101.h @@ -0,0 +1,29 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Ip101.h + * + ****/ + +#pragma once + +#include + +namespace Ethernet +{ +/** + * @brief IP101 PHY interface + */ +class Ip101 : public PhyFactory +{ +public: + using PhyFactory::PhyFactory; + + PhyInstance* create(const PhyConfig& config) override; + void destroy(PhyInstance* inst) override; +}; + +} // namespace Ethernet diff --git a/Sming/Components/Network/src/Network/Ethernet/Ksz8041.h b/Sming/Components/Network/src/Network/Ethernet/Ksz8041.h new file mode 100644 index 0000000000..6ce64b5795 --- /dev/null +++ b/Sming/Components/Network/src/Network/Ethernet/Ksz8041.h @@ -0,0 +1,29 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Ksz8041.h + * + ****/ + +#pragma once + +#include + +namespace Ethernet +{ +/** + * @brief KSZ 8041 PHY interface + */ +class Ksz8041 : public PhyFactory +{ +public: + using PhyFactory::PhyFactory; + + PhyInstance* create(const PhyConfig& config) override; + void destroy(PhyInstance* inst) override; +}; + +} // namespace Ethernet diff --git a/Sming/Components/Network/src/Network/Ethernet/Lan8720.h b/Sming/Components/Network/src/Network/Ethernet/Lan8720.h new file mode 100644 index 0000000000..6b49452ef8 --- /dev/null +++ b/Sming/Components/Network/src/Network/Ethernet/Lan8720.h @@ -0,0 +1,29 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Lan8720.h + * + ****/ + +#pragma once + +#include + +namespace Ethernet +{ +/** + * @brief LAN 8720 PHY interface + */ +class Lan8720 : public PhyFactory +{ +public: + using PhyFactory::PhyFactory; + + PhyInstance* create(const PhyConfig& config) override; + void destroy(PhyInstance* inst) override; +}; + +} // namespace Ethernet diff --git a/Sming/Components/Network/src/Network/Ethernet/Rtl8201.h b/Sming/Components/Network/src/Network/Ethernet/Rtl8201.h new file mode 100644 index 0000000000..748f3f3306 --- /dev/null +++ b/Sming/Components/Network/src/Network/Ethernet/Rtl8201.h @@ -0,0 +1,29 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Rtl8201.h + * + ****/ + +#pragma once + +#include + +namespace Ethernet +{ +/** + * @brief RTL 8201 PHY interface + */ +class Rtl8201 : public PhyFactory +{ +public: + using PhyFactory::PhyFactory; + + PhyInstance* create(const PhyConfig& config) override; + void destroy(PhyInstance* inst) override; +}; + +} // namespace Ethernet diff --git a/Sming/Components/Network/src/Platform/Ethernet.cpp b/Sming/Components/Network/src/Platform/Ethernet.cpp new file mode 100644 index 0000000000..d211484170 --- /dev/null +++ b/Sming/Components/Network/src/Platform/Ethernet.cpp @@ -0,0 +1,37 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Ethernet.cpp + * + ****/ + +#include "Ethernet.h" + +String toString(Ethernet::Event event) +{ + switch(event) { +#define XX(tag, desc) \ + case Ethernet::Event::tag: \ + return F(#tag); + ETHERNET_EVENT_MAP(XX) +#undef XX + default: + return F("Unknown_") + unsigned(event); + } +} + +String toLongString(Ethernet::Event event) +{ + switch(event) { +#define XX(tag, desc) \ + case Ethernet::Event::tag: \ + return F(desc); + ETHERNET_EVENT_MAP(XX) +#undef XX + default: + return F("Unknown_") + unsigned(event); + } +} diff --git a/Sming/Components/Network/src/Platform/Ethernet.h b/Sming/Components/Network/src/Platform/Ethernet.h new file mode 100644 index 0000000000..f3662ed01a --- /dev/null +++ b/Sming/Components/Network/src/Platform/Ethernet.h @@ -0,0 +1,244 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Ethernet.h + * + ****/ + +#pragma once + +#include +#include +#include +#include +#include + +/** + * @brief Ethernet event code map + * @note These codes are based on the ESP IDF public API. + */ +#define ETHERNET_EVENT_MAP(XX) \ + XX(Start, "Ethernet driver Started") \ + XX(Stop, "Ethernet driver stopped") \ + XX(Connected, "Ethernet link established") \ + XX(Disconnected, "Ethernet link lost") + +namespace Ethernet +{ +/** + * @brief Ethernet event codes + */ +enum class Event { +#define XX(tag, desc) tag, + ETHERNET_EVENT_MAP(XX) +#undef XX +}; + +/** + * @brief Delegate type for Ethernet events + * @param event Which event occurred + * @param mac Provided on 'Connected' event only + */ +using EventDelegate = Delegate; + +/** + * @brief Delegate type for 'got IP address' event + */ +using GotIpDelegate = Delegate; + +/** + * @brief Use default pin for platform + */ +constexpr int8_t PIN_DEFAULT{-2}; + +/** + * @brief Do not configure this pin + * + * Only applies if pin is optional, otherwise it will be interpreted as 'auto detect'. + */ +constexpr int8_t PIN_UNUSED{-1}; + +/** + * @brief Configuration for Ethernet MAC + */ +struct MacConfig { + int8_t smiMdcPin = PIN_DEFAULT; //< SMI MDC GPIO number + int8_t smiMdioPin = PIN_DEFAULT; //< SMI MDIO GPIO number +}; + +/** + * @brief Link speed + */ +enum class Speed { + MBPS10, + MBPS100, +}; + +/** + * @brief Constructed PHY instance. An opaque, implementation-specific type. + */ +struct PhyInstance; + +/** + * @brief Automatically detect PHY address during initialization + */ +constexpr int8_t PHY_ADDR_AUTO{-1}; + +/** + * @brief PHY configuration + */ +struct PhyConfig { + int8_t phyAddr = PHY_ADDR_AUTO; ///< PHY address + int8_t resetPin = PIN_UNUSED; ///< Reset GPIO number */ + uint16_t resetTimeout = 100; ///< Reset timeout value in milliseconds + uint16_t autoNegTimeout = 4000; ///< Auto-negotiation timeout in milliseconds +}; + +/** + * @brief Virtual class used to construct a specific PHY instance + * + * Applications provide an instance of this factory class so that the Service + * can create and configure it at the correct point in initialisation or teardown. + */ +class PhyFactory +{ +public: + using PhyInstance = Ethernet::PhyInstance; + + /** + * @brief Called by the Service to construct a PHY instance. + */ + virtual PhyInstance* create(const PhyConfig& config) = 0; + + /** + * @brief Called by the Service to destroy a PHY instance. + */ + virtual void destroy(PhyInstance* inst) = 0; +}; + +/** + * @brief Service configuration options + */ +struct Config { + MacConfig mac; + PhyConfig phy; +}; + +/** + * @brief Abstract Service class + * + * Provides a common implementation for TCP/IP ethernet support. + * + * An Ethernet interface requires a MAC layer plus PHY. + * + * The ESP32, for example, contains a MAC but requires an external PHY. + * Other solutions, such as the W5500, contain MAC+PHY and require the correct + * PhyFactory to work. + * + * Ethernet implementations should provide appropriate setup methods which are called by the application + * before invoking `begin()`. + */ +class Service +{ +public: + /** + * @brief Configure and start the ethernet service + * @param config Configuration options + * + * Applications should expect to receive Start and Connected events following this call. + */ + virtual bool begin(const Config& config) = 0; + + /** + * @brief Tear down the ethernet connection + */ + virtual void end() = 0; + + /** + * @brief Get MAC address + */ + virtual MacAddress getMacAddress() const = 0; + + /** + * @brief Set MAC address + */ + virtual bool setMacAddress(const MacAddress& addr) = 0; + + /** + * @brief Set speed of MAC + */ + virtual bool setSpeed(Speed speed) = 0; + + /** + * @brief Set duplex mode of MAC + */ + virtual bool setFullDuplex(bool enable) = 0; + + /** + * @brief Set link status of MAC + */ + virtual bool setLinkState(bool up) = 0; + + /** + * @brief Set MAC promiscuous mode + */ + virtual bool setPromiscuous(bool enable) = 0; + + /** + * @brief Set DHCP hostname + */ + virtual void setHostname(const String& hostname) = 0; + + /** + * @brief Get DHCP hostname + */ + virtual String getHostname() const = 0; + + /** + * @brief Get current IP address + */ + virtual IpAddress getIP() const = 0; + + /** + * @brief Set static IP address + */ + virtual bool setIP(IpAddress address, IpAddress netmask, IpAddress gateway) = 0; + + /** + * @brief Determine if DHCP is active for this interface + */ + virtual bool isEnabledDHCP() const = 0; + + /** + * @brief Enable/disable DHCP on this interface + */ + virtual bool enableDHCP(bool enable) = 0; + + /** + * @brief Set callback for ethernet events + */ + void onEvent(EventDelegate callback) + { + eventCallback = callback; + } + + /** + * @brief Set callback for 'station connected with IP address' event + */ + void onGotIp(GotIpDelegate callback) + { + gotIpCallback = callback; + } + +protected: + EventDelegate eventCallback; + GotIpDelegate gotIpCallback; +}; + +} // namespace Ethernet + +String toString(Ethernet::Event event); +String toLongString(Ethernet::Event event); diff --git a/docs/source/framework/platform/ethernet.rst b/docs/source/framework/platform/ethernet.rst new file mode 100644 index 0000000000..74bcde4b3e --- /dev/null +++ b/docs/source/framework/platform/ethernet.rst @@ -0,0 +1,7 @@ +Ethernet +======== + +Currently only supported on ESP32 using embedded MAC. + +.. doxygennamespace:: Ethernet + :members: diff --git a/docs/source/framework/platform/index.rst b/docs/source/framework/platform/index.rst index a96ba829b7..29ad6f21b3 100644 --- a/docs/source/framework/platform/index.rst +++ b/docs/source/framework/platform/index.rst @@ -2,7 +2,7 @@ Platform Support ================ .. toctree:: + :glob: :maxdepth: 1 - wifi - system + * diff --git a/samples/Basic_Ethernet/.cproject b/samples/Basic_Ethernet/.cproject new file mode 100644 index 0000000000..335030239d --- /dev/null +++ b/samples/Basic_Ethernet/.cproject @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + make + -f ${ProjDirPath}/Makefile + all + true + true + true + + + make + -f ${ProjDirPath}/Makefile + clean + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flash + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flashonefile + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flashinit + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flashboot + true + true + true + + + make + -f ${ProjDirPath}/Makefile + rebuild + true + true + true + + + + + + + + + + + + + + + + + + + + diff --git a/samples/Basic_Ethernet/.project b/samples/Basic_Ethernet/.project new file mode 100644 index 0000000000..ded68cb818 --- /dev/null +++ b/samples/Basic_Ethernet/.project @@ -0,0 +1,28 @@ + + + Basic_Ethernet + + + SmingFramework + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/samples/Basic_Ethernet/Makefile b/samples/Basic_Ethernet/Makefile new file mode 100644 index 0000000000..ff51b6c3a7 --- /dev/null +++ b/samples/Basic_Ethernet/Makefile @@ -0,0 +1,9 @@ +##################################################################### +#### Please don't change this file. Use component.mk instead #### +##################################################################### + +ifndef SMING_HOME +$(error SMING_HOME is not set: please configure it as an environment variable) +endif + +include $(SMING_HOME)/project.mk diff --git a/samples/Basic_Ethernet/README.rst b/samples/Basic_Ethernet/README.rst new file mode 100644 index 0000000000..798fef75c9 --- /dev/null +++ b/samples/Basic_Ethernet/README.rst @@ -0,0 +1,16 @@ +Basic Ethernet +============== + +Demonstrates creating an ethernet connection. + +Currently only supported for ESP32 with internal MAC. +An external PHY is required. This demonstration uses a commonly available LAN8270 module. + +.. note: + + The MAC/PHY interface uses high-speed signalling so connections must be solid. + The connection may *appear* to work but fail, for example, to obtain network address. + If this happens, check connections. + + In the current configuration a 50MHz synchronisation clock is received from the PHY on GPIO0. + Not all ESP32 development boards have this pin available! diff --git a/samples/Basic_Ethernet/app/application.cpp b/samples/Basic_Ethernet/app/application.cpp new file mode 100644 index 0000000000..858b9abb1f --- /dev/null +++ b/samples/Basic_Ethernet/app/application.cpp @@ -0,0 +1,53 @@ +#include + +#ifdef SUBARCH_ESP32 + +#include +#include + +Ethernet::Lan8720 phy; +EmbeddedEthernet ethernet(phy); + +static void ethernetEventHandler(Ethernet::Event event) +{ + Serial.print(toString(event)); + Serial.print(_F(", MAC = ")); + Serial.println(ethernet.getMacAddress().toString()); +} + +static void ethernetGotIp(IpAddress ip, IpAddress netmask, IpAddress gateway) +{ + Serial.print(_F("Connected! Ethernet IP ")); + Serial.print(ip.toString()); + Serial.print(_F(", netmask ")); + Serial.print(netmask.toString()); + Serial.print(_F(", gateway ")); + Serial.println(gateway.toString()); +} + +void init() +{ + Serial.begin(SERIAL_BAUD_RATE); + Serial.systemDebugOutput(true); // Allow debug print to serial + + ethernet.onEvent(ethernetEventHandler); + ethernet.onGotIp(ethernetGotIp); + + // Modify default config as required + Ethernet::Config config; + ethernet.begin(config); + + // Change the advertised hostname for DHCP + ethernet.setHostname("sming-ethernet"); + + // Set a static IP + // ethernet.setIP(IpAddress("192.168.1.12"), IpAddress("255.255.255.0"), IpAddress("192.168.1.254")); +} + +#else + +void init() +{ +} + +#endif From 22dfd8638e0c69daf27db6101eb8db0d8bdaa191 Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 5 Sep 2021 05:29:17 +0100 Subject: [PATCH 053/130] Add DISABLE_NETWORK and SDK_CUSTOM_CONFIG settings (#2362) This PR adds the `DISABLE_NETWORK` setting to explicitly remove network support. When enabled, if performs much like `DISABLE_WIFI` except it now works for ESP32 as well. The behaviour of `DISABLE_WIFI` is now more logical, and can be used with ethernet. The ESP32 build process has been improved. PR #2358 added a separate project for each `ESP_VARIANT`. This is now created dynamically from a template along with the default configuration. The `SDK_CUSTOM_CONFIG` build variable has been added so ESP32 SDK settings may be configured on a per-project basis, if necessary. --- Sming/Arch/Esp32/Components/esp32/.gitignore | 4 +- Sming/Arch/Esp32/Components/esp32/README.rst | 36 ++++---- .../Arch/Esp32/Components/esp32/component.mk | 86 +++++++++++++------ .../project.esp32/sdkconfig.defaults.debug | 47 ---------- .../project.esp32/sdkconfig.defaults.release | 44 ---------- .../esp32/project.esp32c3/CMakeLists.txt | 8 -- .../esp32/project.esp32c3/main/CMakeLists.txt | 2 - .../esp32/project.esp32c3/main/main.c | 3 - .../project.esp32c3/sdkconfig.defaults.debug | 37 -------- .../sdkconfig.defaults.release | 34 -------- .../esp32/project.esp32s2/CMakeLists.txt | 8 -- .../esp32/project.esp32s2/main/CMakeLists.txt | 2 - .../esp32/project.esp32s2/main/main.c | 3 - .../esp32/project.esp32s3/CMakeLists.txt | 8 -- .../esp32/project.esp32s3/main/CMakeLists.txt | 2 - .../esp32/project.esp32s3/main/main.c | 3 - .../project.esp32s3/sdkconfig.defaults.debug | 37 -------- .../sdkconfig.defaults.release | 34 -------- .../Components/esp32/sdk/config/README.rst | 23 +++++ .../Esp32/Components/esp32/sdk/config/common | 28 ++++++ .../config/debug} | 16 +--- .../config/release} | 16 +--- Sming/Arch/Esp32/Components/esp32/sdk/misc.mk | 7 +- .../project}/CMakeLists.txt | 0 .../project}/main/CMakeLists.txt | 0 .../project}/main/main.c | 0 .../Esp32/Components/esp32/src/startup.cpp | 17 +++- Sming/Arch/Esp32/README.rst | 35 +++++--- Sming/Arch/Esp32/build.mk | 3 + .../Arch/Host/Components/hostlib/startup.cpp | 16 ++-- Sming/Arch/Host/System/include/user_config.h | 2 +- .../Arch/Esp32/Platform/EmbeddedEthernet.cpp | 3 + Sming/Core/SmingCore.h | 4 +- Sming/README.rst | 16 +++- .../CommandProcessing/CommandExecutor.cpp | 2 +- .../CommandProcessing/CommandExecutor.h | 4 +- .../CommandProcessing/CommandHandler.cpp | 2 +- .../CommandProcessing/CommandOutput.cpp | 2 +- .../CommandProcessing/CommandOutput.h | 6 +- Sming/System/include/debug_progmem.h | 4 +- Sming/Wiring/WiringFrameworkIncludes.h | 2 +- Sming/component.mk | 12 ++- docs/source/upgrading/4.3-4.4.rst | 15 ++-- samples/Basic_Ethernet/component.mk | 1 + 44 files changed, 235 insertions(+), 399 deletions(-) delete mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.debug delete mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.release delete mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32c3/CMakeLists.txt delete mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/CMakeLists.txt delete mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/main.c delete mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.debug delete mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.release delete mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s2/CMakeLists.txt delete mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/CMakeLists.txt delete mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/main.c delete mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s3/CMakeLists.txt delete mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/CMakeLists.txt delete mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/main.c delete mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.debug delete mode 100644 Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.release create mode 100644 Sming/Arch/Esp32/Components/esp32/sdk/config/README.rst create mode 100644 Sming/Arch/Esp32/Components/esp32/sdk/config/common rename Sming/Arch/Esp32/Components/esp32/{project.esp32s2/sdkconfig.defaults.debug => sdk/config/debug} (70%) rename Sming/Arch/Esp32/Components/esp32/{project.esp32s2/sdkconfig.defaults.release => sdk/config/release} (62%) rename Sming/Arch/Esp32/Components/esp32/{project.esp32 => sdk/project}/CMakeLists.txt (100%) rename Sming/Arch/Esp32/Components/esp32/{project.esp32 => sdk/project}/main/CMakeLists.txt (100%) rename Sming/Arch/Esp32/Components/esp32/{project.esp32 => sdk/project}/main/main.c (100%) create mode 100644 samples/Basic_Ethernet/component.mk diff --git a/Sming/Arch/Esp32/Components/esp32/.gitignore b/Sming/Arch/Esp32/Components/esp32/.gitignore index 33915c150b..5191b607b3 100644 --- a/Sming/Arch/Esp32/Components/esp32/.gitignore +++ b/Sming/Arch/Esp32/Components/esp32/.gitignore @@ -1,3 +1 @@ -sdkconfig -sdkconfig.defaults -sdkconfig.old +project diff --git a/Sming/Arch/Esp32/Components/esp32/README.rst b/Sming/Arch/Esp32/Components/esp32/README.rst index 5017d7f176..94c906a6ee 100644 --- a/Sming/Arch/Esp32/Components/esp32/README.rst +++ b/Sming/Arch/Esp32/Components/esp32/README.rst @@ -21,23 +21,6 @@ Followed by:: Configuration variables ----------------------- -These are read-only debug variables: - -.. envvar:: SDK_INTERNAL - - **READONLY** When compiled using the current (version 3+) Espressif SDK this value is set to 1. - - -.. envvar:: SDK_LIBDIR - - **READONLY** Path to the directory containing SDK archive libraries - - -.. envvar:: SDK_INCDIR - - **READONLY** Path to the directory containing SDK header files - - Option variables: .. envvar:: ESP_VARIANT @@ -68,3 +51,22 @@ or if multiple versions are installed. By default, the most current version will Location of ESP-IDF python. + +Background +---------- + +An empty ESP IDF project is built which generates a set of libraries and headers +which the framework can then be built against. + +The project is located in ``project/{ESP_VARIANT}`. + +The code for this project is copied from ``sdk/project``. + +The default configuration settings are obtained from ``sdk/config`` and written +to ``project/{ESP_VARIANT}/sdkconfig.defaults``. + +When ``sdk-menuconfig`` is run, the ``project/{ESP_VARIANT}/sdkconfig`` is modified. +This can be reset using ``make sdk-config-clean``. + +If custom settings are required for a project then place these in a separate file +and set :envvar:`SDK_CUSTOM_CONFIG` to the location, relative to the project source root directory. diff --git a/Sming/Arch/Esp32/Components/esp32/component.mk b/Sming/Arch/Esp32/Components/esp32/component.mk index d973b6461c..d1347f05e2 100644 --- a/Sming/Arch/Esp32/Components/esp32/component.mk +++ b/Sming/Arch/Esp32/Components/esp32/component.mk @@ -11,8 +11,13 @@ COMPONENT_INCDIRS := src/include include CACHE_VARS += SDK_FULL_BUILD SDK_FULL_BUILD ?= 0 -SDK_BUILD_BASE := $(COMPONENT_BUILD_DIR)/sdk.$(ESP_VARIANT) -SDK_COMPONENT_LIBDIR := $(COMPONENT_BUILD_DIR)/lib.$(ESP_VARIANT) +# Applications can provide file with custom SDK configuration settings +CACHE_VARS += SDK_CUSTOM_CONFIG + +COMPONENT_RELINK_VARS += DISABLE_NETWORK DISABLE_WIFI + +SDK_BUILD_BASE := $(COMPONENT_BUILD_BASE)/$(ESP_VARIANT)/sdk +SDK_COMPONENT_LIBDIR := $(COMPONENT_BUILD_BASE)/$(ESP_VARIANT)/lib SDKCONFIG_H := $(SDK_BUILD_BASE)/config/sdkconfig.h @@ -146,7 +151,6 @@ SDK_COMPONENTS := \ esp-tls \ $(ESP_VARIANT) \ esp_common \ - esp_eth \ esp_event \ esp_gdbstub \ esp_hw_support \ @@ -156,29 +160,35 @@ SDK_COMPONENTS := \ esp_rom \ esp_system \ esp_timer \ - esp_wifi \ espcoredump \ freertos \ hal \ heap \ log \ - lwip \ - mbedtls \ - mbedcrypto \ - esp_netif \ newlib \ nvs_flash \ - openssl \ protobuf-c \ protocomm \ pthread \ soc \ spi_flash \ - tcp_transport \ - tcpip_adapter \ - vfs \ + vfs + +ifneq ($(DISABLE_NETWORK),1) +SDK_COMPONENTS += \ + esp_wifi \ + esp_eth \ + lwip \ + mbedtls \ + mbedcrypto \ + esp_netif \ + openssl +ifneq ($(DISABLE_WIFI),1) +SDK_COMPONENTS += \ wifi_provisioning \ wpa_supplicant +endif +endif ifneq ($(ESP_VARIANT),esp32s3) SDK_COMPONENTS += esp_adc_cal @@ -246,10 +256,13 @@ endif EXTRA_LIBS := \ gcc \ $(SDK_COMPONENTS) \ - $(SDK_ESP_WIFI_LIBS) \ $(SDK_NEWLIB_LIBS) \ $(SDK_TARGET_ARCH_LIBS) +ifneq ($(DISABLE_WIFI),1) +EXTRA_LIBS += $(SDK_ESP_WIFI_LIBS) +endif + LinkerScript = -T $(ESP_VARIANT).$1.ld LDFLAGS_esp32 := \ @@ -298,13 +311,9 @@ FLASH_BOOT_CHUNKS := 0x1000=$(FLASH_BOOT_LOADER) SDK_DEFAULT_PATH := $(COMPONENT_PATH)/sdk -##@Partitions - -SDK_PARTITION_PATH := $(SDK_DEFAULT_PATH)/partitions - ##@SDK -SDK_PROJECT_PATH := $(COMPONENT_PATH)/project.$(ESP_VARIANT) +SDK_PROJECT_PATH := $(COMPONENT_PATH)/project/$(ESP_VARIANT) SDK_CONFIG_DEFAULTS := $(SDK_PROJECT_PATH)/sdkconfig.defaults SDKCONFIG_MAKEFILE := $(SDK_PROJECT_PATH)/sdkconfig @@ -323,7 +332,7 @@ SDK_BUILD_COMPLETE := $(SDK_BUILD_BASE)/.complete CUSTOM_TARGETS += checksdk .PHONY: checksdk -checksdk: $(SDK_BUILD_COMPLETE) +checksdk: $(SDK_PROJECT_PATH) $(SDK_BUILD_COMPLETE) SDK_BUILD = $(ESP32_PYTHON) $(IDF_PATH)/tools/idf.py -C $(SDK_PROJECT_PATH) -B $(SDK_BUILD_BASE) -G Ninja @@ -333,29 +342,52 @@ export SDK_COMPONENT_LIBDIR export SDK_COMPONENTS $(SDK_BUILD_COMPLETE): $(SDKCONFIG_H) $(SDKCONFIG_MAKEFILE) - $(Q) $(SDK_BUILD) bootloader app + $(Q) $(SDK_BUILD) reconfigure + $(Q) $(NINJA) -C $(SDK_BUILD_BASE) bootloader app $(Q) $(MAKE) --no-print-directory -C $(SDK_DEFAULT_PATH) -f misc.mk copylibs touch $(SDK_BUILD_COMPLETE) -$(SDKCONFIG_H) $(SDKCONFIG_MAKEFILE) $(SDK_COMPONENT_LIBS): $(SDK_CONFIG_DEFAULTS) | $(SDK_BUILD_BASE) $(SDK_COMPONENT_LIBDIR) +$(SDKCONFIG_H) $(SDKCONFIG_MAKEFILE) $(SDK_COMPONENT_LIBS): $(SDK_PROJECT_PATH) $(SDK_CONFIG_DEFAULTS) | $(SDK_BUILD_BASE) $(SDK_COMPONENT_LIBDIR) + +$(SDK_PROJECT_PATH): + $(Q) mkdir -p $@ + $(Q) cp -r $(SDK_DEFAULT_PATH)/project/* $@ $(SDK_COMPONENT_LIBS): $(SDK_BUILD_COMPLETE) -$(SDK_CONFIG_DEFAULTS): - $(Q) cp $@.$(BUILD_TYPE) $@ +SDK_CONFIG_FILES := \ + common \ + $(BUILD_TYPE) \ + $(ESP_VARIANT).common \ + $(ESP_VARIANT).$(BUILD_TYPE) + +ifdef SDK_CUSTOM_CONFIG +SDK_CUSTOM_CONFIG_PATH := $(call AbsoluteSourcePath,$(PROJECT_DIR),$(SDK_CUSTOM_CONFIG)) +endif + +SDK_CONFIG_FILES := \ + $(addprefix $(SDK_DEFAULT_PATH)/config/,$(SDK_CONFIG_FILES)) \ + $(SDK_CUSTOM_CONFIG_PATH) + +$(SDK_CONFIG_DEFAULTS): $(SDK_CUSTOM_CONFIG_PATH) + @echo Creating $@ + $(Q) rm -f $@ + $(Q) $(foreach f,$(SDK_CONFIG_FILES),\ + $(if $(wildcard $f),cat $f >> $@;) \ + ) PHONY: sdk-menuconfig sdk-menuconfig: $(SDK_CONFIG_DEFAULTS) | $(SDK_BUILD_BASE) ##Configure SDK options $(Q) $(SDK_BUILD) menuconfig - $(Q) rm $(SDK_BUILD_COMPLETE) + $(Q) rm -f $(SDK_BUILD_COMPLETE) @echo Now run 'make esp32-build' .PHONY: sdk-defconfig sdk-defconfig: $(SDKCONFIG_H) ##Create default SDK config files -.PHONY: sdk-menuconfig-clean -sdk-menuconfig-clean: esp32-clean ##Wipe SDK configuration and revert to defaults - $(Q) rm -f $(SDKCONFIG_MAKEFILE) $(SDK_CONFIG_DEFAULTS) +.PHONY: sdk-config-clean +sdk-config-clean: esp32-clean ##Wipe SDK configuration and revert to defaults + $(Q) rm -rf $(SDK_PROJECT_PATH) .PHONY: sdk-help sdk-help: ##Get SDK build options diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.debug b/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.debug deleted file mode 100644 index c7e8dc3728..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.debug +++ /dev/null @@ -1,47 +0,0 @@ -# -# Espressif IoT Development Framework (ESP-IDF) Project Configuration -# -# SMING DEBUG SETTINGS -# - -# -# SDK tool configuration -# - -CONFIG_COMPILER_OPTIMIZATION_SIZE=y - -# Mandatory LWIP changes -CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 - -# Ethernet -#CONFIG_ETH_USE_ESP32_EMAC=y -#CONFIG_ETH_PHY_INTERFACE_RMII=y -#CONFIG_ETH_RMII_CLK_INPUT=y -#CONFIG_ETH_RMII_CLK_IN_GPIO=0 -CONFIG_ETH_USE_SPI_ETHERNET=n - -# Mandatory Sming framework changes -CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 -CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 -CONFIG_ESP_TIMER_IMPL_FRC2=y - -# The bootloader logs all type of messages -CONFIG_BOOTLOADER_LOG_LEVEL_NONE= -CONFIG_BOOTLOADER_LOG_LEVEL_ERROR= -CONFIG_BOOTLOADER_LOG_LEVEL_WARN= -CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y -CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= -CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= -CONFIG_BOOTLOADER_LOG_LEVEL=3 - -# ESP-IDF logs all type of messages -CONFIG_LOG_DEFAULT_LEVEL_NONE= -CONFIG_LOG_DEFAULT_LEVEL_ERROR= -CONFIG_LOG_DEFAULT_LEVEL_WARN= -CONFIG_LOG_DEFAULT_LEVEL_INFO=y -CONFIG_LOG_DEFAULT_LEVEL_DEBUG= -CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= -CONFIG_LOG_DEFAULT_LEVEL=3 - -# Sets watchpoint index 1 (the second of two) at the end of any task stack. This is the most accurate way to debug task stack overflows. -CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.release b/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.release deleted file mode 100644 index 14b10b5611..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32/sdkconfig.defaults.release +++ /dev/null @@ -1,44 +0,0 @@ -# -# Espressif IoT Development Framework (ESP-IDF) Project Configuration -# -# SMING RELEASE SETTINGS -# - -# -# SDK tool configuration -# - -CONFIG_COMPILER_OPTIMIZATION_SIZE=y - -# Mandatory LWIP changes -CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 - -# Ethernet -#CONFIG_ETH_USE_ESP32_EMAC=y -#CONFIG_ETH_PHY_INTERFACE_RMII=y -#CONFIG_ETH_RMII_CLK_INPUT=y -#CONFIG_ETH_RMII_CLK_IN_GPIO=0 -CONFIG_ETH_USE_SPI_ETHERNET=n - -# Mandatory Sming framework changes -CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 -CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 -CONFIG_ESP_TIMER_IMPL_FRC2=y - -# The bootloader logs only errors -CONFIG_BOOTLOADER_LOG_LEVEL_NONE= -CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y -CONFIG_BOOTLOADER_LOG_LEVEL_WARN= -CONFIG_BOOTLOADER_LOG_LEVEL_INFO= -CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= -CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= -CONFIG_BOOTLOADER_LOG_LEVEL=1 - -# ESP-IDF logs only errors -CONFIG_LOG_DEFAULT_LEVEL_NONE= -CONFIG_LOG_DEFAULT_LEVEL_ERROR=y -CONFIG_LOG_DEFAULT_LEVEL_WARN= -CONFIG_LOG_DEFAULT_LEVEL_INFO= -CONFIG_LOG_DEFAULT_LEVEL_DEBUG= -CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= -CONFIG_LOG_DEFAULT_LEVEL=1 diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32c3/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/project.esp32c3/CMakeLists.txt deleted file mode 100644 index bd18c8ced4..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32c3/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -# The following five lines of boilerplate have to be in your project's -# CMakeLists in this exact order for cmake to work correctly -cmake_minimum_required(VERSION 3.5) - -set(IDF_TARGET "$ENV{ESP_VARIANT}") - -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(Sming) diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/CMakeLists.txt deleted file mode 100644 index cf2c455cb5..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -idf_component_register(SRCS "main.c" - INCLUDE_DIRS ".") diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/main.c b/Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/main.c deleted file mode 100644 index cb05851571..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32c3/main/main.c +++ /dev/null @@ -1,3 +0,0 @@ -void app_main(void) -{ -} diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.debug b/Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.debug deleted file mode 100644 index 8d660b03c2..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.debug +++ /dev/null @@ -1,37 +0,0 @@ -# -# Espressif IoT Development Framework (ESP-IDF) Project Configuration -# -# SMING DEBUG SETTINGS -# - -# -# SDK tool configuration -# - -# Mandatory LWIP changes -CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 - -# Mandatory Sming framework changes -CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 -CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 - -# The bootloader logs all type of messages -CONFIG_BOOTLOADER_LOG_LEVEL_NONE= -CONFIG_BOOTLOADER_LOG_LEVEL_ERROR= -CONFIG_BOOTLOADER_LOG_LEVEL_WARN= -CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y -CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= -CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= -CONFIG_BOOTLOADER_LOG_LEVEL=3 - -# ESP-IDF logs all type of messages -CONFIG_LOG_DEFAULT_LEVEL_NONE= -CONFIG_LOG_DEFAULT_LEVEL_ERROR= -CONFIG_LOG_DEFAULT_LEVEL_WARN= -CONFIG_LOG_DEFAULT_LEVEL_INFO=y -CONFIG_LOG_DEFAULT_LEVEL_DEBUG= -CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= -CONFIG_LOG_DEFAULT_LEVEL=3 - -# Sets watchpoint index 1 (the second of two) at the end of any task stack. This is the most accurate way to debug task stack overflows. -CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.release b/Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.release deleted file mode 100644 index c0346b7594..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32c3/sdkconfig.defaults.release +++ /dev/null @@ -1,34 +0,0 @@ -# -# Espressif IoT Development Framework (ESP-IDF) Project Configuration -# -# SMING RELEASE SETTINGS -# - -# -# SDK tool configuration -# - -# Mandatory LWIP changes -CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 - -# Mandatory Sming framework changes -CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 -CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 - -# The bootloader logs only errors -CONFIG_BOOTLOADER_LOG_LEVEL_NONE= -CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y -CONFIG_BOOTLOADER_LOG_LEVEL_WARN= -CONFIG_BOOTLOADER_LOG_LEVEL_INFO= -CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= -CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= -CONFIG_BOOTLOADER_LOG_LEVEL=1 - -# ESP-IDF logs only errors -CONFIG_LOG_DEFAULT_LEVEL_NONE= -CONFIG_LOG_DEFAULT_LEVEL_ERROR=y -CONFIG_LOG_DEFAULT_LEVEL_WARN= -CONFIG_LOG_DEFAULT_LEVEL_INFO= -CONFIG_LOG_DEFAULT_LEVEL_DEBUG= -CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= -CONFIG_LOG_DEFAULT_LEVEL=1 diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s2/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/project.esp32s2/CMakeLists.txt deleted file mode 100644 index bd18c8ced4..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32s2/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -# The following five lines of boilerplate have to be in your project's -# CMakeLists in this exact order for cmake to work correctly -cmake_minimum_required(VERSION 3.5) - -set(IDF_TARGET "$ENV{ESP_VARIANT}") - -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(Sming) diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/CMakeLists.txt deleted file mode 100644 index cf2c455cb5..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -idf_component_register(SRCS "main.c" - INCLUDE_DIRS ".") diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/main.c b/Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/main.c deleted file mode 100644 index cb05851571..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32s2/main/main.c +++ /dev/null @@ -1,3 +0,0 @@ -void app_main(void) -{ -} diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s3/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/project.esp32s3/CMakeLists.txt deleted file mode 100644 index bd18c8ced4..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32s3/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -# The following five lines of boilerplate have to be in your project's -# CMakeLists in this exact order for cmake to work correctly -cmake_minimum_required(VERSION 3.5) - -set(IDF_TARGET "$ENV{ESP_VARIANT}") - -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -project(Sming) diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/CMakeLists.txt deleted file mode 100644 index cf2c455cb5..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -idf_component_register(SRCS "main.c" - INCLUDE_DIRS ".") diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/main.c b/Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/main.c deleted file mode 100644 index cb05851571..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32s3/main/main.c +++ /dev/null @@ -1,3 +0,0 @@ -void app_main(void) -{ -} diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.debug b/Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.debug deleted file mode 100644 index 8d660b03c2..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.debug +++ /dev/null @@ -1,37 +0,0 @@ -# -# Espressif IoT Development Framework (ESP-IDF) Project Configuration -# -# SMING DEBUG SETTINGS -# - -# -# SDK tool configuration -# - -# Mandatory LWIP changes -CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 - -# Mandatory Sming framework changes -CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 -CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 - -# The bootloader logs all type of messages -CONFIG_BOOTLOADER_LOG_LEVEL_NONE= -CONFIG_BOOTLOADER_LOG_LEVEL_ERROR= -CONFIG_BOOTLOADER_LOG_LEVEL_WARN= -CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y -CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= -CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= -CONFIG_BOOTLOADER_LOG_LEVEL=3 - -# ESP-IDF logs all type of messages -CONFIG_LOG_DEFAULT_LEVEL_NONE= -CONFIG_LOG_DEFAULT_LEVEL_ERROR= -CONFIG_LOG_DEFAULT_LEVEL_WARN= -CONFIG_LOG_DEFAULT_LEVEL_INFO=y -CONFIG_LOG_DEFAULT_LEVEL_DEBUG= -CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= -CONFIG_LOG_DEFAULT_LEVEL=3 - -# Sets watchpoint index 1 (the second of two) at the end of any task stack. This is the most accurate way to debug task stack overflows. -CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.release b/Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.release deleted file mode 100644 index c0346b7594..0000000000 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32s3/sdkconfig.defaults.release +++ /dev/null @@ -1,34 +0,0 @@ -# -# Espressif IoT Development Framework (ESP-IDF) Project Configuration -# -# SMING RELEASE SETTINGS -# - -# -# SDK tool configuration -# - -# Mandatory LWIP changes -CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 - -# Mandatory Sming framework changes -CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 -CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 - -# The bootloader logs only errors -CONFIG_BOOTLOADER_LOG_LEVEL_NONE= -CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y -CONFIG_BOOTLOADER_LOG_LEVEL_WARN= -CONFIG_BOOTLOADER_LOG_LEVEL_INFO= -CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= -CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= -CONFIG_BOOTLOADER_LOG_LEVEL=1 - -# ESP-IDF logs only errors -CONFIG_LOG_DEFAULT_LEVEL_NONE= -CONFIG_LOG_DEFAULT_LEVEL_ERROR=y -CONFIG_LOG_DEFAULT_LEVEL_WARN= -CONFIG_LOG_DEFAULT_LEVEL_INFO= -CONFIG_LOG_DEFAULT_LEVEL_DEBUG= -CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= -CONFIG_LOG_DEFAULT_LEVEL=1 diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/config/README.rst b/Sming/Arch/Esp32/Components/esp32/sdk/config/README.rst new file mode 100644 index 0000000000..404ff46990 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/sdk/config/README.rst @@ -0,0 +1,23 @@ +config +====== + +Default SDK project settings are contained in several files, +which are concatenated into the project's ``sdkconfig.default`` file. + +The following files are assembled, in order: + + common + {BUILD_TYPE} + {ESP_VARIANT}.common + {ESP_VARIANT}.{BUILD_TYPE} + {PROJECT_DIR}/{SDK_CUSTOM_CONFIG} + +Where: + +- ``{BUILD_TYPE}`` is ``debug`` or ``release``. +- ``SDK_CUSTOM_CONFIG`` is optional and is always relative to the project root directory. + +To provide additional settings for a specific variant use the variant prefix such +as ``esp32.debug``, ``esp32c3.release``, ``esp32s2.common``, etc. + +etc. diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/config/common b/Sming/Arch/Esp32/Components/esp32/sdk/config/common new file mode 100644 index 0000000000..929616cf60 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/sdk/config/common @@ -0,0 +1,28 @@ +# +# Espressif IoT Development Framework (ESP-IDF) Project Configuration +# +# SMING SETTINGS +# + +# +# Common to all ESP32 variants +# + +# +# SDK tool configuration +# + +CONFIG_COMPILER_OPTIMIZATION_SIZE=y + +# Mandatory LWIP changes +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 +CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER=n + +# Ethernet +CONFIG_ETH_USE_SPI_ETHERNET=y + +# Mandatory Sming framework changes +CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 +CONFIG_ESP_TIMER_IMPL_FRC2=y + diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s2/sdkconfig.defaults.debug b/Sming/Arch/Esp32/Components/esp32/sdk/config/debug similarity index 70% rename from Sming/Arch/Esp32/Components/esp32/project.esp32s2/sdkconfig.defaults.debug rename to Sming/Arch/Esp32/Components/esp32/sdk/config/debug index 8d660b03c2..14f1c0a1b3 100644 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32s2/sdkconfig.defaults.debug +++ b/Sming/Arch/Esp32/Components/esp32/sdk/config/debug @@ -1,20 +1,7 @@ # -# Espressif IoT Development Framework (ESP-IDF) Project Configuration -# -# SMING DEBUG SETTINGS -# - -# -# SDK tool configuration +# DEBUG # -# Mandatory LWIP changes -CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 - -# Mandatory Sming framework changes -CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 -CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 - # The bootloader logs all type of messages CONFIG_BOOTLOADER_LOG_LEVEL_NONE= CONFIG_BOOTLOADER_LOG_LEVEL_ERROR= @@ -35,3 +22,4 @@ CONFIG_LOG_DEFAULT_LEVEL=3 # Sets watchpoint index 1 (the second of two) at the end of any task stack. This is the most accurate way to debug task stack overflows. CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y + diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32s2/sdkconfig.defaults.release b/Sming/Arch/Esp32/Components/esp32/sdk/config/release similarity index 62% rename from Sming/Arch/Esp32/Components/esp32/project.esp32s2/sdkconfig.defaults.release rename to Sming/Arch/Esp32/Components/esp32/sdk/config/release index c0346b7594..f130372a9e 100644 --- a/Sming/Arch/Esp32/Components/esp32/project.esp32s2/sdkconfig.defaults.release +++ b/Sming/Arch/Esp32/Components/esp32/sdk/config/release @@ -1,20 +1,7 @@ # -# Espressif IoT Development Framework (ESP-IDF) Project Configuration -# -# SMING RELEASE SETTINGS -# - -# -# SDK tool configuration +# RELEASE # -# Mandatory LWIP changes -CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=8192 - -# Mandatory Sming framework changes -CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 -CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 - # The bootloader logs only errors CONFIG_BOOTLOADER_LOG_LEVEL_NONE= CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y @@ -32,3 +19,4 @@ CONFIG_LOG_DEFAULT_LEVEL_INFO= CONFIG_LOG_DEFAULT_LEVEL_DEBUG= CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= CONFIG_LOG_DEFAULT_LEVEL=1 + diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/misc.mk b/Sming/Arch/Esp32/Components/esp32/sdk/misc.mk index e117fafdeb..8783aef1c0 100644 --- a/Sming/Arch/Esp32/Components/esp32/sdk/misc.mk +++ b/Sming/Arch/Esp32/Components/esp32/sdk/misc.mk @@ -7,13 +7,10 @@ include $(SMING_HOME)/build.mk all: # List of all generated SDK libraries -SDK_ARCHIVE_DIRS = $(foreach c,$(SDK_COMPONENTS),$(SDK_BUILD_BASE)/esp-idf/$c) +SDK_ARCHIVE_DIRS = $(wildcard $(SDK_BUILD_BASE)/esp-idf/*) SDK_ARCHIVE_LIST = $(sort $(foreach d,$(SDK_ARCHIVE_DIRS),$(wildcard $d/*.a))) -# -# Need to suppress errors here as there are multiple copies of some libraries -# .PHONY: copylibs copylibs: $(info Copying generated SDK libraries) - $(Q) cp -u $(SDK_ARCHIVE_LIST) $(SDK_COMPONENT_LIBDIR)/ + $(Q) cp $(SDK_ARCHIVE_LIST) $(SDK_COMPONENT_LIBDIR)/ diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/sdk/project/CMakeLists.txt similarity index 100% rename from Sming/Arch/Esp32/Components/esp32/project.esp32/CMakeLists.txt rename to Sming/Arch/Esp32/Components/esp32/sdk/project/CMakeLists.txt diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32/main/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/sdk/project/main/CMakeLists.txt similarity index 100% rename from Sming/Arch/Esp32/Components/esp32/project.esp32/main/CMakeLists.txt rename to Sming/Arch/Esp32/Components/esp32/sdk/project/main/CMakeLists.txt diff --git a/Sming/Arch/Esp32/Components/esp32/project.esp32/main/main.c b/Sming/Arch/Esp32/Components/esp32/sdk/project/main/main.c similarity index 100% rename from Sming/Arch/Esp32/Components/esp32/project.esp32/main/main.c rename to Sming/Arch/Esp32/Components/esp32/sdk/project/main/main.c diff --git a/Sming/Arch/Esp32/Components/esp32/src/startup.cpp b/Sming/Arch/Esp32/Components/esp32/src/startup.cpp index 917276e375..d185c93ed7 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/startup.cpp +++ b/Sming/Arch/Esp32/Components/esp32/src/startup.cpp @@ -9,12 +9,8 @@ */ #include -#include -#include -#include #include #include -#include #include #include #include @@ -22,6 +18,15 @@ #include #include +#ifndef DISABLE_NETWORK +#include +#ifndef DISABLE_WIFI +#include +#include +#include +#endif +#endif + #ifndef ESP32_STACK_SIZE #define ESP32_STACK_SIZE 16384U #endif @@ -32,6 +37,7 @@ namespace { static constexpr uint32_t WDT_TIMEOUT_MS{8000}; +#ifndef DISABLE_WIFI void esp_init_flash() { esp_err_t ret = nvs_flash_init(); @@ -50,6 +56,7 @@ void esp_init_wifi() wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); } +#endif void main(void*) { @@ -61,8 +68,10 @@ void main(void*) smg_uart_detach_all(); +#ifndef DISABLE_WIFI esp_init_flash(); esp_init_wifi(); +#endif ets_init_tasks(); Storage::initialize(); System.initialize(); diff --git a/Sming/Arch/Esp32/README.rst b/Sming/Arch/Esp32/README.rst index abcc82a6a2..05831e40ba 100644 --- a/Sming/Arch/Esp32/README.rst +++ b/Sming/Arch/Esp32/README.rst @@ -1,6 +1,8 @@ Sming Esp32 Architecture ========================== +.. highlight:: bash + Support building Sming for the Esp32 architecture. @@ -11,11 +13,27 @@ Build variables This contains the base directory for the ESP-IDF toolchain used to build the framework. This variable is required and must be set accordingly. + +.. envvar:: SDK_CUSTOM_CONFIG + + Custom SDK settings for a project can be defined in a separate file + and setting this value to the location, relative to the project source root directory. + + These will be added to the default SDK settings. + + To make the settings current, you must run ``make sdk-config-clean``. + This will discard any changes made via ``make sdk-menuconfig``. + + Requirements ------------ In order to be able to compile for the ESP32 architecture you should have ESP-IDF v4.3 installed. -The Sming installers can do this for you - see :doc:`/getting-started/index`. +Some slight changes are required to enable code to compile correctly for C++, +so a fork has been created here https://github.com/mikee47/esp-idf/tree/sming/release%2Fv4.3 +which you may clone. + +The Sming installers do all this for you - see :doc:`/getting-started/index`. You can find further details in the `ESP-IDF documentation `__. @@ -49,19 +67,13 @@ Sming comes with pre-compiled libraries and configuration files. If needed you c A re-compilation is required after the change of the configuration. This can be done with the following command:: - make SMING_ARCH=Esp32 sdk-build - -If you want to revert to using the default pre-compiled SDK then issue the following command:: + make SMING_ARCH=Esp32 Sming-build all - make SMING_ARCH=Esp32 sdk-default +If you want to revert to using the default SDK settings then issue the following command:: -.. note:: + make SMING_ARCH=Esp32 sdk-config-clean - If you have an ESP32-S2 device you'll need to change :envvar:`ESP_VARIANT`:: - - make ESP_VARIANT=esp32s2 - -See :component-esp32:`esp32` for further details. +You can also configure per-project custom settings via :envvar:`SDK_CUSTOM_CONFIG`. Processor variants @@ -84,6 +96,7 @@ make SMING_ARCH=Esp32 clean components-clean make ESP_VARIANT=esp32c3 ``` +See :component-esp32:`esp32` for further details. Components diff --git a/Sming/Arch/Esp32/build.mk b/Sming/Arch/Esp32/build.mk index 553911d981..73b15f4107 100644 --- a/Sming/Arch/Esp32/build.mk +++ b/Sming/Arch/Esp32/build.mk @@ -99,6 +99,9 @@ endif IDF_PATH_LIST += ESP32_IDFEXE_PATH endif +DEBUG_VARS += NINJA +NINJA := $(if $(ESP32_NINJA_PATH),$(ESP32_NINJA_PATH)/,)ninja + space := space += diff --git a/Sming/Arch/Host/Components/hostlib/startup.cpp b/Sming/Arch/Host/Components/hostlib/startup.cpp index aad178fea9..71ccab74f4 100644 --- a/Sming/Arch/Host/Components/hostlib/startup.cpp +++ b/Sming/Arch/Host/Components/hostlib/startup.cpp @@ -39,7 +39,7 @@ #include #include -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK #include extern void host_wifi_lwip_init_complete(); static bool lwip_initialised; @@ -57,7 +57,7 @@ static void cleanup() host_flashmem_cleanup(); UartServer::shutdown(); sockets_finalise(); -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK host_lwip_shutdown(); #endif host_debug_i("Goodbye!"); @@ -117,7 +117,7 @@ void host_main_loop() { host_service_tasks(); host_service_timers(); -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK if(lwip_initialised && lwipServiceTimer.expired()) { host_lwip_service(); lwipServiceTimer.start(); @@ -137,7 +137,7 @@ int main(int argc, char* argv[]) bool enable_network; UartServer::Config uart; FlashmemConfig flash; -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK struct lwip_param lwip; #endif } config = { @@ -156,7 +156,7 @@ int main(int argc, char* argv[]) .createSize = 0, }, -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK .lwip = { .ifname = nullptr, @@ -200,7 +200,7 @@ int main(int argc, char* argv[]) config.uart.portBase = atoi(arg); break; -#ifdef DISABLE_WIFI +#ifdef DISABLE_NETWORK case opt_ifname: case opt_ipaddr: case opt_gateway: @@ -284,7 +284,7 @@ int main(int argc, char* argv[]) sockets_initialise(); UartServer::startup(config.uart); -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK if(config.enable_network) { lwip_initialised = host_lwip_init(&config.lwip); if(lwip_initialised) { @@ -304,7 +304,7 @@ int main(int argc, char* argv[]) host_init(); -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK lwipServiceTimer.reset(); #endif while(!done) { diff --git a/Sming/Arch/Host/System/include/user_config.h b/Sming/Arch/Host/System/include/user_config.h index e2f3dbcbcd..586fe2f944 100644 --- a/Sming/Arch/Host/System/include/user_config.h +++ b/Sming/Arch/Host/System/include/user_config.h @@ -10,7 +10,7 @@ #include // Network base API -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK #include #include #include diff --git a/Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp b/Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp index 826dfd9296..af6d8e7dad 100644 --- a/Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp +++ b/Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp @@ -25,6 +25,9 @@ bool EmbeddedEthernet::begin(const Config& config) #else + esp_netif_init(); + esp_event_loop_create_default(); + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); netif = esp_netif_new(&cfg); diff --git a/Sming/Core/SmingCore.h b/Sming/Core/SmingCore.h index d5bf98265a..9e06713097 100644 --- a/Sming/Core/SmingCore.h +++ b/Sming/Core/SmingCore.h @@ -33,10 +33,12 @@ #include "Platform/Timers.h" #include "Platform/WDT.h" +#ifndef DISABLE_NETWORK #ifndef DISABLE_WIFI #include "Platform/WifiEvents.h" #include "Platform/Station.h" #include "Platform/AccessPoint.h" +#endif #include "Network/DnsServer.h" #include "Network/HttpClient.h" @@ -52,7 +54,7 @@ #include "Network/TcpConnection.h" #include "Network/UdpConnection.h" #include "Network/Url.h" -#endif +#endif // DISABLE_NETWORK #include "Data/Stream/FileStream.h" #include "Data/Stream/TemplateFileStream.h" diff --git a/Sming/README.rst b/Sming/README.rst index 0fc35032ef..52b153693f 100644 --- a/Sming/README.rst +++ b/Sming/README.rst @@ -85,12 +85,10 @@ Localisation Networking ~~~~~~~~~~ -.. envvar:: DISABLE_WIFI +.. envvar:: DISABLE_NETWORK .. note:: - EXPERIMENTAL - 0 (Default) 1 - Remove core networking support @@ -101,6 +99,18 @@ Networking Builds will not succeeded if network code has been inadvertently included. +.. envvar:: DISABLE_WIFI + + .. note:: + + EXPERIMENTAL + + 0 (Default) + 1 - Exclude WiFi initialisation code + + Keeps the core :component:`Network` library but excludes WiFi code. + Applications using ethernet can use this to reduce code size. See :sample:`Basic_Ethernet`. + Components ---------- diff --git a/Sming/Services/CommandProcessing/CommandExecutor.cpp b/Sming/Services/CommandProcessing/CommandExecutor.cpp index 46c4c81ddc..fc61202431 100644 --- a/Sming/Services/CommandProcessing/CommandExecutor.cpp +++ b/Sming/Services/CommandProcessing/CommandExecutor.cpp @@ -21,7 +21,7 @@ CommandExecutor::CommandExecutor(Stream* reqStream) : CommandExecutor() } } -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK CommandExecutor::CommandExecutor(TcpClient* cmdClient) : CommandExecutor() { commandOutput = new CommandOutput(cmdClient); diff --git a/Sming/Services/CommandProcessing/CommandExecutor.h b/Sming/Services/CommandProcessing/CommandExecutor.h index a1be0932c6..9bf51f749d 100644 --- a/Sming/Services/CommandProcessing/CommandExecutor.h +++ b/Sming/Services/CommandProcessing/CommandExecutor.h @@ -11,7 +11,7 @@ #include "CommandOutput.h" #include -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK #include #endif @@ -20,7 +20,7 @@ class CommandExecutor { public: -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK CommandExecutor(TcpClient* cmdClient); CommandExecutor(WebsocketConnection* reqSocket); #endif diff --git a/Sming/Services/CommandProcessing/CommandHandler.cpp b/Sming/Services/CommandProcessing/CommandHandler.cpp index 752dd42a25..496a471525 100644 --- a/Sming/Services/CommandProcessing/CommandHandler.cpp +++ b/Sming/Services/CommandProcessing/CommandHandler.cpp @@ -99,7 +99,7 @@ void CommandHandler::procesStatusCommand(String commandLine, CommandOutput* comm commandOutput->print(_F("ESP SDK version : ")); commandOutput->print(system_get_sdk_version()); commandOutput->println(); -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK commandOutput->printf(_F("lwIP version : %d.%d.%d(%s)\r\n"), LWIP_VERSION_MAJOR, LWIP_VERSION_MINOR, LWIP_VERSION_REVISION, LWIP_HASH_STR); #endif diff --git a/Sming/Services/CommandProcessing/CommandOutput.cpp b/Sming/Services/CommandProcessing/CommandOutput.cpp index 66d97d8a5d..604ce4a3ed 100644 --- a/Sming/Services/CommandProcessing/CommandOutput.cpp +++ b/Sming/Services/CommandProcessing/CommandOutput.cpp @@ -23,7 +23,7 @@ size_t CommandOutput::write(uint8_t outChar) return outputStream->write(outChar); } -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK if(outputTcpClient) { char outBuf[1] = {char(outChar)}; return outputTcpClient->write(outBuf, 1); diff --git a/Sming/Services/CommandProcessing/CommandOutput.h b/Sming/Services/CommandProcessing/CommandOutput.h index b1f4cd4b12..774c672e7e 100644 --- a/Sming/Services/CommandProcessing/CommandOutput.h +++ b/Sming/Services/CommandProcessing/CommandOutput.h @@ -10,7 +10,7 @@ #include #include -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK #include #include #endif @@ -18,7 +18,7 @@ class CommandOutput : public Print { public: -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK CommandOutput(TcpClient* reqClient) : outputTcpClient(reqClient) { } @@ -34,7 +34,7 @@ class CommandOutput : public Print size_t write(uint8_t outChar); -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK TcpClient* outputTcpClient = nullptr; WebsocketConnection* outputSocket = nullptr; #endif diff --git a/Sming/System/include/debug_progmem.h b/Sming/System/include/debug_progmem.h index 90cbbbf670..71a011c383 100644 --- a/Sming/System/include/debug_progmem.h +++ b/Sming/System/include/debug_progmem.h @@ -54,6 +54,8 @@ extern "C" { do { \ } while(0) +extern uint32_t system_get_time(); + #if DEBUG_BUILD // http://stackoverflow.com/a/35441900 @@ -62,8 +64,6 @@ extern "C" { #define MACROQUOT(x) #x #define MACROQUOTE(x) MACROQUOT(x) -extern uint32_t system_get_time(); - //A static const char[] is defined having a unique name (log_ prefix, filename and line number) //This will be stored in the irom section(on flash) freeing up the RAM //Next special version of printf from FakePgmSpace is called to fetch and print the message diff --git a/Sming/Wiring/WiringFrameworkIncludes.h b/Sming/Wiring/WiringFrameworkIncludes.h index 8b40ba7660..660e7743d9 100644 --- a/Sming/Wiring/WiringFrameworkIncludes.h +++ b/Sming/Wiring/WiringFrameworkIncludes.h @@ -25,6 +25,6 @@ #include "Display.h" #include "WHashMap.h" -#ifndef DISABLE_WIFI +#ifndef DISABLE_NETWORK #include #endif diff --git a/Sming/component.mk b/Sming/component.mk index e597ce07c6..f97cb98ceb 100644 --- a/Sming/component.mk +++ b/Sming/component.mk @@ -45,13 +45,21 @@ ifeq ($(MQTT_NO_COMPAT),1) GLOBAL_CFLAGS += -DMQTT_NO_COMPAT=1 endif +# +RELINK_VARS += DISABLE_NETWORK +DISABLE_NETWORK ?= 0 +ifeq ($(DISABLE_NETWORK),1) +GLOBAL_CFLAGS += -DDISABLE_NETWORK=1 +DISABLE_WIFI := 1 +else +COMPONENT_DEPENDS += Network +endif + # RELINK_VARS += DISABLE_WIFI DISABLE_WIFI ?= 0 ifeq ($(DISABLE_WIFI),1) GLOBAL_CFLAGS += -DDISABLE_WIFI=1 -else -COMPONENT_DEPENDS += Network endif # WiFi settings may be provide via Environment variables diff --git a/docs/source/upgrading/4.3-4.4.rst b/docs/source/upgrading/4.3-4.4.rst index 6a0d1912f8..a6e32914e7 100644 --- a/docs/source/upgrading/4.3-4.4.rst +++ b/docs/source/upgrading/4.3-4.4.rst @@ -1,23 +1,26 @@ From v4.3 to v4.4 ================= -4.3.1 ------ - .. highlight:: c++ Network support ~~~~~~~~~~~~~~~ -The core network code has been moved out of ``Sming/Core/Network`` and into a separate component at ``Components/Network``. -This is to extend the use of the :envvar:`DISABLE_WIFI` setting to reduce the code required to be built for applications which -do not require networking. +If WiFi is not required then the :envvar:`DISABLE_WIFI` setting can be used to reduce code size. +This has a more pronounced effect for the ESP8266 which uses an experimental library. +The core network code has been moved out of ``Sming/Core/Network`` and into a separate component at ``Components/Network``. Some support code has been moved into ``Core/Data/WebHelpers``: applications should still build OK but you will get a compiler warning advising of the changes. Note that ``Network/WebHelpers/aw-sha1.h`` has been removed in favour of the :component:`crypto` library. +Ethernet support has been added, currently only for the ESP32 embedded MAC. +If WiFi is not used then the :envvar:`DISABLE_WIFI` setting can be used to reduce code size. + +The :envvar:`DISABLE_NETWORK` setting can be used to exclude all networking support for signficant code size reduction. + + ESP32 ~~~~~ diff --git a/samples/Basic_Ethernet/component.mk b/samples/Basic_Ethernet/component.mk new file mode 100644 index 0000000000..f7504e5dd6 --- /dev/null +++ b/samples/Basic_Ethernet/component.mk @@ -0,0 +1 @@ +DISABLE_WIFI := 1 From 06260738ae68e593199b7aa2202800b89bd0b7c1 Mon Sep 17 00:00:00 2001 From: Mike Date: Sat, 11 Sep 2021 12:36:01 +0100 Subject: [PATCH 054/130] Fix ArduinoJson6 serialisation bug and update from v6.13 to v6.15.2 (#2363) * Update ArduinoJson6 from v6.13 to v6.15.2 Contains bugfix where serializing to String returns wrong length. Note: Later releases require rework to FlashStringRefAdapter * Windows CI build broken --- Sming/Arch/Host/Tools/ci/install.cmd | 2 +- Sming/Libraries/ArduinoJson6/ArduinoJson | 2 +- docs/requirements.txt | 1 + tests/HostTests/modules/ArduinoJson6.cpp | 7 ++++++- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Sming/Arch/Host/Tools/ci/install.cmd b/Sming/Arch/Host/Tools/ci/install.cmd index 2e26df3ecd..b1218913be 100644 --- a/Sming/Arch/Host/Tools/ci/install.cmd +++ b/Sming/Arch/Host/Tools/ci/install.cmd @@ -3,7 +3,7 @@ REM Host install.cmd call :install "c:\tools\doxygen" doxygen-1.9.1.windows.bin.zip call :install "c:\tools" stable_windows_10_msbuild_Release_Win32_graphviz-2.46.1-win32.zip -python -m pip install --upgrade pip setuptools wheel +python -m pip install --upgrade pip wheel python -m pip install -r %SMING_HOME%/../docs/requirements.txt diff --git a/Sming/Libraries/ArduinoJson6/ArduinoJson b/Sming/Libraries/ArduinoJson6/ArduinoJson index 1b8107094f..6fb52c3638 160000 --- a/Sming/Libraries/ArduinoJson6/ArduinoJson +++ b/Sming/Libraries/ArduinoJson6/ArduinoJson @@ -1 +1 @@ -Subproject commit 1b8107094f00d89b8ef781a4ac71c5c2528ff63e +Subproject commit 6fb52c363849557c69485f97110371d0a4454432 diff --git a/docs/requirements.txt b/docs/requirements.txt index a73ee6596b..e65bfc6a39 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -8,3 +8,4 @@ sphinxcontrib-wavedrom sphinx-copybutton sphinxcontrib-seqdiag>=0.8.5 jinja2>=2.11.3 +setuptools==57.5.0 diff --git a/tests/HostTests/modules/ArduinoJson6.cpp b/tests/HostTests/modules/ArduinoJson6.cpp index 544d5a83a8..2bc19a64c9 100644 --- a/tests/HostTests/modules/ArduinoJson6.cpp +++ b/tests/HostTests/modules/ArduinoJson6.cpp @@ -84,8 +84,13 @@ class JsonTest6 : public TestGroup TEST_CASE("Json::serialize(doc, String), then save to file") { + DEFINE_FSTR_LOCAL(expectedOutput, + "{\"string1\":\"string value " + "1\",\"number2\":12345,\"arr\":[\"FlashString-1\"],\"FlashString-2\":\"FlashString-1\"}") String s; - REQUIRE(Json::serialize(doc, s) == 95); + auto sz = Json::serialize(doc, s); + REQUIRE_EQ(sz, 100U); + REQUIRE(expectedOutput == s); REQUIRE(fileSetContent(test_json, s) == int(s.length())); } From 3f63abb6a45a4d663b8af08d3c9f1dfe2dc6a01f Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 16 Sep 2021 09:27:15 +0100 Subject: [PATCH 055/130] ESP32 revisions to support esp32-c3 and s2 variants (#2365) This PR improves support for ESP32 soc variants with testing on real hardware: - ESP32 devkit V1 (ESP-WROOM32) - AI-thinker `esp-c3-32s` - AI-thinker `esp-12k` (esp32-s2) devkits **SDK build changes** SDK builds automatically if files or config changed. Ninja is very fast so low overhead. Create separate SDK project for debug/release builds. Use separate build directory for Esp32 variants, i.e. `{SMING_ARCH}/{ESP_VARIANT}`. Esp8266 & Host unchanged. Build only selected SDK components: reduces number of files to compile by about 1/3rd. Remove unused components, including VFS an SDK_FULL_BUILD option. Move SDK build targets into appropriate help categories Remove redundant `sdk-defconfig` target **Fixes** Fix `interrupts()` function to work with RISCV. `system_os_post()` must be in IRAM. Fix building for esp32c3 with DISABLE_WIFI=1. **Flashing** Fix flashing for esp32c3 & esp32s2 variants, variant must be passed to esptool. Fix issues with mismatched types, e.g. RISCV compiler for esp32c3 uses different type definitions. Route `_write_r` to `m_nputs` and set IDF logging function to `m_vprintf` **Rework uart driver using IDF HAL** Works reliably with esp32-s2 and c3 UART default pin allocations selected by variant Peripheral registers are mapped in two places on s2 (see tech. manual), need to use AHB_FIFO mappings. Use HAL to accommodate these differences. Ensure internal memory used for serial buffer Remove `uartHardware` structure, located in flash: use `GetDriver` which is OK for use in ISR. Register ISR to be called in interrupt context so it remains active whilst flash is disabled. **Enable GDB panic handler** Can now use regular `make gdb` if panic occurs. **Esp32 Framework changes** Remove redundant network code Fix `flashmem_get_address()` for all Esp32 variants Fix PROGMEM definition Only applies to DROM, not IROM so simplify `isFlashPtr` also Implement CPU frequency setting A bit more involved on the ESP32, requires power management to be enabled via SDK. **HostTests changes** Simplify `initorder` test Compile HttpRequest, TcpClient test modules only for Host Fix heap checking for shared pointers Move XorOutputStream from Network Component into gen. pop. - not particularly network-centric. Allows test code to compile with Clock tests sometimes generate interrupt WDT timeout on ESP32: don't leave interrupts disabled whilst printing to UART. --- .../driver/include/driver/hw_timer.h | 6 +- Sming/Arch/Esp32/Components/driver/uart.cpp | 364 +++----- .../Arch/Esp32/Components/esp32/component.mk | 142 +-- .../Esp32/Components/esp32/sdk/config/common | 8 + .../esp32/sdk/project/CMakeLists.txt | 1 + Sming/Arch/Esp32/Components/esp32/src/clk.c | 60 ++ .../Components/esp32/src/include/esp_clk.h | 17 +- .../esp32/src/include/esp_systemapi.h | 3 +- .../Esp32/Components/esp32/src/startup.cpp | 2 + .../Esp32/Components/esp32/src/system.cpp | 16 + .../Arch/Esp32/Components/esp32/src/tasks.cpp | 6 +- .../Esp32/Components/gdbstub/component.mk | 10 +- Sming/Arch/Esp32/Components/gdbstub/gdbcmds | 5 + Sming/Arch/Esp32/Components/libc/component.mk | 8 +- .../libc/{ => src}/include/assert.h | 0 .../libc/{ => src}/include/esp_attr.h | 0 .../libc/{ => src}/include/sys/pgmspace.h | 6 +- .../Esp32/Components/libc/src/replacements.c | 24 + .../Esp32/Components/spi_flash/flashmem.cpp | 39 +- Sming/Arch/Esp32/Platform/Clocks.h | 4 + Sming/Arch/Esp32/README.rst | 9 +- Sming/Arch/Esp32/System/include/user_config.h | 4 - Sming/Arch/Esp32/Tools/ci/build.run.sh | 3 - Sming/Arch/Esp32/app.mk | 7 +- Sming/Arch/Esp32/build.mk | 2 +- Sming/Arch/Esp8266/build.mk | 4 + Sming/Arch/Host/build.mk | 4 + .../Arch/Esp32/Platform/LwipAdapter.cpp | 876 ------------------ .../Network/Arch/Esp32/Platform/LwipAdapter.h | 71 -- .../arch_driver/src/SerialBuffer.cpp | 9 + Sming/Components/esptool/component.mk | 2 +- Sming/Components/terminal/component.mk | 2 +- .../Data/Stream/XorOutputStream.h | 0 Sming/Core/HardwareSerial.h | 8 +- Sming/Libraries/LittleFS | 2 +- Sming/Libraries/SmingTest | 2 +- Sming/Platform/Clocks.h | 5 + Sming/System/include/gdb/gdb_hooks.h | 4 +- Sming/build.mk | 23 +- Sming/project.mk | 16 +- docs/source/arch/esp32/debugging/index.rst | 17 +- docs/source/information/multitasking.rst | 3 + samples/Basic_Blink/README.rst | 15 +- samples/Basic_Serial/app/application.cpp | 14 +- .../{modules => Arch/Host}/HttpRequest.cpp | 3 - .../{modules => Arch/Host}/TcpClient.cpp | 2 - tests/HostTests/include/modules.h | 7 +- tests/HostTests/modules/Clocks.cpp | 46 +- tests/HostTests/modules/Libc.cpp | 31 +- tests/HostTests/modules/Stream.cpp | 12 +- 50 files changed, 503 insertions(+), 1421 deletions(-) create mode 100644 Sming/Arch/Esp32/Components/esp32/src/clk.c create mode 100644 Sming/Arch/Esp32/Components/gdbstub/gdbcmds rename Sming/Arch/Esp32/Components/libc/{ => src}/include/assert.h (100%) rename Sming/Arch/Esp32/Components/libc/{ => src}/include/esp_attr.h (100%) rename Sming/Arch/Esp32/Components/libc/{ => src}/include/sys/pgmspace.h (90%) create mode 100644 Sming/Arch/Esp32/Components/libc/src/replacements.c mode change 100755 => 100644 Sming/Arch/Esp32/Tools/ci/build.run.sh delete mode 100644 Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp delete mode 100644 Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.h rename Sming/{Components/Network/src => Core}/Data/Stream/XorOutputStream.h (100%) rename tests/HostTests/{modules => Arch/Host}/HttpRequest.cpp (97%) rename tests/HostTests/{modules => Arch/Host}/TcpClient.cpp (98%) diff --git a/Sming/Arch/Esp32/Components/driver/include/driver/hw_timer.h b/Sming/Arch/Esp32/Components/driver/include/driver/hw_timer.h index 5006b6c2f0..b0a7a7e577 100644 --- a/Sming/Arch/Esp32/Components/driver/include/driver/hw_timer.h +++ b/Sming/Arch/Esp32/Components/driver/include/driver/hw_timer.h @@ -10,7 +10,7 @@ #pragma once -#if defined(SUBARCH_ESP32) || defined(SUBARCH_ESP32S2) +#if defined(SUBARCH_ESP32) #define FRC_TIMER_ENABLED #endif @@ -158,8 +158,12 @@ __forceinline uint32_t hw_timer1_read(void) * *************************************/ +#ifdef FRC_TIMER_ENABLED constexpr uint32_t HW_TIMER2_CLKDIV = TIMER_CLKDIV_1; constexpr uint32_t HW_TIMER2_CLK = HW_TIMER_BASE_CLK >> HW_TIMER2_CLKDIV; +#else +constexpr uint32_t HW_TIMER2_CLK = 1000000; +#endif /** * @brief Read current timer2 value diff --git a/Sming/Arch/Esp32/Components/driver/uart.cpp b/Sming/Arch/Esp32/Components/driver/uart.cpp index 20acf62d43..72968a664e 100644 --- a/Sming/Arch/Esp32/Components/driver/uart.cpp +++ b/Sming/Arch/Esp32/Components/driver/uart.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include // These conflict with enumerated types defined in IDF - values are same though #undef UART_PARITY_NONE @@ -36,29 +38,59 @@ */ #define DEFAULT_RX_HEADROOM (32 - RX_FIFO_HEADROOM) -// Determines whether to use APB or REF_TICK as clock source -constexpr bool uart_use_apb_clock{true}; - static int s_uart_debug_nr = UART_NO; // Keep track of interrupt enable state for each UART static uint8_t isrMask; -struct smg_uart_hardware_t { - volatile uart_dev_t& dev; - const uart_signal_conn_t& conn; - uint8_t tx_pin_default; - uint8_t rx_pin_default; +struct smg_uart_pins_t { + uint8_t tx; + uint8_t rx; }; -constexpr smg_uart_hardware_t uartHardware[UART_COUNT] = { - {UART0, uart_periph_signal[0], 1, 3}, - {UART1, uart_periph_signal[1], 10, 9}, +#ifdef SUBARCH_ESP32 +#define UART0_PIN_DEFAULT UART_NUM_0_TXD_DIRECT_GPIO_NUM, UART_NUM_0_RXD_DIRECT_GPIO_NUM +#define UART1_PIN_DEFAULT 18, 19 // Defaults conflict with flash +#define UART2_PIN_DEFAULT UART_NUM_2_TXD_DIRECT_GPIO_NUM, UART_NUM_2_RXD_DIRECT_GPIO_NUM +#elif defined(SUBARCH_ESP32C3) +#define UART0_PIN_DEFAULT 21, 20 +#define UART1_PIN_DEFAULT UART_NUM_1_TXD_DIRECT_GPIO_NUM, UART_NUM_1_RXD_DIRECT_GPIO_NUM +#elif defined(SUBARCH_ESP32S2) +#define UART0_PIN_DEFAULT 43, 44 +#define UART1_PIN_DEFAULT UART_NUM_1_TXD_DIRECT_GPIO_NUM, UART_NUM_1_RXD_DIRECT_GPIO_NUM +#elif defined(SUBARCH_ESP32S3) +#define UART0_PIN_DEFAULT 43, 44 +#define UART1_PIN_DEFAULT UART_NUM_1_TXD_DIRECT_GPIO_NUM, UART_NUM_1_RXD_DIRECT_GPIO_NUM +#define UART2_PIN_DEFAULT UART_NUM_2_TXD_DIRECT_GPIO_NUM, UART_NUM_2_RXD_DIRECT_GPIO_NUM +#else +static_assert(false, "Must define default UART pins for selected ESP_VARIANT"); +#endif + +constexpr smg_uart_pins_t defaultPins[UART_COUNT] = { + {UART0_PIN_DEFAULT}, + {UART1_PIN_DEFAULT}, #if UART_COUNT > 2 - {UART2, uart_periph_signal[2], 17, 16}, + {UART2_PIN_DEFAULT}, #endif }; +uart_dev_t* IRAM_ATTR getDevice(uint8_t uart_nr) +{ + if(uart_nr == 0) { + return &UART0; + } + if(uart_nr == 1) { + return &UART1; + } +#if UART_COUNT > 2 + if(uart_nr == 2) { + return &UART2; + } +#endif + assert(false); + return nullptr; +} + // Keep a reference to all created UARTS struct smg_uart_instance_t { smg_uart_t* uart; @@ -68,34 +100,22 @@ struct smg_uart_instance_t { static smg_uart_instance_t uartInstances[UART_COUNT]; -#if SUBARCH_ESP32 -#define FIFO(dev) dev.fifo.rw_byte -#else -#define FIFO(dev) dev.ahb_fifo.rw_byte -#endif - -// Get number of characters in receive FIFO -__forceinline static uint8_t uart_rxfifo_count(uint8_t nr) -{ - return uartHardware[nr].dev.status.rxfifo_cnt; -} - // Get number of characters in transmit FIFO -__forceinline static uint8_t uart_txfifo_count(uint8_t nr) +__forceinline static size_t uart_txfifo_count(uart_dev_t* dev) { - return uartHardware[nr].dev.status.txfifo_cnt; + return dev->status.txfifo_cnt; } // Get available free characters in transmit FIFO -__forceinline static uint8_t uart_txfifo_free(uint8_t nr) +__forceinline static size_t uart_txfifo_free(uart_dev_t* dev) { - return UART_TX_FIFO_SIZE - uart_txfifo_count(nr) - 1; + return UART_TX_FIFO_SIZE - uart_txfifo_count(dev) - 1; } // Return true if transmit FIFO is full -__forceinline static bool uart_txfifo_full(uint8_t nr) +__forceinline static bool uart_txfifo_full(uart_dev_t* dev) { - return uart_txfifo_count(nr) >= (UART_TX_FIFO_SIZE - 1); + return uart_txfifo_count(dev) >= (UART_TX_FIFO_SIZE - 1); } /** @brief Invoke a port callback, if one has been registered @@ -189,7 +209,9 @@ static bool realloc_buffer(SerialBuffer*& buffer, size_t new_size) return true; } - auto new_buf = new SerialBuffer; + // Avoid allocating in SPIRAM + auto mem = heap_caps_malloc(sizeof(SerialBuffer), MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL); + auto new_buf = new(mem) SerialBuffer; if(new_buf != nullptr && new_buf->resize(new_size) == new_size) { buffer = new_buf; return true; @@ -264,22 +286,14 @@ size_t smg_uart_read(smg_uart_t* uart, void* buffer, size_t size) // Top up from hardware FIFO if(is_physical(uart)) { - auto& hw = uartHardware[uart->uart_nr]; - while(read < size && uart_rxfifo_count(uart->uart_nr) != 0) { - buf[read++] = FIFO(hw.dev); - } + auto dev = getDevice(uart->uart_nr); + auto len = std::min(uint32_t(size - read), uart_ll_get_rxfifo_len(dev)); + uart_ll_read_rxfifo(dev, &buf[read], len); + read += len; // FIFO full may have been disabled if buffer overflowed, re-enabled it now - decltype(uart_dev_t::int_clr) clr{}; - clr.rxfifo_full = true; - clr.rxfifo_tout = true; - clr.rxfifo_ovf = true; - hw.dev.int_clr.val = clr.val; - decltype(uart_dev_t::int_ena) ena{}; - ena.rxfifo_full = true; - ena.rxfifo_tout = true; - ena.rxfifo_ovf = true; - hw.dev.int_ena.val = ena.val; + uart_ll_clr_intsts_mask(dev, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT | UART_INTR_RXFIFO_OVF); + uart_ll_ena_intr_mask(dev, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT | UART_INTR_RXFIFO_OVF); } return read; @@ -293,7 +307,8 @@ size_t smg_uart_rx_available(smg_uart_t* uart) smg_uart_disable_interrupts(); - size_t avail = is_physical(uart) ? uart_rxfifo_count(uart->uart_nr) : 0; + auto dev = getDevice(uart->uart_nr); + size_t avail = is_physical(uart) ? uart_ll_get_rxfifo_len(dev) : 0; if(uart->rx_buffer != nullptr) { avail += uart->rx_buffer->available(); @@ -314,11 +329,10 @@ static void IRAM_ATTR uart_isr(smg_uart_instance_t* inst) } auto uart = inst->uart; - auto uart_nr = uart->uart_nr; - auto& hw = uartHardware[uart_nr]; + auto dev = getDevice(uart->uart_nr); decltype(uart_dev_t::int_st) usis; - usis.val = hw.dev.int_st.val; + usis.val = dev->int_st.val; // If status is clear there's no interrupt to service on this UART if(usis.val == 0) { @@ -336,13 +350,15 @@ static void IRAM_ATTR uart_isr(smg_uart_instance_t* inst) // Read as much data as possible from the RX FIFO into buffer if(uart->rx_buffer != nullptr) { - size_t avail = uart_rxfifo_count(uart_nr); + size_t avail = uart_ll_get_rxfifo_len(dev); size_t space = uart->rx_buffer->getFreeSpace(); read = (avail <= space) ? avail : space; space -= read; + uint8_t buf[UART_RX_FIFO_SIZE]; + uart_ll_read_rxfifo(dev, buf, read); + uint8_t* ptr = buf; while(read-- != 0) { - uint8_t c = FIFO(hw.dev); - uart->rx_buffer->writeChar(c); + uart->rx_buffer->writeChar(*ptr++); } // Don't call back until buffer is (almost) full @@ -356,13 +372,9 @@ static void IRAM_ATTR uart_isr(smg_uart_instance_t* inst) * The interrupt gets re-enabled by a call to uart_read() or uart_flush() */ if(usis.rxfifo_ovf) { - hw.dev.int_ena.rxfifo_ovf = false; + uart_ll_disable_intr_mask(dev, UART_INTR_RXFIFO_OVF); } else if(read == 0) { - decltype(uart_dev_t::int_ena) ena{}; - ena.val = hw.dev.int_ena.val; - ena.rxfifo_full = true; - ena.rxfifo_tout = true; - hw.dev.int_ena.val = ena.val; + uart_ll_ena_intr_mask(dev, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT); } } @@ -370,18 +382,20 @@ static void IRAM_ATTR uart_isr(smg_uart_instance_t* inst) if(usis.txfifo_empty) { // Dump as much data as we can from buffer into the TX FIFO if(uart->tx_buffer != nullptr) { - size_t space = uart_txfifo_free(uart_nr); + size_t space = uart_txfifo_free(dev); size_t avail = uart->tx_buffer->available(); - size_t count = (avail <= space) ? avail : space; - while(count-- != 0) { - FIFO(hw.dev) = uart->tx_buffer->readChar(); + size_t count = std::min(avail, space); + uint8_t buf[count]; + for(unsigned i = 0; i < count; ++i) { + buf[i] = uart->tx_buffer->readChar(); } + uart_ll_write_txfifo(dev, buf, count); } // If TX FIFO remains empty then we must disable TX FIFO EMPTY interrupt to stop it recurring. - if(uart_txfifo_count(uart_nr) == 0) { + if(uart_txfifo_count(dev) == 0) { // The interrupt gets re-enabled by uart_write() - hw.dev.int_ena.txfifo_empty = false; + uart_ll_disable_intr_mask(dev, UART_INTR_TXFIFO_EMPTY); } else { // We've topped up TX FIFO so defer callback until next time status.txfifo_empty = false; @@ -397,7 +411,7 @@ static void IRAM_ATTR uart_isr(smg_uart_instance_t* inst) } // Final step is to clear status flags - hw.dev.int_clr.val = usis.val; + dev->int_clr.val = usis.val; } void smg_uart_start_isr(smg_uart_t* uart) @@ -406,19 +420,14 @@ void smg_uart_start_isr(smg_uart_t* uart) return; } - decltype(uart_dev_t::conf1) conf1{}; decltype(uart_dev_t::int_ena) int_ena{}; - auto& hw = uartHardware[uart->uart_nr]; + auto dev = getDevice(uart->uart_nr); + dev->conf1.val = 0; if(smg_uart_rx_enabled(uart)) { - conf1.rxfifo_full_thrhd = 120; -#if SUBARCH_ESP32 - conf1.rx_tout_thrhd = 2; -#else - hw.dev.mem_conf.rx_tout_thrhd = 2; -#endif - conf1.rx_tout_en = true; + uart_ll_set_rxfifo_full_thr(dev, 120); + uart_ll_set_rx_tout(dev, 10); /* * There is little benefit in generating interrupts on errors, instead these @@ -441,16 +450,16 @@ void smg_uart_start_isr(smg_uart_t* uart) */ // TX FIFO empty interrupt only gets enabled via uart_write function() - // conf1.txfifo_empty_thrhd = 0; + uart_ll_set_txfifo_empty_thr(dev, 10); } - hw.dev.conf1.val = conf1.val; - hw.dev.int_clr.val = 0x0007ffff; - hw.dev.int_ena.val = int_ena.val; + dev->int_clr.val = 0x0007ffff; + dev->int_ena.val = int_ena.val; smg_uart_disable_interrupts(); auto& inst = uartInstances[uart->uart_nr]; - esp_intr_alloc(hw.conn.irq, ESP_INTR_FLAG_LOWMED, intr_handler_t(uart_isr), &inst, &inst.handle); + auto& conn = uart_periph_signal[uart->uart_nr]; + esp_intr_alloc(conn.irq, ESP_INTR_FLAG_IRAM, intr_handler_t(uart_isr), &inst, &inst.handle); smg_uart_restore_interrupts(); bitSet(isrMask, uart->uart_nr); } @@ -471,13 +480,13 @@ size_t smg_uart_write(smg_uart_t* uart, const void* buffer, size_t size) if(isPhysical) { // If TX buffer not in use or it's empty then write directly to hardware FIFO if(uart->tx_buffer == nullptr || uart->tx_buffer->isEmpty()) { - auto& hw = uartHardware[uart->uart_nr]; - while(written < size && !uart_txfifo_full(uart->uart_nr)) { - FIFO(hw.dev) = buf[written++]; - } + auto dev = getDevice(uart->uart_nr); + auto len = std::min(size - written, uart_txfifo_free(dev)); + uart_ll_write_txfifo(dev, &buf[written], len); + written += len; // Enable TX FIFO EMPTY interrupt - hw.dev.int_clr.txfifo_empty = true; - hw.dev.int_ena.txfifo_empty = true; + uart_ll_clr_intsts_mask(dev, UART_INTR_TXFIFO_EMPTY); + uart_ll_ena_intr_mask(dev, UART_INTR_TXFIFO_EMPTY); } } @@ -506,7 +515,7 @@ size_t smg_uart_tx_free(smg_uart_t* uart) smg_uart_disable_interrupts(); - size_t space = is_physical(uart) ? uart_txfifo_free(uart->uart_nr) : 0; + size_t space = is_physical(uart) ? uart_txfifo_free(getDevice(uart->uart_nr)) : 0; if(uart->tx_buffer != nullptr) { space += uart->tx_buffer->getFreeSpace(); } @@ -531,7 +540,8 @@ void smg_uart_wait_tx_empty(smg_uart_t* uart) } if(is_physical(uart)) { - while(uart_txfifo_count(uart->uart_nr) != 0) + auto dev = getDevice(uart->uart_nr); + while(uart_txfifo_count(dev) != 0) system_soft_wdt_feed(); } } @@ -540,8 +550,8 @@ void smg_uart_set_break(smg_uart_t* uart, bool state) { uart = get_physical(uart); if(uart != nullptr) { - auto& hw = uartHardware[uart->uart_nr]; - hw.dev.conf0.txd_brk = state; + auto dev = getDevice(uart->uart_nr); + dev->conf0.txd_brk = state; } } @@ -559,15 +569,15 @@ uint8_t smg_uart_get_status(smg_uart_t* uart) // Read raw status register directly from real uart, masking out non-error bits uart = get_physical(uart); if(uart != nullptr) { - auto& hw = uartHardware[uart->uart_nr]; + auto dev = getDevice(uart->uart_nr); decltype(uart_dev_t::int_raw) int_raw; - int_raw.val = hw.dev.int_raw.val; + int_raw.val = dev->int_raw.val; status.brk_det |= int_raw.brk_det; status.rxfifo_ovf |= int_raw.rxfifo_ovf; status.frm_err |= int_raw.frm_err; status.parity_err |= int_raw.parity_err; // Clear errors - hw.dev.int_clr.val = status.val; + dev->int_clr.val = status.val; } smg_uart_restore_interrupts(); } @@ -594,45 +604,24 @@ void smg_uart_flush(smg_uart_t* uart, smg_uart_mode_t mode) } if(is_physical(uart)) { - auto& hw = uartHardware[uart->uart_nr]; + auto dev = getDevice(uart->uart_nr); if(flushTx) { // Prevent TX FIFO EMPTY interrupts - don't need them until uart_write is called again - hw.dev.int_ena.txfifo_empty = false; - hw.dev.conf0.txfifo_rst = true; - hw.dev.conf0.txfifo_rst = false; + uart_ll_disable_intr_mask(dev, UART_INTR_TXFIFO_EMPTY); + uart_ll_txfifo_rst(dev); } // If receive overflow occurred then these interrupts will be masked if(flushRx) { -#if SUBARCH_ESP32 - // Hardware issue: we can not use `rxfifo_rst` to reset the hw rxfifo - while(true) { - auto fifo_cnt = hw.dev.status.rxfifo_cnt; - decltype(uart_dev_t::mem_rx_status) stat; - stat.val = hw.dev.mem_rx_status.val; - if(fifo_cnt == 0 && (stat.rd_addr == stat.wr_addr)) { - break; - } - - (void)FIFO(hw.dev); - } -#else - hw.dev.conf0.rxfifo_rst = true; - hw.dev.conf0.rxfifo_rst = false; -#endif + uart_ll_rxfifo_rst(dev); decltype(uart_dev_t::int_clr) int_clr; int_clr.val = 0x0007ffff; int_clr.txfifo_empty = false; // Leave this one - hw.dev.int_clr.val = int_clr.val; - - decltype(uart_dev_t::int_ena) int_ena; - int_ena.val = hw.dev.int_ena.val; - int_ena.rxfifo_full = true; - int_ena.rxfifo_tout = true; - int_ena.rxfifo_ovf = true; - hw.dev.int_ena.val = int_ena.val; + dev->int_clr.val = int_clr.val; + + uart_ll_ena_intr_mask(dev, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT | UART_INTR_RXFIFO_OVF); } } @@ -645,27 +634,13 @@ uint32_t smg_uart_set_baudrate_reg(int uart_nr, uint32_t baud_rate) return 0; } - auto& hw = uartHardware[uart_nr]; + auto dev = getDevice(uart_nr); -#if SOC_UART_SUPPORT_XTAL_CLK -#define UART_ALT_CLK UART_SCLK_XTAL -#define UART_ALT_CLK_FREQ XTAL_CLK_FREQ -#else -#define UART_ALT_CLK UART_SCLK_REF_TICK -#define UART_ALT_CLK_FREQ REF_CLK_FREQ -#endif - - uint32_t sclk_freq = uart_use_apb_clock ? APB_CLK_FREQ : UART_ALT_CLK_FREQ; - uint32_t clk_div = 16U * sclk_freq / baud_rate; - // The baud-rate configuration register is divided into - // an integer part and a fractional part. - hw.dev.clk_div.div_int = clk_div / 16U; - hw.dev.clk_div.div_frag = clk_div % 16U; - // Configure the UART source clock. - uart_ll_set_sclk(&hw.dev, uart_use_apb_clock ? UART_SCLK_APB : UART_ALT_CLK); + uart_ll_set_sclk(dev, UART_SCLK_APB); + uart_ll_set_baudrate(dev, baud_rate); // Return the actual baud rate in use - return 16U * sclk_freq / clk_div; + return uart_ll_get_baudrate(dev); } uint32_t smg_uart_set_baudrate(smg_uart_t* uart, uint32_t baud_rate) @@ -694,14 +669,12 @@ smg_uart_t* smg_uart_init_ex(const smg_uart_config_t& cfg) return nullptr; } - auto uart = new smg_uart_t; + auto mem = heap_caps_malloc(sizeof(smg_uart_t), MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL); + auto uart = new(mem) smg_uart_t{}; if(uart == nullptr) { return nullptr; } - auto& hw = uartHardware[cfg.uart_nr]; - - memset(uart, 0, sizeof(smg_uart_t)); uart->uart_nr = cfg.uart_nr; uart->mode = cfg.mode; uart->options = cfg.options; @@ -718,15 +691,9 @@ smg_uart_t* smg_uart_init_ex(const smg_uart_config_t& cfg) delete uart; return nullptr; } - - // HardwareSerial default pin is 1 for all ports - check - if(cfg.uart_nr != 0 && tx_pin == 1) { - tx_pin = hw.tx_pin_default; - } else { - tx_pin = (tx_pin == UART_PIN_DEFAULT) ? hw.tx_pin_default : cfg.tx_pin; - } + rx_pin = (cfg.rx_pin == UART_PIN_DEFAULT) ? defaultPins[cfg.uart_nr].rx : cfg.rx_pin; } else { - tx_pin = UART_PIN_NO_CHANGE; + rx_pin = UART_PIN_NO_CHANGE; } if(smg_uart_tx_enabled(uart)) { @@ -735,21 +702,35 @@ smg_uart_t* smg_uart_init_ex(const smg_uart_config_t& cfg) delete uart; return nullptr; } - - rx_pin = (cfg.rx_pin == UART_PIN_DEFAULT) ? hw.rx_pin_default : cfg.rx_pin; + tx_pin = (tx_pin == UART_PIN_DEFAULT) ? defaultPins[cfg.uart_nr].tx : cfg.tx_pin; } else { - rx_pin = UART_PIN_NO_CHANGE; + tx_pin = UART_PIN_NO_CHANGE; } // OK, buffers allocated so setup hardware smg_uart_detach(cfg.uart_nr); smg_uart_set_pins(uart, tx_pin, rx_pin); - periph_module_reset(hw.conn.module); - periph_module_enable(hw.conn.module); + auto& conn = uart_periph_signal[cfg.uart_nr]; + + periph_module_enable(conn.module); + + auto dev = getDevice(cfg.uart_nr); + +// Workaround for ESP32C3: enable core reset before enabling uart module clock to prevent uart output garbage value. +#if SOC_UART_REQUIRE_CORE_RESET + uart_ll_set_reset_core(dev, true); + periph_module_reset(conn.module); + uart_ll_set_reset_core(dev, false); +#else + periph_module_reset(conn.module); +#endif + + uart_ll_set_mode(dev, UART_MODE_UART); + uart_ll_set_tx_idle_num(dev, 0); // Bottom 8 bits identical to esp8266 - hw.dev.conf0.val = (hw.dev.conf0.val & 0xFFFFFF00) | cfg.config; + dev->conf0.val = (dev->conf0.val & 0xFFFFFF00) | cfg.config; smg_uart_set_baudrate(uart, cfg.baudrate); smg_uart_flush(uart); @@ -775,8 +756,8 @@ void smg_uart_uninit(smg_uart_t* uart) smg_uart_set_debug(UART_NO); } - auto& hw = uartHardware[uart->uart_nr]; - periph_module_disable(hw.conn.module); + auto& conn = uart_periph_signal[uart->uart_nr]; + periph_module_disable(conn.module); uartInstances[uart->uart_nr].uart = nullptr; delete uart->rx_buffer; @@ -801,50 +782,7 @@ smg_uart_t* smg_uart_init(uint8_t uart_nr, uint32_t baudrate, uint32_t config, s void smg_uart_swap(smg_uart_t* uart, int tx_pin) { - /* - if(uart == nullptr) { - return; - } - - switch(uart->uart_nr) { - case UART0: - uart0_pin_restore(uart->tx_pin); - uart0_pin_restore(uart->rx_pin); - - if(uart->tx_pin == 1 || uart->tx_pin == 2 || uart->rx_pin == 3) { - if(smg_uart_tx_enabled(uart)) { - uart->tx_pin = 15; - } - - if(smg_uart_rx_enabled(uart)) { - uart->rx_pin = 13; - } - - SET_PERI_REG_MASK(UART_SWAP_REG, UART_SWAP0); - } else { - if(smg_uart_tx_enabled(uart)) { - uart->tx_pin = (tx_pin == 2) ? 2 : 1; - } - - if(smg_uart_rx_enabled(uart)) { - uart->rx_pin = 3; - } - - CLEAR_PERI_REG_MASK(UART_SWAP_REG, UART_SWAP0); - } - - uart0_pin_select(uart->tx_pin); - uart0_pin_select(uart->rx_pin); - break; - - case UART1: - // Currently no swap possible! See GPIO pins used by UART - break; - - default: - break; - } -*/ + // Not implemented } bool smg_uart_set_tx(smg_uart_t* uart, int tx_pin) @@ -866,12 +804,12 @@ bool smg_uart_set_pins(smg_uart_t* uart, int tx_pin, int rx_pin) return false; } - auto& hw = uartHardware[uart->uart_nr]; + auto& conn = uart_periph_signal[uart->uart_nr]; if(tx_pin != UART_PIN_NO_CHANGE) { PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[tx_pin], PIN_FUNC_GPIO); gpio_set_level(gpio_num_t(tx_pin), true); - gpio_matrix_out(tx_pin, hw.conn.tx_sig, false, false); + gpio_matrix_out(tx_pin, conn.tx_sig, false, false); uart->tx_pin = tx_pin; } @@ -879,7 +817,7 @@ bool smg_uart_set_pins(smg_uart_t* uart, int tx_pin, int rx_pin) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[rx_pin], PIN_FUNC_GPIO); gpio_set_pull_mode(gpio_num_t(rx_pin), GPIO_PULLUP_ONLY); gpio_set_direction(gpio_num_t(rx_pin), GPIO_MODE_INPUT); - gpio_matrix_in(rx_pin, hw.conn.rx_sig, false); + gpio_matrix_in(rx_pin, conn.rx_sig, false); } return true; @@ -921,10 +859,10 @@ void smg_uart_detach(int uart_nr) bitClear(isrMask, uart_nr); } - auto& hw = uartHardware[uart_nr]; - hw.dev.conf1.val = 0; - hw.dev.int_clr.val = 0x0007ffff; - hw.dev.int_ena.val = 0; + auto dev = getDevice(uart_nr); + dev->conf1.val = 0; + dev->int_clr.val = 0x0007ffff; + dev->int_ena.val = 0; smg_uart_restore_interrupts(); } @@ -937,10 +875,10 @@ void smg_uart_detach_all() esp_intr_free(inst.handle); inst.handle = nullptr; } - auto& hw = uartHardware[uart_nr]; - hw.dev.conf1.val = 0; - hw.dev.int_clr.val = 0x0007ffff; - hw.dev.int_ena.val = 0; + auto dev = getDevice(uart_nr); + dev->conf1.val = 0; + dev->int_clr.val = 0x0007ffff; + dev->int_ena.val = 0; } isrMask = 0; } diff --git a/Sming/Arch/Esp32/Components/esp32/component.mk b/Sming/Arch/Esp32/Components/esp32/component.mk index d1347f05e2..70ccb9f217 100644 --- a/Sming/Arch/Esp32/Components/esp32/component.mk +++ b/Sming/Arch/Esp32/Components/esp32/component.mk @@ -7,17 +7,13 @@ COMPONENT_DEPENDS := libc COMPONENT_SRCDIRS := src COMPONENT_INCDIRS := src/include include -# Set to build all components, otherwise just the core ones -CACHE_VARS += SDK_FULL_BUILD -SDK_FULL_BUILD ?= 0 - # Applications can provide file with custom SDK configuration settings CACHE_VARS += SDK_CUSTOM_CONFIG COMPONENT_RELINK_VARS += DISABLE_NETWORK DISABLE_WIFI -SDK_BUILD_BASE := $(COMPONENT_BUILD_BASE)/$(ESP_VARIANT)/sdk -SDK_COMPONENT_LIBDIR := $(COMPONENT_BUILD_BASE)/$(ESP_VARIANT)/lib +SDK_BUILD_BASE := $(COMPONENT_BUILD_BASE)/sdk +SDK_COMPONENT_LIBDIR := $(COMPONENT_BUILD_BASE)/lib SDKCONFIG_H := $(SDK_BUILD_BASE)/config/sdkconfig.h @@ -41,12 +37,10 @@ SDK_INCDIRS := \ bootloader_support/include_bootloader \ driver/$(ESP_VARIANT)/include \ driver/include \ - efuse/include \ - efuse/$(ESP_VARIANT)/include \ + esp_pm/include \ esp_rom/include/$(ESP_VARIANT) \ esp_rom/include \ $(ESP_VARIANT)/include \ - espcoredump/include \ esp_timer/include \ soc/include \ soc/$(ESP_VARIANT)/include \ @@ -54,48 +48,24 @@ SDK_INCDIRS := \ log/include \ nvs_flash/include \ freertos/include \ - esp_ringbuf/include \ esp_event/include \ - tcpip_adapter/include \ lwip/lwip/src/include \ lwip/port/esp32/include \ - lwip/include/apps \ - lwip/include/apps/sntp \ - mbedtls/mbedtls/include \ - mbedtls/port/include \ - mdns/include \ - mdns/private_include \ + newlib/platform_include \ spi_flash/include \ - ulp/include \ - vfs/include \ - xtensa-debug-module/include \ wpa_supplicant/include \ wpa_supplicant/port/include \ - app_trace/include \ - app_update/include \ - smartconfig_ack/include \ esp_hw_support/include \ hal/include \ hal/$(ESP_VARIANT)/include \ - esp_system/include - -ifeq ($(SDK_FULL_BUILD),1) -SDK_INCDIRS += \ - console \ - pthread/include \ - sdmmc/include -endif - -SDK_INCDIRS += \ + esp_system/include \ esp_common/include \ esp_adc_cal/include \ esp_netif/include \ esp_eth/include \ - esp_event/private_include \ esp_wifi/include \ esp_wifi/esp32/include \ lwip/include/apps/sntp \ - spi_flash/private_include \ wpa_supplicant/include/esp_supplicant ifdef IDF_TARGET_ARCH_RISCV @@ -109,33 +79,6 @@ SDK_INCDIRS += \ freertos/port/xtensa/include endif -ifeq ($(CONFIG_BT_NIMBLE_ENABLED),y) -SDK_INCDIRS += \ - bt/include \ - bt/common/osi/include \ - bt/common/btc/include \ - bt/common/include \ - bt/host/nimble/nimble/porting/nimble/include \ - bt/host/nimble/port/include \ - bt/host/nimble/nimble/nimble/include \ - bt/host/nimble/nimble/nimble/host/include \ - bt/host/nimble/nimble/nimble/host/services/ans/include \ - bt/host/nimble/nimble/nimble/host/services/bas/include \ - bt/host/nimble/nimble/nimble/host/services/gap/include \ - bt/host/nimble/nimble/nimble/host/services/gatt/include \ - bt/host/nimble/nimble/nimble/host/services/ias/include \ - bt/host/nimble/nimble/nimble/host/services/lls/include \ - bt/host/nimble/nimble/nimble/host/services/tps/include \ - bt/host/nimble/nimble/nimble/host/util/include \ - bt/host/nimble/nimble/nimble/host/store/ram/include \ - bt/host/nimble/nimble/nimble/host/store/config/include \ - bt/host/nimble/nimble/porting/npl/freertos/include \ - bt/host/nimble/nimble/ext/tinycrypt/include \ - bt/host/nimble/esp-hci/include -endif - -SDK_INCDIRS += \ - newlib/platform_include COMPONENT_INCDIRS += \ $(dir $(SDKCONFIG_H)) \ @@ -148,7 +91,6 @@ SDK_COMPONENTS := \ cxx \ driver \ efuse \ - esp-tls \ $(ESP_VARIANT) \ esp_common \ esp_event \ @@ -156,7 +98,6 @@ SDK_COMPONENTS := \ esp_hw_support \ esp_ipc \ esp_pm \ - esp_ringbuf \ esp_rom \ esp_system \ esp_timer \ @@ -167,12 +108,9 @@ SDK_COMPONENTS := \ log \ newlib \ nvs_flash \ - protobuf-c \ - protocomm \ pthread \ soc \ - spi_flash \ - vfs + spi_flash ifneq ($(DISABLE_NETWORK),1) SDK_COMPONENTS += \ @@ -200,34 +138,6 @@ else SDK_COMPONENTS += xtensa endif -ifeq ($(SDK_FULL_BUILD),1) -SDK_COMPONENTS += \ - app_trace \ - asio \ - bt \ - coap \ - console \ - esp_http_client \ - esp_http_server \ - esp_https_ota \ - esp_local_ctrl \ - esp_websocket_client \ - expat \ - fatfs \ - freemodbus \ - idf_test \ - jsmn \ - json \ - libsodium \ - mdns \ - mqtt \ - nghttp \ - sdmmc \ - ulp \ - unity \ - wear_levelling -endif - SDK_ESP_WIFI_LIBS := \ coexist \ core \ @@ -306,14 +216,8 @@ EXTRA_LDFLAGS := \ -Wl,--undefined=uxTopUsedPriority \ $(LDFLAGS_$(ESP_VARIANT)) -FLASH_BOOT_LOADER := $(SDK_BUILD_BASE)/bootloader/bootloader.bin -FLASH_BOOT_CHUNKS := 0x1000=$(FLASH_BOOT_LOADER) - SDK_DEFAULT_PATH := $(COMPONENT_PATH)/sdk - -##@SDK - -SDK_PROJECT_PATH := $(COMPONENT_PATH)/project/$(ESP_VARIANT) +SDK_PROJECT_PATH := $(COMPONENT_PATH)/project/$(ESP_VARIANT)/$(BUILD_TYPE) SDK_CONFIG_DEFAULTS := $(SDK_PROJECT_PATH)/sdkconfig.defaults SDKCONFIG_MAKEFILE := $(SDK_PROJECT_PATH)/sdkconfig @@ -322,18 +226,14 @@ ifeq ($(MAKE_DOCS),) endif export SDKCONFIG_MAKEFILE # sub-makes (like bootloader) will reuse this path +FLASH_BOOT_LOADER := $(SDK_BUILD_BASE)/bootloader/bootloader.bin +FLASH_BOOT_CHUNKS := $(CONFIG_BOOTLOADER_OFFSET_IN_FLASH)=$(FLASH_BOOT_LOADER) + $(SDK_BUILD_BASE) $(SDK_COMPONENT_LIBDIR): $(Q) mkdir -p $@ SDK_COMPONENT_LIBS := $(foreach c,$(SDK_COMPONENTS),$(SDK_COMPONENT_LIBDIR)/lib$c.a) -SDK_BUILD_COMPLETE := $(SDK_BUILD_BASE)/.complete - -CUSTOM_TARGETS += checksdk - -.PHONY: checksdk -checksdk: $(SDK_PROJECT_PATH) $(SDK_BUILD_COMPLETE) - SDK_BUILD = $(ESP32_PYTHON) $(IDF_PATH)/tools/idf.py -C $(SDK_PROJECT_PATH) -B $(SDK_BUILD_BASE) -G Ninja # For misc.mk / copylibs @@ -341,19 +241,21 @@ export SDK_BUILD_BASE export SDK_COMPONENT_LIBDIR export SDK_COMPONENTS -$(SDK_BUILD_COMPLETE): $(SDKCONFIG_H) $(SDKCONFIG_MAKEFILE) - $(Q) $(SDK_BUILD) reconfigure +CUSTOM_TARGETS += checksdk + +.PHONY: checksdk +checksdk: $(SDK_PROJECT_PATH) $(SDKCONFIG_H) $(SDKCONFIG_MAKEFILE) $(Q) $(NINJA) -C $(SDK_BUILD_BASE) bootloader app $(Q) $(MAKE) --no-print-directory -C $(SDK_DEFAULT_PATH) -f misc.mk copylibs - touch $(SDK_BUILD_COMPLETE) $(SDKCONFIG_H) $(SDKCONFIG_MAKEFILE) $(SDK_COMPONENT_LIBS): $(SDK_PROJECT_PATH) $(SDK_CONFIG_DEFAULTS) | $(SDK_BUILD_BASE) $(SDK_COMPONENT_LIBDIR) + $(Q) $(SDK_BUILD) reconfigure $(SDK_PROJECT_PATH): $(Q) mkdir -p $@ $(Q) cp -r $(SDK_DEFAULT_PATH)/project/* $@ -$(SDK_COMPONENT_LIBS): $(SDK_BUILD_COMPLETE) +$(SDK_COMPONENT_LIBS): checksdk SDK_CONFIG_FILES := \ common \ @@ -376,23 +278,27 @@ $(SDK_CONFIG_DEFAULTS): $(SDK_CUSTOM_CONFIG_PATH) $(if $(wildcard $f),cat $f >> $@;) \ ) +##@Configuration + PHONY: sdk-menuconfig sdk-menuconfig: $(SDK_CONFIG_DEFAULTS) | $(SDK_BUILD_BASE) ##Configure SDK options $(Q) $(SDK_BUILD) menuconfig - $(Q) rm -f $(SDK_BUILD_COMPLETE) - @echo Now run 'make esp32-build' + @echo Now run 'make' -.PHONY: sdk-defconfig -sdk-defconfig: $(SDKCONFIG_H) ##Create default SDK config files +##@Cleaning .PHONY: sdk-config-clean sdk-config-clean: esp32-clean ##Wipe SDK configuration and revert to defaults $(Q) rm -rf $(SDK_PROJECT_PATH) +##@Help + .PHONY: sdk-help sdk-help: ##Get SDK build options $(Q) $(SDK_BUILD) --help +##@Tools + .PHONY: sdk sdk: ##Pass options to IDF builder, e.g. `make sdk -- --help` or `make sdk menuconfig` $(Q) $(SDK_BUILD) $(filter-out sdk,$(MAKECMDGOALS)) diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/config/common b/Sming/Arch/Esp32/Components/esp32/sdk/config/common index 929616cf60..4ac31502b3 100644 --- a/Sming/Arch/Esp32/Components/esp32/sdk/config/common +++ b/Sming/Arch/Esp32/Components/esp32/sdk/config/common @@ -26,3 +26,11 @@ CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 CONFIG_ESP_TIMER_IMPL_FRC2=y +# Disable VFS +CONFIG_VFS_SUPPORT_IO=n +CONFIG_VFS_SUPPORT_DIR=n +CONFIG_VFS_SUPPORT_SELECT=n +CONFIG_VFS_SUPPORT_TERMIOS=n + +# Debugging +CONFIG_ESP_SYSTEM_PANIC_GDBSTUB=y diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/project/CMakeLists.txt b/Sming/Arch/Esp32/Components/esp32/sdk/project/CMakeLists.txt index bd18c8ced4..08d09bf1c8 100644 --- a/Sming/Arch/Esp32/Components/esp32/sdk/project/CMakeLists.txt +++ b/Sming/Arch/Esp32/Components/esp32/sdk/project/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.5) set(IDF_TARGET "$ENV{ESP_VARIANT}") +set(COMPONENTS $ENV{SDK_COMPONENTS} esptool_py main) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(Sming) diff --git a/Sming/Arch/Esp32/Components/esp32/src/clk.c b/Sming/Arch/Esp32/Components/esp32/src/clk.c new file mode 100644 index 0000000000..ca0bd2fee6 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/src/clk.c @@ -0,0 +1,60 @@ +#include "include/esp_clk.h" +#include +#include + +#if CONFIG_IDF_TARGET_ESP32 +typedef esp_pm_config_esp32_t pm_config_t; +#elif CONFIG_IDF_TARGET_ESP32C3 +typedef esp_pm_config_esp32c3_t pm_config_t; +#elif CONFIG_IDF_TARGET_ESP32S2 +typedef esp_pm_config_esp32s2_t pm_config_t; +#elif CONFIG_IDF_TARGET_ESP32S3 +typedef esp_pm_config_esp32s3_t pm_config_t; +#endif + +int esp_clk_cpu_freq(void); + +static esp_pm_lock_handle_t handle; + +bool system_update_cpu_freq(uint32_t freq) +{ + pm_config_t config = {}; + esp_err_t err = esp_pm_get_configuration(&config); + if(err != ESP_OK) { + debug_e("[PM] Failed to read PM config %u", err); + } else if(config.max_freq_mhz == freq) { + return true; + } + + if(handle == NULL) { + esp_err_t err = esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "sming", &handle); + if(err != ESP_OK) { + debug_e("[PM] Failed to create lock"); + return false; + } + } else { + // Reverts PM to default settings + esp_pm_lock_release(handle); + } + + config.max_freq_mhz = config.min_freq_mhz = freq; + err = esp_pm_configure(&config); + if(err != ESP_OK) { + debug_e("[PM] Failed to set CPU to %u MHz", freq); + m_printHex("CFG", &config, sizeof(config), -1, 16); + return false; + } + + err = esp_pm_lock_acquire(handle); + if(err != ESP_OK) { + debug_e("[PM] Failed to lock CPU to %u MHz", freq); + return false; + } + + return true; +} + +uint32_t system_get_cpu_freq(void) +{ + return esp_clk_cpu_freq() / MHZ; +} diff --git a/Sming/Arch/Esp32/Components/esp32/src/include/esp_clk.h b/Sming/Arch/Esp32/Components/esp32/src/include/esp_clk.h index eaed0750d9..e34c31504a 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/include/esp_clk.h +++ b/Sming/Arch/Esp32/Components/esp32/src/include/esp_clk.h @@ -2,6 +2,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -13,20 +14,8 @@ extern "C" { #define SYS_CPU_160MHZ 160 #define SYS_CPU_240MHZ 240 -__forceinline bool system_update_cpu_freq(uint8_t freq) -{ - if(freq != SYS_CPU_80MHZ && freq != SYS_CPU_160MHZ && freq != SYS_CPU_240MHZ) { - return false; - } - - ets_update_cpu_frequency(freq); - return true; -} - -__forceinline uint32_t system_get_cpu_freq(void) -{ - return ets_get_cpu_frequency(); -} +bool system_update_cpu_freq(uint32_t freq); +uint32_t system_get_cpu_freq(void); __forceinline static uint32_t esp_get_ccount() { diff --git a/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h b/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h index 1d785f83b9..04897d7545 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h +++ b/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h @@ -44,13 +44,12 @@ /** @brief Disable interrupts * @retval Current interrupt level - * @note Hardware timer is unaffected if operating in non-maskable mode */ #define noInterrupts() portENTER_CRITICAL_NESTED() /** @brief Enable interrupts */ -#define interrupts() portEXIT_CRITICAL_NESTED(0) +#define interrupts() portENABLE_INTERRUPTS() /** @brief Restore interrupts to level saved from previous noInterrupts() call */ diff --git a/Sming/Arch/Esp32/Components/esp32/src/startup.cpp b/Sming/Arch/Esp32/Components/esp32/src/startup.cpp index d185c93ed7..13908fa2e9 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/startup.cpp +++ b/Sming/Arch/Esp32/Components/esp32/src/startup.cpp @@ -68,6 +68,8 @@ void main(void*) smg_uart_detach_all(); + esp_log_set_vprintf(m_vprintf); + #ifndef DISABLE_WIFI esp_init_flash(); esp_init_wifi(); diff --git a/Sming/Arch/Esp32/Components/esp32/src/system.cpp b/Sming/Arch/Esp32/Components/esp32/src/system.cpp index 29cc7a3333..fd0ce1fdee 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/system.cpp +++ b/Sming/Arch/Esp32/Components/esp32/src/system.cpp @@ -1,6 +1,7 @@ #include #include #include +#include extern "C" uint32_t system_get_time(void) { @@ -67,3 +68,18 @@ uint32_t system_get_chip_id() memcpy(&id, &baseMac[2], sizeof(id)); return id; } + +/* + * Building without WiFi sometimes leaves these functions undefined. + */ + +unsigned long WEAK_ATTR os_random(void) +{ + return esp_random(); +} + +int WEAK_ATTR os_get_random(unsigned char* buf, size_t len) +{ + esp_fill_random(buf, len); + return 0; +} diff --git a/Sming/Arch/Esp32/Components/esp32/src/tasks.cpp b/Sming/Arch/Esp32/Components/esp32/src/tasks.cpp index 49d8e28386..9cacd1000e 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/tasks.cpp +++ b/Sming/Arch/Esp32/Components/esp32/src/tasks.cpp @@ -13,7 +13,7 @@ class TaskQueue read = count = 0; } - bool post(os_signal_t sig, os_param_t par) + bool IRAM_ATTR post(os_signal_t sig, os_param_t par) { bool full = (count == length); if(!full) { @@ -62,15 +62,13 @@ bool system_os_task(os_task_t callback, uint8_t prio, os_event_t* events, uint8_ return queue != nullptr; } -bool system_os_post(uint8_t prio, os_signal_t sig, os_param_t par) +bool IRAM_ATTR system_os_post(uint8_t prio, os_signal_t sig, os_param_t par) { if(prio >= USER_TASK_PRIO_MAX) { - debug_e("TQ: Invalid priority %u", prio); return false; } auto& queue = task_queues[prio]; if(queue == nullptr) { - debug_e("TQ: Task queue %u not initialised", prio); return false; } diff --git a/Sming/Arch/Esp32/Components/gdbstub/component.mk b/Sming/Arch/Esp32/Components/gdbstub/component.mk index 6790f9d714..ef86e79a3d 100644 --- a/Sming/Arch/Esp32/Components/gdbstub/component.mk +++ b/Sming/Arch/Esp32/Components/gdbstub/component.mk @@ -1,6 +1,14 @@ DEBUG_VARS += GDBSTUB_DIR GDBSTUB_DIR := $(COMPONENT_PATH) +CACHE_VARS += COM_PORT_GDB +DEBUG_VARS += COM_SPEED_GDB +COM_PORT_GDB ?= $(COM_PORT) +COM_SPEED_GDB ?= $(COM_SPEED_SERIAL) + # Full GDB command line +DEBUG_VARS += GDBSTUB_DIR +GDBSTUB_DIR := $(COMPONENT_PATH) + CACHE_VARS += GDB_CMDLINE -GDB_CMDLINE = trap '' INT; $(GDB) -x $(ARCH_TOOLS)/gdbinit $(TARGET_OUT) +GDB_CMDLINE = trap '' INT; $(GDB) -x $(GDBSTUB_DIR)/gdbcmds -b $(COM_SPEED_GDB) -ex "target remote $(COM_PORT_GDB)" $(TARGET_OUT) diff --git a/Sming/Arch/Esp32/Components/gdbstub/gdbcmds b/Sming/Arch/Esp32/Components/gdbstub/gdbcmds new file mode 100644 index 0000000000..1a8f57677f --- /dev/null +++ b/Sming/Arch/Esp32/Components/gdbstub/gdbcmds @@ -0,0 +1,5 @@ +# Enable this if you want to log all traffic between GDB and the stub +#set remotelogfile gdb_rsp_logfile.txt + +# Display a welcome prompt +echo \nWelcome to SMING!\nType 'c' (continue) to run application\n\n diff --git a/Sming/Arch/Esp32/Components/libc/component.mk b/Sming/Arch/Esp32/Components/libc/component.mk index 67f040512b..e7bac1bcb1 100644 --- a/Sming/Arch/Esp32/Components/libc/component.mk +++ b/Sming/Arch/Esp32/Components/libc/component.mk @@ -1,4 +1,8 @@ COMPONENT_SRCDIRS := src -COMPONENT_INCDIRS := include +COMPONENT_INCDIRS := src/include -COMPONENT_DOXYGEN_INPUT := include/sys +COMPONENT_DOXYGEN_INPUT := src/include/sys + +EXTRA_LDFLAGS := \ + -Wl,-wrap,_write_r \ + -Wl,-wrap,_read_r \ diff --git a/Sming/Arch/Esp32/Components/libc/include/assert.h b/Sming/Arch/Esp32/Components/libc/src/include/assert.h similarity index 100% rename from Sming/Arch/Esp32/Components/libc/include/assert.h rename to Sming/Arch/Esp32/Components/libc/src/include/assert.h diff --git a/Sming/Arch/Esp32/Components/libc/include/esp_attr.h b/Sming/Arch/Esp32/Components/libc/src/include/esp_attr.h similarity index 100% rename from Sming/Arch/Esp32/Components/libc/include/esp_attr.h rename to Sming/Arch/Esp32/Components/libc/src/include/esp_attr.h diff --git a/Sming/Arch/Esp32/Components/libc/include/sys/pgmspace.h b/Sming/Arch/Esp32/Components/libc/src/include/sys/pgmspace.h similarity index 90% rename from Sming/Arch/Esp32/Components/libc/include/sys/pgmspace.h rename to Sming/Arch/Esp32/Components/libc/src/include/sys/pgmspace.h index 9315325204..a2c3294ebe 100644 --- a/Sming/Arch/Esp32/Components/libc/include/sys/pgmspace.h +++ b/Sming/Arch/Esp32/Components/libc/src/include/sys/pgmspace.h @@ -21,11 +21,9 @@ extern "C" { /** * @brief Simple check to determine if a pointer refers to flash memory */ -#define isFlashPtr(ptr) \ - (((uint32_t)(ptr) >= SOC_DROM_LOW && (uint32_t)(ptr) < SOC_DROM_HIGH) || \ - ((uint32_t)(ptr) >= SOC_IROM_LOW && (uint32_t)(ptr) < SOC_IROM_HIGH)) +#define isFlashPtr(ptr) ((uint32_t)(ptr) >= SOC_DROM_LOW && (uint32_t)(ptr) < SOC_DROM_HIGH) -#define PROGMEM STORE_ATTR +#define PROGMEM STORE_ATTR ICACHE_RODATA_ATTR #define PROGMEM_PSTR PROGMEM #define PSTR(str) (str) diff --git a/Sming/Arch/Esp32/Components/libc/src/replacements.c b/Sming/Arch/Esp32/Components/libc/src/replacements.c new file mode 100644 index 0000000000..04d972852b --- /dev/null +++ b/Sming/Arch/Esp32/Components/libc/src/replacements.c @@ -0,0 +1,24 @@ +/* + * Provide overrides to direct any console writes e.g. via newlib printf. + */ + +#include +#include +#include + +ssize_t __wrap__write_r(struct _reent* r, int fd, const void* data, size_t size) +{ + (void)r; + (void)fd; // Ignore, direct everything + return m_nputs(data, size); +} + +ssize_t __wrap__read_r(struct _reent* r, int fd, void* dst, size_t size) +{ + (void)r; + (void)fd; + (void)dst; + (void)size; + errno = ENOSYS; + return -1; +} diff --git a/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp b/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp index d77efdd403..0079c0c690 100644 --- a/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp +++ b/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp @@ -10,16 +10,10 @@ #include #include -#include -#include #include -#include #include #include - -#ifndef CONFIG_IDF_TARGET_ESP32C3 -#include -#endif +#include /* * Physical <-> Virtual address mapping is handled in `$IDF_COMPONENTS/spi_flash/flash_mmap.c`. @@ -28,28 +22,27 @@ */ uint32_t flashmem_get_address(const void* memptr) { -#define VADDR0_START_ADDR SOC_DROM_LOW -#define VADDR1_START_ADDR 0x40000000 - auto vaddr = reinterpret_cast(memptr); - - uint32_t page; - if(vaddr >= SOC_DROM_LOW && vaddr < SOC_DROM_HIGH) { - auto offset = vaddr - VADDR0_START_ADDR; - page = offset / SPI_FLASH_MMU_PAGE_SIZE; - } else if(vaddr >= SOC_IROM_LOW && vaddr < SOC_IROM_HIGH) { - auto offset = vaddr - VADDR1_START_ADDR; - page = 64 + (offset / SPI_FLASH_MMU_PAGE_SIZE); - } else { + if(vaddr < SOC_DROM_LOW || vaddr >= SOC_DROM_HIGH) { return 0; } + + auto offset = vaddr - SOC_MMU_VADDR0_START_ADDR; + uint32_t page = SOC_MMU_DROM0_PAGES_START + (offset / SPI_FLASH_MMU_PAGE_SIZE); #if CONFIG_IDF_TARGET_ESP32 && !CONFIG_FREERTOS_UNICORE - uint32_t entry = DPORT_SEQUENCE_REG_READ(uint32_t(&DPORT_APP_FLASH_MMU_TABLE[page])); + uint32_t entry = DPORT_APP_FLASH_MMU_TABLE[page]; #else - uint32_t entry = DPORT_SEQUENCE_REG_READ(uint32_t(&SOC_MMU_DPORT_PRO_FLASH_MMU_TABLE[page])); + uint32_t entry = SOC_MMU_DPORT_PRO_FLASH_MMU_TABLE[page]; #endif - uint32_t paddr = (entry * SPI_FLASH_MMU_PAGE_SIZE) + (vaddr & (SPI_FLASH_MMU_PAGE_SIZE - 1)); - return (entry & 0x0100) ? 0 : paddr; + + if(entry == SOC_MMU_INVALID_ENTRY_VAL) { + debug_e("Invalid flash address %p (page %u, entry 0x%08x)", memptr, page, entry); + return 0; + } + + entry &= SOC_MMU_ADDR_MASK; + uint32_t paddr = (entry * SPI_FLASH_MMU_PAGE_SIZE) + (vaddr % SPI_FLASH_MMU_PAGE_SIZE); + return paddr; } uint32_t flashmem_write(const void* from, uint32_t toaddr, uint32_t size) diff --git a/Sming/Arch/Esp32/Platform/Clocks.h b/Sming/Arch/Esp32/Platform/Clocks.h index 9a7e838b05..34484ac99b 100644 --- a/Sming/Arch/Esp32/Platform/Clocks.h +++ b/Sming/Arch/Esp32/Platform/Clocks.h @@ -38,6 +38,10 @@ using PolledTimerClock = OsTimerClock; using CpuCycleClockSlow = CpuCycleClock; using CpuCycleClockNormal = CpuCycleClock; +#ifdef SUBARCH_ESP32C3 +using CpuCycleClockFast = CpuCycleClockNormal; +#else using CpuCycleClockFast = CpuCycleClock; +#endif /** @} */ diff --git a/Sming/Arch/Esp32/README.rst b/Sming/Arch/Esp32/README.rst index 05831e40ba..3092ea6496 100644 --- a/Sming/Arch/Esp32/README.rst +++ b/Sming/Arch/Esp32/README.rst @@ -30,7 +30,7 @@ Requirements In order to be able to compile for the ESP32 architecture you should have ESP-IDF v4.3 installed. Some slight changes are required to enable code to compile correctly for C++, -so a fork has been created here https://github.com/mikee47/esp-idf/tree/sming/release%2Fv4.3 +so a fork has been created here https://github.com/mikee47/esp-idf/tree/sming/release/v4.3 which you may clone. The Sming installers do all this for you - see :doc:`/getting-started/index`. @@ -89,13 +89,14 @@ This is still at an early stage of development however basic applications should - esp32c3 - esp32s3 -If changing variant the project must be cleaned first. You can change variants like this: +You can change variants like this: ``` -make SMING_ARCH=Esp32 clean components-clean -make ESP_VARIANT=esp32c3 +make SMING_ARCH=Esp32 ESP_VARIANT=esp32c3 ``` +Each variant uses a different build directory, e.g. ``out/Esp32/esp32c3/...`` to avoid conflicts. + See :component-esp32:`esp32` for further details. diff --git a/Sming/Arch/Esp32/System/include/user_config.h b/Sming/Arch/Esp32/System/include/user_config.h index b57a7d9666..590d433248 100644 --- a/Sming/Arch/Esp32/System/include/user_config.h +++ b/Sming/Arch/Esp32/System/include/user_config.h @@ -12,10 +12,6 @@ #include // Network base API -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsign-compare" #include -#pragma GCC diagnostic pop - #endif diff --git a/Sming/Arch/Esp32/Tools/ci/build.run.sh b/Sming/Arch/Esp32/Tools/ci/build.run.sh old mode 100755 new mode 100644 index 1d1aa4aa39..12de1fb80e --- a/Sming/Arch/Esp32/Tools/ci/build.run.sh +++ b/Sming/Arch/Esp32/Tools/ci/build.run.sh @@ -4,13 +4,10 @@ $MAKE_PARALLEL Basic_Blink Basic_WiFi HttpServer_ConfigNetwork DEBUG_VERBOSE_LEV $MAKE_PARALLEL Basic_Ssl ENABLE_SSL=Bearssl DEBUG_VERBOSE_LEVEL=3 STRICT=1 # esp32s2 -make clean components-clean $MAKE_PARALLEL Basic_Blink ESP_VARIANT=esp32s2 # esp32c3 -make clean components-clean $MAKE_PARALLEL Basic_Blink ESP_VARIANT=esp32c3 # esp32s3 -make clean components-clean $MAKE_PARALLEL Basic_Blink ESP_VARIANT=esp32s3 diff --git a/Sming/Arch/Esp32/app.mk b/Sming/Arch/Esp32/app.mk index 6f4dc23d03..db6ca31cbf 100644 --- a/Sming/Arch/Esp32/app.mk +++ b/Sming/Arch/Esp32/app.mk @@ -35,5 +35,10 @@ $(TARGET_OUT): $(COMPONENTS_AR) cat $(FW_MEMINFO_NEW); \ fi +CHIP_REV_MIN := $(CONFIG_$(call ToUpper,$(ESP_VARIANT))_REV_MIN) +ifeq ($(CHIP_REV_MIN),) +CHIP_REV_MIN := 0 +endif + $(TARGET_BIN): $(TARGET_OUT) - $(Q) $(ESPTOOL_CMDLINE) elf2image --min-rev 0 --elf-sha256-offset 0xb0 $(flashimageoptions) -o $@ $< + $(Q) $(ESPTOOL_CMDLINE) elf2image --min-rev $(CHIP_REV_MIN) --elf-sha256-offset 0xb0 $(flashimageoptions) -o $@ $< diff --git a/Sming/Arch/Esp32/build.mk b/Sming/Arch/Esp32/build.mk index 73b15f4107..3356671ea9 100644 --- a/Sming/Arch/Esp32/build.mk +++ b/Sming/Arch/Esp32/build.mk @@ -23,7 +23,7 @@ endif export IDF_TOOLS_PATH := $(call FixPath,$(IDF_TOOLS_PATH)) ifndef ESP_VARIANT -ESP_VARIANT := esp32 +override ESP_VARIANT := esp32 endif export ESP_VARIANT diff --git a/Sming/Arch/Esp8266/build.mk b/Sming/Arch/Esp8266/build.mk index 1d41353210..d3497ba66d 100644 --- a/Sming/Arch/Esp8266/build.mk +++ b/Sming/Arch/Esp8266/build.mk @@ -4,6 +4,10 @@ # ############## +ifdef ESP_VARIANT +override ESP_VARIANT := +endif + CPPFLAGS += -DARCH_ESP8266 CXXFLAGS += -fno-rtti -fno-exceptions -fno-threadsafe-statics diff --git a/Sming/Arch/Host/build.mk b/Sming/Arch/Host/build.mk index db1582468f..0f98412d23 100644 --- a/Sming/Arch/Host/build.mk +++ b/Sming/Arch/Host/build.mk @@ -4,6 +4,10 @@ # ############## +ifdef ESP_VARIANT +override ESP_VARIANT := +endif + CPPFLAGS += -DARCH_HOST TOOLSPEC := diff --git a/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp b/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp deleted file mode 100644 index 4d59e586e8..0000000000 --- a/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.cpp +++ /dev/null @@ -1,876 +0,0 @@ -/** - A big thank you to Hristo Gochkov and his Asynchronous TCP library - Big chunks of the code are inspired from this project. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#if 0 - -#include "LwipAdapter.h" -#include -#include "esp_task_wdt.h" -extern "C" { -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "lwip/priv/tcpip_priv.h" -} - -// TODO: Tune these.... -#define CONFIG_ASYNC_TCP_RUNNING_CORE 1 -#define CONFIG_ASYNC_TCP_USE_WDT 1 - -typedef enum { - LWIP_TCP_SENT, - LWIP_TCP_RECV, - LWIP_TCP_FIN, - LWIP_TCP_ERROR, - LWIP_TCP_POLL, - LWIP_TCP_CLEAR, - LWIP_TCP_ACCEPT, - LWIP_TCP_CONNECTED, - LWIP_TCP_DNS -} lwip_event_t; - -typedef struct { - lwip_event_t event; - void* arg; - union { - struct { - tcp_pcb* pcb; - int8_t err; - } connected; - struct { - int8_t err; - } error; - struct { - tcp_pcb* pcb; - uint16_t len; - } sent; - struct { - tcp_pcb* pcb; - pbuf* pb; - int8_t err; - } recv; - struct { - tcp_pcb* pcb; - int8_t err; - } fin; - struct { - tcp_pcb* pcb; - } poll; - struct { - tcp_pcb* pcb; - int8_t err; - } accept; - struct { - const char* name; - ip_addr_t addr; - } dns; - }; -} lwip_event_packet_t; - -struct DnsLookup { - TcpConnection* con; - int port; -}; - -typedef struct { - TcpConnection* connection; - uint8_t closed; // 0 - open, 1 - closed - struct { - tcp_connected_fn callback; - } connect; - struct { - tcp_accept_fn callback; - } accept; - struct { - tcp_recv_fn callback; - } receive; - struct { - tcp_sent_fn callback; - } sent; - struct { - tcp_poll_fn callback; - } poll; - struct { - tcp_err_fn callback; - } error; - struct { - dns_found_callback callback; - DnsLookup* lookup; - } dns; - // TODO: ... -} tcp_api_arg_t; - -typedef struct { - struct tcpip_api_call_data call; - tcp_pcb* pcb; - int8_t err; - union { - struct { - const char* data; - size_t size; - uint8_t apiflags; - } write; - size_t received; - struct { - const ip_addr_t* addr; - uint16_t port; - tcp_connected_fn cb; - } connect; - struct { - const ip_addr_t* addr; - uint16_t port; - } bind; - uint8_t backlog; - }; -} tcp_api_call_t; - -// Event Logic -static xQueueHandle _async_queue; -static TaskHandle_t _async_service_task_handle = NULL; - -// TODO: remove the slots.. -//SemaphoreHandle_t _slots_lock; -//const int _number_of_closed_slots = CONFIG_LWIP_MAX_ACTIVE_TCP; -//static uint32_t _closed_slots[_number_of_closed_slots]; -//static uint32_t _closed_index = []() { -// _slots_lock = xSemaphoreCreateBinary(); -// xSemaphoreGive(_slots_lock); -// for(int i = 0; i < _number_of_closed_slots; ++i) { -// _closed_slots[i] = 1; -// } -// return 1; -//}(); - -static inline bool _init_async_event_queue() -{ - if(!_async_queue) { - _async_queue = xQueueCreate(32, sizeof(lwip_event_packet_t*)); - if(!_async_queue) { - return false; - } - } - return true; -} - -static inline bool _send_async_event(lwip_event_packet_t** e) -{ - return _async_queue && xQueueSend(_async_queue, e, portMAX_DELAY) == pdPASS; -} - -static inline bool _prepend_async_event(lwip_event_packet_t** e) -{ - return _async_queue && xQueueSendToFront(_async_queue, e, portMAX_DELAY) == pdPASS; -} - -static inline bool _get_async_event(lwip_event_packet_t** e) -{ - return _async_queue && xQueueReceive(_async_queue, e, portMAX_DELAY) == pdPASS; -} - -static bool _remove_events_with_arg(void* arg) -{ - lwip_event_packet_t* first_packet = NULL; - lwip_event_packet_t* packet = NULL; - - if(!_async_queue) { - return false; - } - //figure out which is the first packet so we can keep the order - while(!first_packet) { - if(xQueueReceive(_async_queue, &first_packet, 0) != pdPASS) { - return false; - } - //discard packet if matching - if((int)first_packet->arg == (int)arg) { - free(first_packet); - first_packet = NULL; - //return first packet to the back of the queue - } else if(xQueueSend(_async_queue, &first_packet, portMAX_DELAY) != pdPASS) { - return false; - } - } - - while(xQueuePeek(_async_queue, &packet, 0) == pdPASS && packet != first_packet) { - if(xQueueReceive(_async_queue, &packet, 0) != pdPASS) { - return false; - } - if((int)packet->arg == (int)arg) { - free(packet); - packet = NULL; - } else if(xQueueSend(_async_queue, &packet, portMAX_DELAY) != pdPASS) { - return false; - } - } - return true; -} - -static void _handle_async_event(lwip_event_packet_t* e) -{ - tcp_api_arg_t* arg = reinterpret_cast(e->arg); - - if(e->arg == NULL) { - // do nothing when arg is NULL - debug_d("event (%d) arg == NULL(org: 0x%08x): 0x%08x, Pcb State: %d", e->event, e->arg, e->recv.pcb, e->recv.pcb->state); - if(e->event == LWIP_TCP_POLL) { - debug_d("Close connection. Pcb: 0x%08x", e->poll.pcb); - async_tcp_close(e->poll.pcb); // close the connection - } - - } else if(e->event == LWIP_TCP_CLEAR) { - _remove_events_with_arg(e->arg); - } else if(e->event == LWIP_TCP_RECV) { - debug_d("-R: Pcb: 0x%08x, Arg: 0x%08x, Err: %d", e->recv.pcb, e->arg, e->recv.err); - if(arg->receive.callback) { - arg->receive.callback(arg->connection, e->recv.pcb, e->recv.pb, e->recv.err); - } - } else if(e->event == LWIP_TCP_FIN) { - debug_d("-F: Pcb: 0x%08x, Arg: 0x%08x", e->fin.pcb, e->arg); - debug_d("Close connection. Pcb: 0x%08x", e->fin.pcb); - async_tcp_close(e->fin.pcb); // close the connection - } else if(e->event == LWIP_TCP_SENT) { - debug_d("-S: Pcb: 0x%08x, Arg: 0x%08x, Callback: 0x%08x", e->sent.pcb, e->arg, arg->sent.callback); - if(arg->sent.callback) { - arg->sent.callback(arg->connection, e->sent.pcb, e->sent.len); - } - } else if(e->event == LWIP_TCP_POLL) { - debug_d("-P: Pcb: 0x%08x, Arg: 0x%08x, Callback: 0x%08x", e->poll.pcb, e->arg, arg->poll.callback); - if(arg->poll.callback) { - arg->poll.callback(arg->connection, e->poll.pcb); - } - } else if(e->event == LWIP_TCP_ERROR) { - debug_d("-E: Arg: 0x%08x, Error: %d", e->arg, e->error.err); - if(arg->error.callback) { - arg->error.callback(arg->connection, e->error.err); - } - } else if(e->event == LWIP_TCP_CONNECTED) { - debug_d("-C: Pcb: 0x%08x, Arg: 0x%08x, Callback: 0x%08x, Err: %d", e->connected.pcb, e->arg, arg->connect.callback, e->connected.err); - if(arg->connect.callback) { - arg->connect.callback(arg->connection, e->connected.pcb, e->connected.err); - } - } else if(e->event == LWIP_TCP_ACCEPT) { - debug_d("-A: Pcb: 0x%08x, Arg: 0x%08x, Callback: 0x%08x", e->accept.pcb, e->arg, arg->accept.callback); - if(arg->accept.callback) { - arg->accept.callback(arg->connection, e->accept.pcb, e->accept.err); - } - } else if(e->event == LWIP_TCP_DNS) { - debug_d("D: 0x%08x %s = %s", e->arg, e->dns.name, ipaddr_ntoa(&e->dns.addr)); - if(arg->dns.callback) { - arg->dns.callback(e->dns.name, &e->dns.addr, arg->dns.lookup); - } - delete arg; - } - free((void*)(e)); -} - -static void _async_service_task(void* pvParameters) -{ - lwip_event_packet_t* packet = NULL; - for(;;) { - if(_get_async_event(&packet)) { -#if CONFIG_ASYNC_TCP_USE_WDT - if(esp_task_wdt_add(NULL) != ESP_OK) { - debug_e("Failed to add async task to WDT"); - } -#endif - _handle_async_event(packet); -#if CONFIG_ASYNC_TCP_USE_WDT - if(esp_task_wdt_delete(NULL) != ESP_OK) { - debug_e("Failed to remove loop task from WDT"); - } -#endif - } - } - vTaskDelete(NULL); - _async_service_task_handle = NULL; -} -/* -static void _stop_async_task(){ - if(_async_service_task_handle){ - vTaskDelete(_async_service_task_handle); - _async_service_task_handle = NULL; - } -} -*/ -static bool _start_async_task() -{ - if(!_init_async_event_queue()) { - return false; - } - if(!_async_service_task_handle) { - xTaskCreatePinnedToCore(_async_service_task, "async_tcp", 8192 * 2, NULL, 3, &_async_service_task_handle, - CONFIG_ASYNC_TCP_RUNNING_CORE); - if(!_async_service_task_handle) { - return false; - } - debug_d("Starting Async Event Queue."); - } - return true; -} - -// Low-Level LwIP Functions -static int8_t _tcp_clear_events(void* arg) -{ - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_CLEAR; - e->arg = arg; - if(!_prepend_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_connected(void* arg, tcp_pcb* pcb, int8_t err) -{ - debug_d("+C: 0x%08x", pcb); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - - e->event = LWIP_TCP_CONNECTED; - e->arg = arg; - e->connected.pcb = pcb; - e->connected.err = err; - if(!_prepend_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_poll(void* arg, struct tcp_pcb* pcb) -{ - debug_d("+P: 0x%08x", pcb); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_POLL; - e->arg = arg; - e->poll.pcb = pcb; - if(!_send_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* pb, int8_t err) -{ - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->arg = arg; - if(pb) { - debug_d("+R: 0x%08x", pcb); - e->event = LWIP_TCP_RECV; - e->recv.pcb = pcb; - e->recv.pb = pb; - e->recv.err = err; - } else { - debug_d("+F: 0x%08x", pcb); - e->event = LWIP_TCP_FIN; - e->fin.pcb = pcb; - e->fin.err = err; - // //close the PCB in LwIP thread - // // TODO: - //// TcpConnection::_s_lwip_fin(e->arg, e->fin.pcb, e->fin.err); - } - if(!_send_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -static int8_t _tcp_sent(void* arg, struct tcp_pcb* pcb, uint16_t len) -{ - debug_d("+S: 0x%08x", pcb); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_SENT; - e->arg = arg; - e->sent.pcb = pcb; - e->sent.len = len; - if(!_send_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -static void _tcp_error(void* arg, int8_t err) -{ - debug_d("+E: 0x%08x", arg); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_ERROR; - e->arg = arg; - e->error.err = err; - if(!_send_async_event(&e)) { - free((void*)(e)); - } -} - -static void _tcp_dns_found(const char* name, const ip_addr* ipaddr, void* arg) -{ - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - debug_d("+DNS: name=%s ipaddr=0x%08x arg=%x", name, ipaddr, arg); - e->event = LWIP_TCP_DNS; - e->arg = arg; - e->dns.name = name; - if(ipaddr) { - memcpy(&e->dns.addr, ipaddr, sizeof(struct ip_addr)); - } else { - memset(&e->dns.addr, 0, sizeof(e->dns.addr)); - } - if(!_send_async_event(&e)) { - free((void*)(e)); - } -} - -//Used to switch out from LwIP thread -static int8_t _tcp_accept(void* arg, struct tcp_pcb* newpcb, err_t err) -{ - debug_d("+A: arg: 0x%08x, new pcb: 0x%08x, error: %d", arg, newpcb, err); - lwip_event_packet_t* e = (lwip_event_packet_t*)malloc(sizeof(lwip_event_packet_t)); - e->event = LWIP_TCP_ACCEPT; - e->arg = arg; - e->accept.pcb = newpcb; - e->accept.err = err; - if(!_prepend_async_event(&e)) { - free((void*)(e)); - } - return ERR_OK; -} - -/* - * TCP/IP API Calls - * */ - -static err_t _tcp_output_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = ERR_CONN; - tcp_api_arg_t* arg = reinterpret_cast(msg->pcb->callback_arg); - if(arg != nullptr && arg->closed) { - return msg->err; - } - msg->err = tcp_output(msg->pcb); - return msg->err; -} - -static esp_err_t _tcp_output(tcp_pcb* pcb) -{ - if(!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - tcpip_api_call(_tcp_output_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_write_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = ERR_CONN; - tcp_api_arg_t* arg = reinterpret_cast(msg->pcb->callback_arg); - if(arg != nullptr && arg->closed) { - return msg->err; - } - msg->err = tcp_write(msg->pcb, msg->write.data, msg->write.size, msg->write.apiflags); - return msg->err; -} - -static esp_err_t _tcp_write(tcp_pcb* pcb, const char* data, size_t size, uint8_t apiflags) -{ - if(!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - - msg.write.data = data; - msg.write.size = size; - msg.write.apiflags = apiflags; - tcpip_api_call(_tcp_write_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_recved_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = ERR_CONN; - tcp_api_arg_t* arg = reinterpret_cast(msg->pcb->callback_arg); - if(arg != nullptr && arg->closed) { - return msg->err; - } - - tcp_recved(msg->pcb, msg->received); - return msg->err; -} - -static esp_err_t _tcp_recved(tcp_pcb* pcb, size_t len) -{ - if(!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - - msg.received = len; - tcpip_api_call(_tcp_recved_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_close_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = ERR_CONN; - tcp_api_arg_t* arg = reinterpret_cast(msg->pcb->callback_arg); - if(arg != nullptr) { - if(arg->closed) { - return msg->err; - } - arg->closed = 1; - } - msg->err = tcp_close(msg->pcb); - _tcp_clear_events(msg->pcb); - return msg->err; -} - -static esp_err_t _tcp_close(tcp_pcb* pcb) -{ - if(!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - - tcpip_api_call(_tcp_close_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_abort_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = ERR_CONN; - tcp_api_arg_t* arg = reinterpret_cast(msg->pcb->callback_arg); - if(arg != nullptr) { - if(arg->closed) { - return msg->err; - } - arg->closed = 1; - } - - tcp_abort(msg->pcb); - return msg->err; -} - -static esp_err_t _tcp_abort(tcp_pcb* pcb) -{ - if(!pcb) { - return ERR_CONN; - } - tcp_api_call_t msg; - msg.pcb = pcb; - - tcpip_api_call(_tcp_abort_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_connect_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = tcp_connect(msg->pcb, msg->connect.addr, msg->connect.port, _tcp_connected); - return msg->err; -} - -static esp_err_t _tcp_connect(tcp_pcb* pcb, const ip_addr_t* addr, uint16_t port, tcp_connected_fn cb) -{ - if(!pcb) { - return ESP_FAIL; - } - - tcp_api_call_t msg; - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg != nullptr) { - debug_d("Connecting ..."); - arg->closed = 0; - arg->connect.callback = cb; - } - - msg.pcb = pcb; - - msg.connect.addr = addr; - msg.connect.port = port; - msg.connect.cb = _tcp_connected; - tcpip_api_call(_tcp_connect_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_bind_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = tcp_bind(msg->pcb, msg->bind.addr, msg->bind.port); - return msg->err; -} - -static esp_err_t _tcp_bind(tcp_pcb* pcb, const ip_addr_t* addr, uint16_t port) -{ - if(!pcb) { - return ESP_FAIL; - } - tcp_api_call_t msg; - msg.pcb = pcb; - tcp_api_arg_t* arg = reinterpret_cast(msg.pcb->callback_arg); - if(arg != nullptr) { - arg->closed = 0; - } - msg.bind.addr = addr; - msg.bind.port = port; - tcpip_api_call(_tcp_bind_api, (struct tcpip_api_call_data*)&msg); - return msg.err; -} - -static err_t _tcp_listen_api(struct tcpip_api_call_data* api_call_msg) -{ - tcp_api_call_t* msg = (tcp_api_call_t*)api_call_msg; - msg->err = 0; - msg->pcb = tcp_listen_with_backlog(msg->pcb, msg->backlog); - return msg->err; -} - -static tcp_pcb* _tcp_listen_with_backlog(tcp_pcb* pcb, uint8_t backlog) -{ - if(!pcb) { - return NULL; - } - tcp_api_call_t msg; - msg.pcb = pcb; - tcp_api_arg_t* arg = reinterpret_cast(msg.pcb->callback_arg); - if(arg != nullptr) { - arg->closed = 0; - } - msg.backlog = backlog ? backlog : TCP_DEFAULT_LISTEN_BACKLOG; - tcpip_api_call(_tcp_listen_api, (struct tcpip_api_call_data*)&msg); - return msg.pcb; -} - -// High Level Functions - -tcp_pcb* async_tcp_new() -{ - return tcp_new_ip_type(IPADDR_TYPE_V4); -} - -void async_tcp_arg(struct tcp_pcb* pcb, void* arg) -{ - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* apiArgument = reinterpret_cast(pcb->callback_arg); - // delete the current argument if this is an api argument - if(apiArgument != nullptr) { - delete apiArgument; - apiArgument = nullptr; - } - - TcpConnection* connectionArg = reinterpret_cast(arg); - if(connectionArg != nullptr) { - // we have the right argument, lets wrap it - if(apiArgument == nullptr) { - apiArgument = new tcp_api_arg_t(); - memset(apiArgument, 0, sizeof(tcp_api_arg_t)); - } - apiArgument->connection = connectionArg; - debug_d("Arg: Set wrapped arg. Pcb: 0x%08x, arg: 0x%08x, connection: 0x%08x", pcb, apiArgument, arg); - tcp_arg(pcb, apiArgument); - return; - } - - // all other arguments are set as usual - debug_d("Arg: Set unknown arg. Pcb: 0x%08x, arg: 0x%08x", pcb, arg); - tcp_arg(pcb, arg); -} - -err_t async_tcp_connect(tcp_pcb* pcb, const ip_addr_t* ipaddr, u16_t port, tcp_connected_fn connected) -{ - if(!_start_async_task()) { - return ERR_ABRT; - } - - debug_d("Connecting: Pcb: 0x%08x", pcb); - - return _tcp_connect(pcb, ipaddr, port, connected); -} - -tcp_pcb* async_tcp_listen_with_backlog(struct tcp_pcb* pcb, u8_t backlog) -{ - if(!_start_async_task()) { - return nullptr; - } - - return _tcp_listen_with_backlog(pcb, backlog); -} - -err_t async_tcp_bind(struct tcp_pcb* pcb, const ip_addr_t* ipaddr, u16_t port) -{ - if(!_start_async_task()) { - return ERR_ABRT; - } - - return _tcp_bind(pcb, ipaddr, port); -} - -void async_tcp_accept(struct tcp_pcb* pcb, tcp_accept_fn callback) -{ - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - debug_d("Set to direct accept callback: Pcb: 0x%08x, Arg: 0x%08x, State: %d.", pcb, pcb->callback_arg, pcb->state); - tcp_accept(pcb, callback); - return; - } - - debug_d("Accept: Pcb: 0x%08x, Callback: 0x%08x", pcb, callback); - - arg->accept.callback = callback; - tcp_accept(pcb, _tcp_accept); -} - -err_t async_tcp_write(struct tcp_pcb* pcb, const void* dataptr, u16_t len, u8_t apiflags) -{ - return _tcp_write(pcb, (const char*)dataptr, len, apiflags); -} - -void async_tcp_recved(struct tcp_pcb* pcb, u16_t len) -{ - _tcp_recved(pcb, len); -} - -err_t async_tcp_output(struct tcp_pcb* pcb) -{ - return _tcp_output(pcb); -} - -void async_tcp_abort(struct tcp_pcb* pcb) -{ - _tcp_abort(pcb); -} - -err_t async_tcp_close(struct tcp_pcb* pcb) -{ - return _tcp_close(pcb); -} - -void async_tcp_recv(struct tcp_pcb* pcb, tcp_recv_fn callback) -{ - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - debug_d("Set to direct recv callback: Pcb: 0x%08x, Arg: 0x%08x, State: %d.", pcb, pcb->callback_arg, pcb->state); - tcp_recv(pcb, callback); - return; - } - - debug_d("Receive: Pcb: 0x%08x, Callback: 0x%08x", pcb, callback); - - arg->receive.callback = callback; - tcp_recv(pcb, _tcp_recv); -} - -void async_tcp_sent(struct tcp_pcb* pcb, tcp_sent_fn callback) -{ - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - debug_d("Set to direct sent callback: Pcb: 0x%08x, Arg: 0x%08x, State: %d.", pcb, pcb->callback_arg, pcb->state); - tcp_sent(pcb, callback); - return; - } - - debug_d("Sent: Pcb: 0x%08x, Callback: 0x%08x", pcb, callback); - - arg->sent.callback = callback; - tcp_sent(pcb, _tcp_sent); -} - -void async_tcp_poll(struct tcp_pcb* pcb, tcp_poll_fn callback, u8_t interval) -{ - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - debug_d("Set to direct poll callback: Pcb: 0x%08x, Arg: 0x%08x, State: %d.", pcb, pcb->callback_arg, pcb->state); - tcp_poll(pcb, callback, interval); - return; - } - - debug_d("Poll: Pcb: 0x%08x, Callback: 0x%08x, Interval: %d", pcb, callback, interval); - - arg->poll.callback = callback; - tcp_poll(pcb, _tcp_poll, interval); -} - -void async_tcp_err(struct tcp_pcb* pcb, tcp_err_fn callback) -{ - if(pcb == nullptr) { - return; - } - - tcp_api_arg_t* arg = reinterpret_cast(pcb->callback_arg); - if(arg == nullptr) { - debug_d("Set to direct error callback: Pcb: 0x%08x, Arg: 0x%08x, State: %d.", pcb, pcb->callback_arg, pcb->state); - tcp_err(pcb, callback); - return; - } - - debug_d("Error: Pcb: 0x%08x, Callback: 0x%08x", pcb, callback); - - arg->error.callback = callback; - tcp_err(pcb, _tcp_error); -} - -err_t async_dns_gethostbyname(const char* hostname, ip_addr_t* addr, dns_found_callback found, void* callback_arg) -{ - DnsLookup* dnsLookup = reinterpret_cast(callback_arg); - if(dnsLookup == nullptr) { - // This should not happen - return ERR_ABRT; - } - - tcp_api_arg_t* arg = new tcp_api_arg_t(); - if(arg == nullptr) { - // This should not happen - return ERR_ABRT; - } - - if(!_start_async_task()) { - delete arg; - return ERR_ABRT; - } - - arg->dns.callback = found; - arg->dns.lookup = dnsLookup; - return dns_gethostbyname(hostname, addr, _tcp_dns_found, arg); -} - -#endif // 0 diff --git a/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.h b/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.h deleted file mode 100644 index c891b95123..0000000000 --- a/Sming/Components/Network/Arch/Esp32/Platform/LwipAdapter.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * LWIP adapter compatibility - */ -#pragma once - -/** - A big thank you to Hristo Gochkov and his Asynchronous TCP library - Big chunks of the code are inspired from this project. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include - -#if 0 - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#include "espinc/lwip_includes.h" - -#ifndef DEBUG_TCP_EXTENDED -#define DEBUG_TCP_EXTENDED 1 -#endif - -// Settings -tcp_pcb* async_tcp_new(); -void async_tcp_arg(struct tcp_pcb* pcb, void* arg); - -// Actions -err_t async_tcp_connect(struct tcp_pcb* pcb, const ip_addr_t* ipaddr, u16_t port, tcp_connected_fn callback); -tcp_pcb* async_tcp_listen_with_backlog(struct tcp_pcb* pcb, u8_t backlog); -void async_tcp_accept(struct tcp_pcb* pcb, tcp_accept_fn callback); -err_t async_tcp_bind(struct tcp_pcb* pcb, const ip_addr_t* ipaddr, u16_t port); -err_t async_tcp_write(struct tcp_pcb* pcb, const void* dataptr, u16_t len, u8_t apiflags); -void async_tcp_recved(struct tcp_pcb* pcb, u16_t len); -err_t async_tcp_output(struct tcp_pcb* pcb); -void async_tcp_abort(struct tcp_pcb* pcb); -err_t async_tcp_close(struct tcp_pcb* pcb); - -// Event callbacks -void async_tcp_recv(struct tcp_pcb* pcb, tcp_recv_fn callback); -void async_tcp_sent(struct tcp_pcb* pcb, tcp_sent_fn callback); -void async_tcp_poll(struct tcp_pcb* pcb, tcp_poll_fn callback, u8_t interval); -void async_tcp_err(struct tcp_pcb* pcb, tcp_err_fn callback); - -// DNS callback -err_t async_dns_gethostbyname(const char* hostname, ip_addr_t* addr, dns_found_callback found, void* callback_arg); - -#ifdef __cplusplus -} -#endif - -#endif // 0 diff --git a/Sming/Components/arch_driver/src/SerialBuffer.cpp b/Sming/Components/arch_driver/src/SerialBuffer.cpp index b567fd4fc4..078b2cc896 100644 --- a/Sming/Components/arch_driver/src/SerialBuffer.cpp +++ b/Sming/Components/arch_driver/src/SerialBuffer.cpp @@ -12,6 +12,10 @@ #include "include/driver/SerialBuffer.h" +#ifdef ARCH_ESP32 +#include +#endif + /** @brief find a character in the buffer * @param c * @retval int position relative to current read pointer, -1 if character not found @@ -46,7 +50,12 @@ size_t SerialBuffer::resize(size_t newSize) return size; } +#ifdef ARCH_ESP32 + // Avoid allocating in SPIRAM + auto new_buf = static_cast(heap_caps_malloc(newSize, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL)); +#else auto new_buf = new char[newSize]; +#endif if(new_buf == nullptr) { return size; } diff --git a/Sming/Components/esptool/component.mk b/Sming/Components/esptool/component.mk index 2a4cf0bbc0..3c936f186f 100644 --- a/Sming/Components/esptool/component.mk +++ b/Sming/Components/esptool/component.mk @@ -22,7 +22,7 @@ $(ESPTOOL): $(ESPTOOL_SUBMODULE)/.submodule ifeq ($(SMING_ARCH),Esp8266) ESP_CHIP := esp8266 else ifeq ($(SMING_ARCH),Esp32) -ESP_CHIP := esp32 +ESP_CHIP := $(ESP_VARIANT) else ifeq ($(MAKE_DOCS),) $(error esptool unsupported arch: $(SMING_ARCH)) endif diff --git a/Sming/Components/terminal/component.mk b/Sming/Components/terminal/component.mk index 5529f916ee..959cc6f2c3 100644 --- a/Sming/Components/terminal/component.mk +++ b/Sming/Components/terminal/component.mk @@ -15,7 +15,7 @@ endif # Universal python terminal application CACHE_VARS += COM_OPTS KILL_TERM TERMINAL -COM_OPTS ?= --raw --encoding ascii +COM_OPTS ?= --raw --encoding ascii --rts 0 --dtr 0 KILL_TERM ?= pkill -9 -f "$(COM_PORT) $(COM_SPEED_SERIAL)" || exit 0 ifdef WSL_ROOT TERMINAL ?= powershell.exe -Command "python -m serial.tools.miniterm $(COM_OPTS) $(COM_PORT) $(COM_SPEED_SERIAL)" diff --git a/Sming/Components/Network/src/Data/Stream/XorOutputStream.h b/Sming/Core/Data/Stream/XorOutputStream.h similarity index 100% rename from Sming/Components/Network/src/Data/Stream/XorOutputStream.h rename to Sming/Core/Data/Stream/XorOutputStream.h diff --git a/Sming/Core/HardwareSerial.h b/Sming/Core/HardwareSerial.h index b689452648..c2fe0cc17c 100644 --- a/Sming/Core/HardwareSerial.h +++ b/Sming/Core/HardwareSerial.h @@ -26,6 +26,8 @@ #define NUMBER_UARTS UART_COUNT ///< Quantity of UARTs available +#define SERIAL_PIN_DEFAULT UART_PIN_DEFAULT + class HardwareSerial; /** @brief Delegate callback type for serial data reception @@ -132,7 +134,7 @@ class HardwareSerial : public ReadWriteStream */ void begin(uint32_t baud = 9600) { - begin(baud, SERIAL_8N1, SERIAL_FULL, 1); + begin(baud, SERIAL_8N1, SERIAL_FULL, SERIAL_PIN_DEFAULT); } /** @@ -145,7 +147,7 @@ class HardwareSerial : public ReadWriteStream */ void begin(uint32_t baud, SerialConfig config) { - begin(baud, config, SERIAL_FULL, 1); + begin(baud, config, SERIAL_FULL, SERIAL_PIN_DEFAULT); } /** @@ -170,7 +172,7 @@ class HardwareSerial : public ReadWriteStream * @param txPin Can specify alternate pin for TX * @param rxPin */ - void begin(uint32_t baud, SerialConfig config, SerialMode mode, uint8_t txPin, uint8_t rxPin = UART_PIN_DEFAULT); + void begin(uint32_t baud, SerialConfig config, SerialMode mode, uint8_t txPin, uint8_t rxPin = SERIAL_PIN_DEFAULT); /** * @brief De-inits the current UART if it is already used diff --git a/Sming/Libraries/LittleFS b/Sming/Libraries/LittleFS index 21f3969a64..e02efce58e 160000 --- a/Sming/Libraries/LittleFS +++ b/Sming/Libraries/LittleFS @@ -1 +1 @@ -Subproject commit 21f3969a6407771d8823e4eac3a3b266d1772ba6 +Subproject commit e02efce58e6996601255e59c3c688d583f1b843d diff --git a/Sming/Libraries/SmingTest b/Sming/Libraries/SmingTest index 36913db2ce..29ca79605d 160000 --- a/Sming/Libraries/SmingTest +++ b/Sming/Libraries/SmingTest @@ -1 +1 @@ -Subproject commit 36913db2ce3a5be82abb90d2140988307adfc38d +Subproject commit 29ca79605d2ec29c06c71ce23583ee74fc16e879 diff --git a/Sming/Platform/Clocks.h b/Sming/Platform/Clocks.h index 6d9b71c40d..3e7ad28f8b 100644 --- a/Sming/Platform/Clocks.h +++ b/Sming/Platform/Clocks.h @@ -95,6 +95,11 @@ struct CpuCycleClock { return cpuFreq == eCF_160MHz; } + + static constexpr CpuFrequency cpuFrequency() + { + return cpuFreq; + } }; #include_next diff --git a/Sming/System/include/gdb/gdb_hooks.h b/Sming/System/include/gdb/gdb_hooks.h index 5a0c28adf4..98bc1fc21e 100644 --- a/Sming/System/include/gdb/gdb_hooks.h +++ b/Sming/System/include/gdb/gdb_hooks.h @@ -55,8 +55,10 @@ void gdb_enable(bool state); #ifdef ENABLE_GDB #ifdef ARCH_HOST #define gdb_do_break() __asm__("int $0x03") -#else +#elif defined(ARCH_ESP8266) #define gdb_do_break() __asm__("break 0,0") +#else +#define gdb_do_break() cpu_hal_break() #endif #else #define gdb_do_break() \ diff --git a/Sming/build.mk b/Sming/build.mk index 151f1411b2..0d3a480527 100644 --- a/Sming/build.mk +++ b/Sming/build.mk @@ -87,7 +87,7 @@ endif export SMING_HOME export COMPILE := gcc -DEBUG_VARS += ARCH_BASE USER_LIBDIR OUT_BASE BUILD_BASE FW_BASE TOOLS_BASE +DEBUG_VARS += ARCH_BASE ARCH_BASE := $(SMING_HOME)/Arch/$(SMING_ARCH) ARCH_SYS = $(ARCH_BASE)/System @@ -95,11 +95,6 @@ ARCH_CORE = $(ARCH_BASE)/Core ARCH_TOOLS = $(ARCH_BASE)/Tools ARCH_COMPONENTS = $(ARCH_BASE)/Components -OUT_BASE := out/$(SMING_ARCH)/$(BUILD_TYPE) -BUILD_BASE = $(OUT_BASE)/build -FW_BASE = $(OUT_BASE)/firmware -TOOLS_BASE = $(SMING_HOME)/$(OUT_BASE)/tools -USER_LIBDIR = $(SMING_HOME)/$(OUT_BASE)/lib # Git command DEBUG_VARS += GIT @@ -194,8 +189,6 @@ endif include $(ARCH_BASE)/build.mk -DEBUG_VARS += ESP_VARIANT - # Detect compiler version DEBUG_VARS += GCC_VERSION GCC_VERSION := $(shell $(CC) -dumpversion) @@ -223,6 +216,20 @@ $(info Instructions for upgrading your compiler can be found here: $(GCC_UPGRADE endif endif +DEBUG_VARS += USER_LIBDIR OUT_BASE BUILD_BASE FW_BASE TOOLS_BASE SMING_ARCH_FULL + +ifdef ESP_VARIANT +SMING_ARCH_FULL := $(SMING_ARCH)/$(ESP_VARIANT) +else +SMING_ARCH_FULL := $(SMING_ARCH) +endif + +OUT_BASE := out/$(SMING_ARCH_FULL)/$(BUILD_TYPE) +BUILD_BASE = $(OUT_BASE)/build +FW_BASE = $(OUT_BASE)/firmware +TOOLS_BASE = $(SMING_HOME)/$(OUT_BASE)/tools +USER_LIBDIR = $(SMING_HOME)/$(OUT_BASE)/lib + # Component (user) libraries have a special prefix so linker script can identify them CLIB_PREFIX := clib- diff --git a/Sming/project.mk b/Sming/project.mk index 2da18baf55..828ef31c58 100644 --- a/Sming/project.mk +++ b/Sming/project.mk @@ -25,6 +25,9 @@ all: checkdirs submodules ##(default) Build all Component libraries BUILD_TYPE_FILE := out/build-type.mk -include $(BUILD_TYPE_FILE) +BUILD_SUBTYPE_FILE = out/$(SMING_ARCH)/build-subtype.mk +-include $(BUILD_SUBTYPE_FILE) + # include $(SMING_HOME)/build.mk @@ -59,7 +62,7 @@ export PROJECT_DIR ifeq ($(MAKELEVEL),0) $(info ) -$(info $(notdir $(PROJECT_DIR)): Invoking '$(MAKECMDGOALS)' for $(SMING_ARCH).$(ESP_VARIANT) ($(BUILD_TYPE)) architecture) +$(info $(notdir $(PROJECT_DIR)): Invoking '$(MAKECMDGOALS)' for $(SMING_ARCH_FULL) ($(BUILD_TYPE)) architecture) endif # CFLAGS used for application and any custom targets @@ -546,14 +549,16 @@ otaserver: all ##Launch a simple python HTTP server for testing OTA updates $(Q) cd $(FW_BASE) && $(PYTHON) -m http.server $(SERVER_OTA_PORT) -# +# Serial redirector to support GDB +TCP_SERIAL_REDIRECT = $(SMING_HOME)/../Tools/tcp_serial_redirect.py $(COM_PORT) $(COM_SPEED_SERIAL) --rts 0 --dtr 0 + .PHONY: tcp-serial-redirect tcp-serial-redirect: ##Redirect COM port to TCP port $(info Starting serial redirector) ifdef WSL_ROOT - $(Q) cmd.exe /c start /MIN python3 $(WSL_ROOT)/$(SMING_HOME)/../Tools/tcp_serial_redirect.py $(COM_PORT) $(COM_SPEED_SERIAL) + $(Q) cmd.exe /c start /MIN python3 $(WSL_ROOT)/$(TCP_SERIAL_REDIRECT) else - $(Q) gnome-terminal -- bash -c "$(PYTHON) $(SMING_HOME)/../Tools/tcp_serial_redirect.py $(COM_PORT) $(COM_SPEED_SERIAL)" + $(Q) gnome-terminal -- bash -c "$(PYTHON) $(TCP_SERIAL_REDIRECT)" endif @@ -647,7 +652,8 @@ $(shell mkdir -p $(dir $1); endef # Update build type cache -$(eval $(call WriteCacheValues,$(BUILD_TYPE_FILE),SMING_ARCH ESP_VARIANT SMING_RELEASE STRICT)) +$(eval $(call WriteCacheValues,$(BUILD_TYPE_FILE),SMING_ARCH SMING_RELEASE STRICT)) +$(eval $(call WriteCacheValues,$(BUILD_SUBTYPE_FILE),ESP_VARIANT)) # Update config cache file # We store the list of variable names to ensure that any not actively in use don't get lost diff --git a/docs/source/arch/esp32/debugging/index.rst b/docs/source/arch/esp32/debugging/index.rst index 94f909b367..b682dd714e 100644 --- a/docs/source/arch/esp32/debugging/index.rst +++ b/docs/source/arch/esp32/debugging/index.rst @@ -1,9 +1,22 @@ Debugging on ESP32 ================== +Serial debugging +---------------- + +If an exception occurs in debug builds then a prompt will be printed to the serial terminal +such as ``Entering gdb stub``. + +As with the ESP8266, if such an exception occurs you can stop the serial debug terminal and type ``make gdb``. + +More advanced debugging is available via JTAG if you have the appropriate tools. + +See https://docs.espressif.com/projects/esp-idf/en/release-v4.3/esp32/api-guides/fatal-errors.html for further details. + Required tools and hardware --------------------------- + A debugger and a JTAG hardware are required. The debugger is part of the provided toolchain. Make sure that you have the following executable in your PATH:: @@ -82,11 +95,11 @@ If you want to debug your application and the Sming Framework code make sure to (re)compile it with :envvar:`ENABLE_GDB` =1 directive:: cd $SMING_HOME/../samples/Basic_Blink - make dist-clean + make clean components-clean make ENABLE_GDB=1 The commands above will re-compile Sming with debug symbols and -optimizations for debugging. These commands need to be executed once. +optimizations for debugging. Application ~~~~~~~~~~~ diff --git a/docs/source/information/multitasking.rst b/docs/source/information/multitasking.rst index b7c5bcc68d..4220613862 100644 --- a/docs/source/information/multitasking.rst +++ b/docs/source/information/multitasking.rst @@ -54,6 +54,9 @@ connections and poor responsiveness. In extreme cases, the system will reset as via *Watchdog Timer*; If it didn't do this, the device would remain unresponsive until physically reset, which is generally a bad thing for an embedded device! +Note that whilst the ESP32 implementation uses the ESP IDF framework based on FreeRTOS, which is a pre-emptive OS, +the programming model for Sming is the same. + .. attention:: Although there are functions available to manually reset watchdog timers, you should endeavour to avoid diff --git a/samples/Basic_Blink/README.rst b/samples/Basic_Blink/README.rst index 81c1fa1a8a..62967a3a06 100644 --- a/samples/Basic_Blink/README.rst +++ b/samples/Basic_Blink/README.rst @@ -1,9 +1,18 @@ Basic Blink =========== -Simple blink example. We use Timer instead of a loop because we want to allow WiFi communications to work in the background. +Simple blink example to confirm that the basic build system is working with your system. + +We use Timer instead of a loop because we want to allow WiFi communications to work in the background. +See :doc:`/information/multitasking`. + +The LED on many development boards is connected to GPIO2, so this is the default. + +If you get no response then check the documentation or schematic as your system +may differ and change the LED_PIN definition accordingly. + +For example, the NodeMCU ESP-C3 kits have an RGB LED connected to GPIO 3, 4 & 5. + .. image:: blink.jpg :height: 192px - - \ No newline at end of file diff --git a/samples/Basic_Serial/app/application.cpp b/samples/Basic_Serial/app/application.cpp index 02548412d1..f580e081da 100644 --- a/samples/Basic_Serial/app/application.cpp +++ b/samples/Basic_Serial/app/application.cpp @@ -12,18 +12,12 @@ struct SerialPins { SerialPins serialPins[2]{ { - UART_PIN_DEFAULT, - UART_PIN_DEFAULT, + SERIAL_PIN_DEFAULT, + SERIAL_PIN_DEFAULT, }, { -#ifdef ESP32 - // Default pins are occupied by flash lines - 17, - 16, -#else - UART_PIN_DEFAULT, - UART_PIN_DEFAULT, -#endif + SERIAL_PIN_DEFAULT, + SERIAL_PIN_DEFAULT, }, }; diff --git a/tests/HostTests/modules/HttpRequest.cpp b/tests/HostTests/Arch/Host/HttpRequest.cpp similarity index 97% rename from tests/HostTests/modules/HttpRequest.cpp rename to tests/HostTests/Arch/Host/HttpRequest.cpp index bf061a3b36..04123022d3 100644 --- a/tests/HostTests/modules/HttpRequest.cpp +++ b/tests/HostTests/Arch/Host/HttpRequest.cpp @@ -109,8 +109,5 @@ class HttpRequestTest : public TestGroup void REGISTER_TEST(HttpRequest) { - // Currently only supported for Host CI -#if defined(ARCH_HOST) registerGroup(); -#endif } diff --git a/tests/HostTests/modules/TcpClient.cpp b/tests/HostTests/Arch/Host/TcpClient.cpp similarity index 98% rename from tests/HostTests/modules/TcpClient.cpp rename to tests/HostTests/Arch/Host/TcpClient.cpp index e22d02391e..0a9a9383ef 100644 --- a/tests/HostTests/modules/TcpClient.cpp +++ b/tests/HostTests/Arch/Host/TcpClient.cpp @@ -98,7 +98,5 @@ class TcpClientTest : public TestGroup void REGISTER_TEST(TcpClient) { -#ifdef ARCH_HOST registerGroup(); -#endif } diff --git a/tests/HostTests/include/modules.h b/tests/HostTests/include/modules.h index c1d8ec57d8..676fea8fc8 100644 --- a/tests/HostTests/include/modules.h +++ b/tests/HostTests/include/modules.h @@ -2,7 +2,10 @@ // Architecture-specific test modules #ifdef ARCH_HOST -#define ARCH_TEST_MAP(XX) XX(Hosted) +#define ARCH_TEST_MAP(XX) \ + XX(Hosted) \ + XX(HttpRequest) \ + XX(TcpClient) #else #define ARCH_TEST_MAP(XX) #endif @@ -33,6 +36,4 @@ XX(Rational) \ XX(Clocks) \ XX(Timers) \ - XX(HttpRequest) \ - XX(TcpClient) \ ARCH_TEST_MAP(XX) diff --git a/tests/HostTests/modules/Clocks.cpp b/tests/HostTests/modules/Clocks.cpp index 8987fbec0e..a1d251483a 100644 --- a/tests/HostTests/modules/Clocks.cpp +++ b/tests/HostTests/modules/Clocks.cpp @@ -23,12 +23,6 @@ template class ClockTestTemplate : public TestG void execute() override { - if(Clock::frequency() == 160000000) { - System.setCpuFrequency(eCF_160MHz); - } else { - System.setCpuFrequency(eCF_80MHz); - } - printLimits(); for(unsigned i = 0; i < 2000; ++i) { @@ -67,10 +61,11 @@ template class ClockTestTemplate : public TestG this->timeunit = unit; - noInterrupts(); - + // valueIsTime = true; - this->value = value % (TimeSource::maxCalcTime() + 1); + this->value = value % TimeSource::maxCalcTime(); + + noInterrupts(); refCycles.start(); ref = timeToTicksRef(); refCycles.update(); @@ -78,6 +73,7 @@ template class ClockTestTemplate : public TestG calcCycles.start(); calc = TimeSource::timeToTicks(this->value); calcCycles.update(); + interrupts(); if(calc != ref) { calc = TimeSource::timeToTicks(this->value); @@ -85,8 +81,11 @@ template class ClockTestTemplate : public TestG compare(); + // valueIsTime = false; - this->value = value % (TimeSource::maxCalcTicks() + 1); + this->value = value % TimeSource::maxCalcTicks(); + + noInterrupts(); refCycles.start(); ref = ticksToTimeRef(); refCycles.update(); @@ -94,9 +93,8 @@ template class ClockTestTemplate : public TestG calcCycles.start(); calc = TimeSource::ticksToTime(this->value); calcCycles.update(); - compare(); - interrupts(); + compare(); } void printStats() @@ -240,6 +238,23 @@ template class ClockTestTemplate : public TestG CpuCycleTimes calcCycles; }; +template class CpuClockTestTemplate : public ClockTestTemplate +{ +public: + using ClockTestTemplate::ClockTestTemplate; + + void execute() override + { + uint32_t curFreq = system_get_cpu_freq(); + System.setCpuFrequency(Clock::cpuFrequency()); + + // delay(100); + debug_i("CPU freq: %u -> %u MHz", curFreq, system_get_cpu_freq()); + ClockTestTemplate::execute(); + System.setCpuFrequency(CpuCycleClockNormal::cpuFrequency()); + } +}; + /* * Why use a Polled timer? Comparison versus hand-coded loops. */ @@ -463,6 +478,9 @@ void REGISTER_TEST(Clocks) registerGroup>(); - registerGroup>(); - registerGroup>(); + if(CpuCycleClockSlow::cpuFrequency() != CpuCycleClockNormal::cpuFrequency()) { + registerGroup>(); + } + registerGroup>(); + registerGroup>(); } diff --git a/tests/HostTests/modules/Libc.cpp b/tests/HostTests/modules/Libc.cpp index dacdcc3459..2ae0596fa1 100644 --- a/tests/HostTests/modules/Libc.cpp +++ b/tests/HostTests/modules/Libc.cpp @@ -39,25 +39,18 @@ class LibcTest : public TestGroup { using namespace libc_initorder; debug_i("order: %d, %d, %d, %d", a1.order, a2.order, a3.order, a4.order); -#ifdef ARCH_ESP32 - /* - * Bit odd this one. There's even a test case in components/cxx/test/test_initialization.cpp - * which is essentially identical to this test. - * - * This order isn't technically wrong since our init_priority attributes are being honoured. - * - * TODO: What going on? - */ - REQUIRE(a1.order == 3); - REQUIRE(a2.order == 4); - REQUIRE(a3.order == 1); - REQUIRE(a4.order == 2); -#else - REQUIRE(a1.order == 1); - REQUIRE(a2.order == 2); - REQUIRE(a3.order == 3); - REQUIRE(a4.order == 4); -#endif + // Compilers may legitimately order these in two different ways + if(a1.order == 3) { + REQUIRE(a1.order == 3); + REQUIRE(a2.order == 4); + REQUIRE(a3.order == 1); + REQUIRE(a4.order == 2); + } else { + REQUIRE(a1.order == 1); + REQUIRE(a2.order == 2); + REQUIRE(a3.order == 3); + REQUIRE(a4.order == 4); + } } #ifdef ARCH_ESP8266 diff --git a/tests/HostTests/modules/Stream.cpp b/tests/HostTests/modules/Stream.cpp index 2a1194c641..d77afc8f8e 100644 --- a/tests/HostTests/modules/Stream.cpp +++ b/tests/HostTests/modules/Stream.cpp @@ -168,7 +168,14 @@ class StreamTest : public TestGroup debug_hex(DBG, "Text", unmaskedString.c_str(), unmaskedString.length()); } + { + // STL may perform one-time memory allocation for mutexes, etc. + std::shared_ptr data(new char[18]); + SharedMemoryStream(data, 18); + } + auto memStart = MallocCount::getCurrent(); + // auto memStart = system_get_free_heap_size(); TEST_CASE("SharedMemoryStream") { @@ -203,8 +210,9 @@ class StreamTest : public TestGroup REQUIRE(data.use_count() == 1); } - debug_i("memStart = %d, now mem = %d", memStart, MallocCount::getCurrent()); - REQUIRE(memStart == MallocCount::getCurrent()); + auto memNow = MallocCount::getCurrent(); + // auto memNow = system_get_free_heap_size(); + REQUIRE_EQ(memStart, memNow); } private: From f37c5e016c5f227cfec14e07c1f93dcf25d2c5be Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 16 Sep 2021 16:50:48 +0100 Subject: [PATCH 056/130] Move all internal UART code into anonymous namespace (#2367) `getDevice` especially should not be globally visible. Do this for all implementions, also allows code can be compared more easily. --- Sming/Arch/Esp32/Components/driver/uart.cpp | 292 ++++++------ Sming/Arch/Esp8266/Components/driver/uart.cpp | 428 +++++++++--------- Sming/Arch/Host/Components/driver/uart.cpp | 117 ++--- 3 files changed, 412 insertions(+), 425 deletions(-) diff --git a/Sming/Arch/Esp32/Components/driver/uart.cpp b/Sming/Arch/Esp32/Components/driver/uart.cpp index 72968a664e..8538732951 100644 --- a/Sming/Arch/Esp32/Components/driver/uart.cpp +++ b/Sming/Arch/Esp32/Components/driver/uart.cpp @@ -20,6 +20,8 @@ #include #include +namespace +{ /* * Parameters relating to RX FIFO and buffer thresholds * @@ -38,10 +40,10 @@ */ #define DEFAULT_RX_HEADROOM (32 - RX_FIFO_HEADROOM) -static int s_uart_debug_nr = UART_NO; +int s_uart_debug_nr = UART_NO; // Keep track of interrupt enable state for each UART -static uint8_t isrMask; +uint8_t isrMask; struct smg_uart_pins_t { uint8_t tx; @@ -98,22 +100,22 @@ struct smg_uart_instance_t { intr_handle_t handle; }; -static smg_uart_instance_t uartInstances[UART_COUNT]; +smg_uart_instance_t uartInstances[UART_COUNT]; // Get number of characters in transmit FIFO -__forceinline static size_t uart_txfifo_count(uart_dev_t* dev) +__forceinline size_t uart_txfifo_count(uart_dev_t* dev) { return dev->status.txfifo_cnt; } // Get available free characters in transmit FIFO -__forceinline static size_t uart_txfifo_free(uart_dev_t* dev) +__forceinline size_t uart_txfifo_free(uart_dev_t* dev) { return UART_TX_FIFO_SIZE - uart_txfifo_count(dev) - 1; } // Return true if transmit FIFO is full -__forceinline static bool uart_txfifo_full(uart_dev_t* dev) +__forceinline bool uart_txfifo_full(uart_dev_t* dev) { return uart_txfifo_count(dev) >= (UART_TX_FIFO_SIZE - 1); } @@ -122,7 +124,7 @@ __forceinline static bool uart_txfifo_full(uart_dev_t* dev) * @param uart * @param code */ -static void notify(smg_uart_t* uart, smg_uart_notify_code_t code) +void notify(smg_uart_t* uart, smg_uart_notify_code_t code) { auto callback = uartInstances[uart->uart_nr].callback; if(callback != nullptr) { @@ -130,68 +132,31 @@ static void notify(smg_uart_t* uart, smg_uart_notify_code_t code) } } -__forceinline static bool uart_isr_enabled(uint8_t nr) +__forceinline bool uart_isr_enabled(uint8_t nr) { return bitRead(isrMask, nr); } -smg_uart_t* smg_uart_get_uart(uint8_t uart_nr) -{ - return (uart_nr < UART_COUNT) ? uartInstances[uart_nr].uart : nullptr; -} - -uint8_t smg_uart_disable_interrupts() -{ - // ETS_UART_INTR_DISABLE(); - return isrMask; -} - -void smg_uart_restore_interrupts() -{ - if(isrMask != 0) { - // ETS_UART_INTR_ENABLE(); - } -} - -bool smg_uart_set_notify(unsigned uart_nr, smg_uart_notify_callback_t callback) -{ - if(uart_nr >= UART_COUNT) { - return false; - } - - uartInstances[uart_nr].callback = callback; - return true; -} - /** @brief Determine if the given uart is a real uart or a virtual one */ -static __forceinline bool is_physical(int uart_nr) +__forceinline bool is_physical(int uart_nr) { return (uart_nr >= 0) && (uart_nr < UART_PHYSICAL_COUNT); } -static __forceinline bool is_physical(smg_uart_t* uart) +__forceinline bool is_physical(smg_uart_t* uart) { return uart != nullptr && is_physical(uart->uart_nr); } /** @brief If given a virtual uart, obtain the related physical one */ -static smg_uart_t* get_physical(smg_uart_t* uart) +smg_uart_t* get_physical(smg_uart_t* uart) { return uart; } -void smg_uart_set_callback(smg_uart_t* uart, smg_uart_callback_t callback, void* param) -{ - if(uart != nullptr) { - uart->callback = nullptr; // In case interrupt fires between setting param and callback - uart->param = param; - uart->callback = callback; - } -} - -static bool realloc_buffer(SerialBuffer*& buffer, size_t new_size) +bool realloc_buffer(SerialBuffer*& buffer, size_t new_size) { if(buffer != nullptr) { if(new_size == 0) { @@ -221,6 +186,140 @@ static bool realloc_buffer(SerialBuffer*& buffer, size_t new_size) return false; } +/** @brief UART interrupt service routine + * @note both UARTS share the same ISR, although UART1 only supports transmit + */ +void IRAM_ATTR uart_isr(smg_uart_instance_t* inst) +{ + if(inst == nullptr || inst->uart == nullptr) { + return; + } + + auto uart = inst->uart; + auto dev = getDevice(uart->uart_nr); + + decltype(uart_dev_t::int_st) usis; + usis.val = dev->int_st.val; + + // If status is clear there's no interrupt to service on this UART + if(usis.val == 0) { + return; + } + + // Value to be passed to callback + auto status = usis; + + // Deal with the event, unless we're in raw mode + if(!bitRead(uart->options, UART_OPT_CALLBACK_RAW)) { + // Rx FIFO full or timeout + if(usis.rxfifo_full || usis.rxfifo_tout || usis.rxfifo_ovf) { + size_t read = 0; + + // Read as much data as possible from the RX FIFO into buffer + if(uart->rx_buffer != nullptr) { + size_t avail = uart_ll_get_rxfifo_len(dev); + size_t space = uart->rx_buffer->getFreeSpace(); + read = (avail <= space) ? avail : space; + space -= read; + uint8_t buf[UART_RX_FIFO_SIZE]; + uart_ll_read_rxfifo(dev, buf, read); + uint8_t* ptr = buf; + while(read-- != 0) { + uart->rx_buffer->writeChar(*ptr++); + } + + // Don't call back until buffer is (almost) full + if(space > uart->rx_headroom) { + status.rxfifo_full = false; + } + } + + /* + * If the FIFO is full and we didn't read any of the data then need to mask the interrupt out or it'll recur. + * The interrupt gets re-enabled by a call to uart_read() or uart_flush() + */ + if(usis.rxfifo_ovf) { + uart_ll_disable_intr_mask(dev, UART_INTR_RXFIFO_OVF); + } else if(read == 0) { + uart_ll_ena_intr_mask(dev, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT); + } + } + + // Unless we replenish TX FIFO, disable after handling interrupt + if(usis.txfifo_empty) { + // Dump as much data as we can from buffer into the TX FIFO + if(uart->tx_buffer != nullptr) { + size_t space = uart_txfifo_free(dev); + size_t avail = uart->tx_buffer->available(); + size_t count = std::min(avail, space); + uint8_t buf[count]; + for(unsigned i = 0; i < count; ++i) { + buf[i] = uart->tx_buffer->readChar(); + } + uart_ll_write_txfifo(dev, buf, count); + } + + // If TX FIFO remains empty then we must disable TX FIFO EMPTY interrupt to stop it recurring. + if(uart_txfifo_count(dev) == 0) { + // The interrupt gets re-enabled by uart_write() + uart_ll_disable_intr_mask(dev, UART_INTR_TXFIFO_EMPTY); + } else { + // We've topped up TX FIFO so defer callback until next time + status.txfifo_empty = false; + } + } + } + + // Keep a note of persistent flags - cleared via uart_get_status() + uart->status |= status.val; + + if(status.val != 0 && uart->callback != nullptr) { + uart->callback(uart, status.val); + } + + // Final step is to clear status flags + dev->int_clr.val = usis.val; +} + +} // namespace + +smg_uart_t* smg_uart_get_uart(uint8_t uart_nr) +{ + return (uart_nr < UART_COUNT) ? uartInstances[uart_nr].uart : nullptr; +} + +uint8_t smg_uart_disable_interrupts() +{ + // ETS_UART_INTR_DISABLE(); + return isrMask; +} + +void smg_uart_restore_interrupts() +{ + if(isrMask != 0) { + // ETS_UART_INTR_ENABLE(); + } +} + +bool smg_uart_set_notify(unsigned uart_nr, smg_uart_notify_callback_t callback) +{ + if(uart_nr >= UART_COUNT) { + return false; + } + + uartInstances[uart_nr].callback = callback; + return true; +} + +void smg_uart_set_callback(smg_uart_t* uart, smg_uart_callback_t callback, void* param) +{ + if(uart != nullptr) { + uart->callback = nullptr; // In case interrupt fires between setting param and callback + uart->param = param; + uart->callback = callback; + } +} + size_t smg_uart_resize_rx_buffer(smg_uart_t* uart, size_t new_size) { if(smg_uart_rx_enabled(uart)) { @@ -319,101 +418,6 @@ size_t smg_uart_rx_available(smg_uart_t* uart) return avail; } -/** @brief UART interrupt service routine - * @note both UARTS share the same ISR, although UART1 only supports transmit - */ -static void IRAM_ATTR uart_isr(smg_uart_instance_t* inst) -{ - if(inst == nullptr || inst->uart == nullptr) { - return; - } - - auto uart = inst->uart; - auto dev = getDevice(uart->uart_nr); - - decltype(uart_dev_t::int_st) usis; - usis.val = dev->int_st.val; - - // If status is clear there's no interrupt to service on this UART - if(usis.val == 0) { - return; - } - - // Value to be passed to callback - auto status = usis; - - // Deal with the event, unless we're in raw mode - if(!bitRead(uart->options, UART_OPT_CALLBACK_RAW)) { - // Rx FIFO full or timeout - if(usis.rxfifo_full || usis.rxfifo_tout || usis.rxfifo_ovf) { - size_t read = 0; - - // Read as much data as possible from the RX FIFO into buffer - if(uart->rx_buffer != nullptr) { - size_t avail = uart_ll_get_rxfifo_len(dev); - size_t space = uart->rx_buffer->getFreeSpace(); - read = (avail <= space) ? avail : space; - space -= read; - uint8_t buf[UART_RX_FIFO_SIZE]; - uart_ll_read_rxfifo(dev, buf, read); - uint8_t* ptr = buf; - while(read-- != 0) { - uart->rx_buffer->writeChar(*ptr++); - } - - // Don't call back until buffer is (almost) full - if(space > uart->rx_headroom) { - status.rxfifo_full = false; - } - } - - /* - * If the FIFO is full and we didn't read any of the data then need to mask the interrupt out or it'll recur. - * The interrupt gets re-enabled by a call to uart_read() or uart_flush() - */ - if(usis.rxfifo_ovf) { - uart_ll_disable_intr_mask(dev, UART_INTR_RXFIFO_OVF); - } else if(read == 0) { - uart_ll_ena_intr_mask(dev, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT); - } - } - - // Unless we replenish TX FIFO, disable after handling interrupt - if(usis.txfifo_empty) { - // Dump as much data as we can from buffer into the TX FIFO - if(uart->tx_buffer != nullptr) { - size_t space = uart_txfifo_free(dev); - size_t avail = uart->tx_buffer->available(); - size_t count = std::min(avail, space); - uint8_t buf[count]; - for(unsigned i = 0; i < count; ++i) { - buf[i] = uart->tx_buffer->readChar(); - } - uart_ll_write_txfifo(dev, buf, count); - } - - // If TX FIFO remains empty then we must disable TX FIFO EMPTY interrupt to stop it recurring. - if(uart_txfifo_count(dev) == 0) { - // The interrupt gets re-enabled by uart_write() - uart_ll_disable_intr_mask(dev, UART_INTR_TXFIFO_EMPTY); - } else { - // We've topped up TX FIFO so defer callback until next time - status.txfifo_empty = false; - } - } - } - - // Keep a note of persistent flags - cleared via uart_get_status() - uart->status |= status.val; - - if(status.val != 0 && uart->callback != nullptr) { - uart->callback(uart, status.val); - } - - // Final step is to clear status flags - dev->int_clr.val = usis.val; -} - void smg_uart_start_isr(smg_uart_t* uart) { if(!is_physical(uart)) { diff --git a/Sming/Arch/Esp8266/Components/driver/uart.cpp b/Sming/Arch/Esp8266/Components/driver/uart.cpp index 22114d3818..5d3691c008 100644 --- a/Sming/Arch/Esp8266/Components/driver/uart.cpp +++ b/Sming/Arch/Esp8266/Components/driver/uart.cpp @@ -67,45 +67,47 @@ */ #define DEFAULT_RX_HEADROOM (32 - RX_FIFO_HEADROOM) -static int s_uart_debug_nr = UART_NO; +namespace +{ +int s_uart_debug_nr = UART_NO; // Get number of characters in receive FIFO -__forceinline static uint8_t uart_rxfifo_count(uint8_t nr) +__forceinline uint8_t uart_rxfifo_count(uint8_t nr) { return (READ_PERI_REG(UART_STATUS(nr)) >> UART_RXFIFO_CNT_S) & UART_RXFIFO_CNT; } // Get number of characters in transmit FIFO -__forceinline static uint8_t uart_txfifo_count(uint8_t nr) +__forceinline uint8_t uart_txfifo_count(uint8_t nr) { return (READ_PERI_REG(UART_STATUS(nr)) >> UART_TXFIFO_CNT_S) & UART_TXFIFO_CNT; } // Get available free characters in transmit FIFO -__forceinline static uint8_t uart_txfifo_free(uint8_t nr) +__forceinline uint8_t uart_txfifo_free(uint8_t nr) { return UART_TX_FIFO_SIZE - uart_txfifo_count(nr) - 1; } // Return true if transmit FIFO is full -__forceinline static bool uart_txfifo_full(uint8_t nr) +__forceinline bool uart_txfifo_full(uint8_t nr) { return uart_txfifo_count(nr) >= (UART_TX_FIFO_SIZE - 1); } // Keep track of interrupt enable state for each UART -static uint8_t isrMask; +uint8_t isrMask; // Keep a reference to all created UARTS - required because they share an ISR -static smg_uart_t* uartInstances[UART_COUNT]; +smg_uart_t* uartInstances[UART_COUNT]; // Registered port callback functions -static smg_uart_notify_callback_t notifyCallbacks[UART_COUNT]; +smg_uart_notify_callback_t notifyCallbacks[UART_COUNT]; /** @brief Invoke a port callback, if one has been registered * @param uart * @param code */ -static void notify(smg_uart_t* uart, smg_uart_notify_code_t code) +void notify(smg_uart_t* uart, smg_uart_notify_code_t code) { auto callback = notifyCallbacks[uart->uart_nr]; if(callback != nullptr) { @@ -113,54 +115,26 @@ static void notify(smg_uart_t* uart, smg_uart_notify_code_t code) } } -__forceinline static bool uart_isr_enabled(uint8_t nr) +__forceinline bool uart_isr_enabled(uint8_t nr) { return bitRead(isrMask, nr); } -smg_uart_t* smg_uart_get_uart(uint8_t uart_nr) -{ - return (uart_nr < UART_COUNT) ? uartInstances[uart_nr] : nullptr; -} - -uint8_t smg_uart_disable_interrupts() -{ - ETS_UART_INTR_DISABLE(); - return isrMask; -} - -void smg_uart_restore_interrupts() -{ - if(isrMask != 0) { - ETS_UART_INTR_ENABLE(); - } -} - -bool smg_uart_set_notify(unsigned uart_nr, smg_uart_notify_callback_t callback) -{ - if(uart_nr >= UART_COUNT) { - return false; - } - - notifyCallbacks[uart_nr] = callback; - return true; -} - /** @brief Determine if the given uart is a real uart or a virtual one */ -static __forceinline bool is_physical(int uart_nr) +__forceinline bool is_physical(int uart_nr) { return (uart_nr >= 0) && (uart_nr < UART_PHYSICAL_COUNT); } -static __forceinline bool is_physical(smg_uart_t* uart) +__forceinline bool is_physical(smg_uart_t* uart) { return uart != nullptr && is_physical(uart->uart_nr); } /** @brief If given a virtual uart, obtain the related physical one */ -static smg_uart_t* get_physical(smg_uart_t* uart) +smg_uart_t* get_physical(smg_uart_t* uart) { if(uart != nullptr && uart->uart_nr == UART2) { uart = uartInstances[UART0]; @@ -168,16 +142,7 @@ static smg_uart_t* get_physical(smg_uart_t* uart) return uart; } -void smg_uart_set_callback(smg_uart_t* uart, smg_uart_callback_t callback, void* param) -{ - if(uart != nullptr) { - uart->callback = nullptr; // In case interrupt fires between setting param and callback - uart->param = param; - uart->callback = callback; - } -} - -static bool realloc_buffer(SerialBuffer*& buffer, size_t new_size) +bool realloc_buffer(SerialBuffer*& buffer, size_t new_size) { if(buffer != nullptr) { if(new_size == 0) { @@ -205,110 +170,12 @@ static bool realloc_buffer(SerialBuffer*& buffer, size_t new_size) return false; } -size_t smg_uart_resize_rx_buffer(smg_uart_t* uart, size_t new_size) -{ - if(smg_uart_rx_enabled(uart)) { - realloc_buffer(uart->rx_buffer, new_size); - } - return smg_uart_rx_buffer_size(uart); -} - -size_t smg_uart_rx_buffer_size(smg_uart_t* uart) -{ - return uart != nullptr && uart->rx_buffer != nullptr ? uart->rx_buffer->getSize() : 0; -} - -size_t smg_uart_resize_tx_buffer(smg_uart_t* uart, size_t new_size) -{ - if(smg_uart_tx_enabled(uart)) { - realloc_buffer(uart->tx_buffer, new_size); - } - return smg_uart_tx_buffer_size(uart); -} - -size_t smg_uart_tx_buffer_size(smg_uart_t* uart) -{ - return uart != nullptr && uart->tx_buffer != nullptr ? uart->tx_buffer->getSize() : 0; -} - -int smg_uart_peek_char(smg_uart_t* uart) -{ - return uart != nullptr && uart->rx_buffer ? uart->rx_buffer->peekChar() : -1; -} - -int smg_uart_rx_find(smg_uart_t* uart, char c) -{ - if(uart == nullptr || uart->rx_buffer == nullptr) { - return -1; - } - - return uart->rx_buffer->find(c); -} - -int smg_uart_peek_last_char(smg_uart_t* uart) -{ - return uart != nullptr && uart->rx_buffer != nullptr ? uart->rx_buffer->peekLastChar() : -1; -} - -size_t smg_uart_read(smg_uart_t* uart, void* buffer, size_t size) -{ - if(!smg_uart_rx_enabled(uart) || buffer == nullptr || size == 0) { - return 0; - } - - notify(uart, UART_NOTIFY_BEFORE_READ); - - size_t read = 0; - - auto buf = static_cast(buffer); - - // First read data from RX buffer if in use - if(uart->rx_buffer != nullptr) { - while(read < size && !uart->rx_buffer->isEmpty()) - buf[read++] = uart->rx_buffer->readChar(); - } - - // Top up from hardware FIFO - if(is_physical(uart)) { - while(read < size && uart_rxfifo_count(uart->uart_nr) != 0) { - buf[read++] = READ_PERI_REG(UART_FIFO(uart->uart_nr)); - } - - // FIFO full may have been disabled if buffer overflowed, re-enabled it now - WRITE_PERI_REG(UART_INT_CLR(uart->uart_nr), - UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR | UART_RXFIFO_OVF_INT_CLR); - SET_PERI_REG_MASK(UART_INT_ENA(uart->uart_nr), - UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA | UART_RXFIFO_OVF_INT_ENA); - } - - return read; -} - -size_t smg_uart_rx_available(smg_uart_t* uart) -{ - if(!smg_uart_rx_enabled(uart)) { - return 0; - } - - smg_uart_disable_interrupts(); - - size_t avail = is_physical(uart) ? uart_rxfifo_count(uart->uart_nr) : 0; - - if(uart->rx_buffer != nullptr) { - avail += uart->rx_buffer->available(); - } - - smg_uart_restore_interrupts(); - - return avail; -} - /** * @brief service interrupts for a UART * @param uart_nr identifies which UART to check * @param uart the allocated uart structure, which may be NULL if port hasn't been setup */ -static void IRAM_ATTR handle_uart_interrupt(uint8_t uart_nr, smg_uart_t* uart) +void IRAM_ATTR handle_uart_interrupt(uint8_t uart_nr, smg_uart_t* uart) { uint32_t usis = READ_PERI_REG(UART_INT_ST(uart_nr)); @@ -405,12 +272,210 @@ static void IRAM_ATTR handle_uart_interrupt(uint8_t uart_nr, smg_uart_t* uart) /** @brief UART interrupt service routine * @note both UARTS share the same ISR, although UART1 only supports transmit */ -static void IRAM_ATTR uart_isr(void* arg) +void IRAM_ATTR uart_isr(void* arg) { handle_uart_interrupt(UART0, uartInstances[UART0]); handle_uart_interrupt(UART1, uartInstances[UART1]); } +void uart0_pin_select(unsigned pin) +{ + switch(pin) { + case 1: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_UART0_TXD); + break; + case 2: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_UART0_TXD_BK); + break; + case 3: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_UART0_RXD); + break; + case 13: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_UART0_CTS); + break; + case 15: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_UART0_RTS); + break; + } +} + +void uart0_pin_restore(unsigned pin) +{ + switch(pin) { + case 1: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1); + break; + case 2: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2); + break; + case 3: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_GPIO3); + break; + case 13: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13); + break; + case 15: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15); + break; + } +} + +void uart1_pin_select(unsigned pin) +{ + // GPIO7 as TX not possible! See GPIO pins used by UART + switch(pin) { + case 2: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_UART1_TXD_BK); + break; + } +} + +void uart1_pin_restore(const unsigned pin) +{ + switch(pin) { + case 2: + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2); + break; + } +} + +} // namespace + +smg_uart_t* smg_uart_get_uart(uint8_t uart_nr) +{ + return (uart_nr < UART_COUNT) ? uartInstances[uart_nr] : nullptr; +} + +uint8_t smg_uart_disable_interrupts() +{ + ETS_UART_INTR_DISABLE(); + return isrMask; +} + +void smg_uart_restore_interrupts() +{ + if(isrMask != 0) { + ETS_UART_INTR_ENABLE(); + } +} + +bool smg_uart_set_notify(unsigned uart_nr, smg_uart_notify_callback_t callback) +{ + if(uart_nr >= UART_COUNT) { + return false; + } + + notifyCallbacks[uart_nr] = callback; + return true; +} + +void smg_uart_set_callback(smg_uart_t* uart, smg_uart_callback_t callback, void* param) +{ + if(uart != nullptr) { + uart->callback = nullptr; // In case interrupt fires between setting param and callback + uart->param = param; + uart->callback = callback; + } +} + +size_t smg_uart_resize_rx_buffer(smg_uart_t* uart, size_t new_size) +{ + if(smg_uart_rx_enabled(uart)) { + realloc_buffer(uart->rx_buffer, new_size); + } + return smg_uart_rx_buffer_size(uart); +} + +size_t smg_uart_rx_buffer_size(smg_uart_t* uart) +{ + return uart != nullptr && uart->rx_buffer != nullptr ? uart->rx_buffer->getSize() : 0; +} + +size_t smg_uart_resize_tx_buffer(smg_uart_t* uart, size_t new_size) +{ + if(smg_uart_tx_enabled(uart)) { + realloc_buffer(uart->tx_buffer, new_size); + } + return smg_uart_tx_buffer_size(uart); +} + +size_t smg_uart_tx_buffer_size(smg_uart_t* uart) +{ + return uart != nullptr && uart->tx_buffer != nullptr ? uart->tx_buffer->getSize() : 0; +} + +int smg_uart_peek_char(smg_uart_t* uart) +{ + return uart != nullptr && uart->rx_buffer ? uart->rx_buffer->peekChar() : -1; +} + +int smg_uart_rx_find(smg_uart_t* uart, char c) +{ + if(uart == nullptr || uart->rx_buffer == nullptr) { + return -1; + } + + return uart->rx_buffer->find(c); +} + +int smg_uart_peek_last_char(smg_uart_t* uart) +{ + return uart != nullptr && uart->rx_buffer != nullptr ? uart->rx_buffer->peekLastChar() : -1; +} + +size_t smg_uart_read(smg_uart_t* uart, void* buffer, size_t size) +{ + if(!smg_uart_rx_enabled(uart) || buffer == nullptr || size == 0) { + return 0; + } + + notify(uart, UART_NOTIFY_BEFORE_READ); + + size_t read = 0; + + auto buf = static_cast(buffer); + + // First read data from RX buffer if in use + if(uart->rx_buffer != nullptr) { + while(read < size && !uart->rx_buffer->isEmpty()) + buf[read++] = uart->rx_buffer->readChar(); + } + + // Top up from hardware FIFO + if(is_physical(uart)) { + while(read < size && uart_rxfifo_count(uart->uart_nr) != 0) { + buf[read++] = READ_PERI_REG(UART_FIFO(uart->uart_nr)); + } + + // FIFO full may have been disabled if buffer overflowed, re-enabled it now + WRITE_PERI_REG(UART_INT_CLR(uart->uart_nr), + UART_RXFIFO_FULL_INT_CLR | UART_RXFIFO_TOUT_INT_CLR | UART_RXFIFO_OVF_INT_CLR); + SET_PERI_REG_MASK(UART_INT_ENA(uart->uart_nr), + UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA | UART_RXFIFO_OVF_INT_ENA); + } + + return read; +} + +size_t smg_uart_rx_available(smg_uart_t* uart) +{ + if(!smg_uart_rx_enabled(uart)) { + return 0; + } + + smg_uart_disable_interrupts(); + + size_t avail = is_physical(uart) ? uart_rxfifo_count(uart->uart_nr) : 0; + + if(uart->rx_buffer != nullptr) { + avail += uart->rx_buffer->available(); + } + + smg_uart_restore_interrupts(); + + return avail; +} + void smg_uart_start_isr(smg_uart_t* uart) { if(!is_physical(uart)) { @@ -652,67 +717,6 @@ uint32_t smg_uart_get_baudrate(smg_uart_t* uart) return (uart == nullptr) ? 0 : uart->baud_rate; } -static void uart0_pin_select(unsigned pin) -{ - switch(pin) { - case 1: - PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_UART0_TXD); - break; - case 2: - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_UART0_TXD_BK); - break; - case 3: - PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_UART0_RXD); - break; - case 13: - PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_UART0_CTS); - break; - case 15: - PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_UART0_RTS); - break; - } -} - -static void uart0_pin_restore(unsigned pin) -{ - switch(pin) { - case 1: - PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_GPIO1); - break; - case 2: - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2); - break; - case 3: - PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_GPIO3); - break; - case 13: - PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13); - break; - case 15: - PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15); - break; - } -} - -static void uart1_pin_select(unsigned pin) -{ - // GPIO7 as TX not possible! See GPIO pins used by UART - switch(pin) { - case 2: - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_UART1_TXD_BK); - break; - } -} - -static void uart1_pin_restore(const unsigned pin) -{ - switch(pin) { - case 2: - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2); - break; - } -} - smg_uart_t* smg_uart_init_ex(const smg_uart_config_t& cfg) { // Already initialised? diff --git a/Sming/Arch/Host/Components/driver/uart.cpp b/Sming/Arch/Host/Components/driver/uart.cpp index 5e17567753..ff71fbd890 100644 --- a/Sming/Arch/Host/Components/driver/uart.cpp +++ b/Sming/Arch/Host/Components/driver/uart.cpp @@ -47,21 +47,23 @@ */ #define DEFAULT_RX_HEADROOM (32 - RX_FIFO_HEADROOM) -static int s_uart_debug_nr = UART_NO; +namespace +{ +int s_uart_debug_nr = UART_NO; // Keep track of interrupt enable state for each UART -static uint8_t isrMask; +uint8_t isrMask; // Keep a reference to all created UARTS - required because they share an ISR -static smg_uart_t* uartInstances[UART_COUNT]; +smg_uart_t* uartInstances[UART_COUNT]; // Registered port callback functions -static smg_uart_notify_callback_t notifyCallbacks[UART_COUNT]; +smg_uart_notify_callback_t notifyCallbacks[UART_COUNT]; /** @brief Invoke a port callback, if one has been registered * @param uart * @param code */ -static void notify(smg_uart_t* uart, smg_uart_notify_code_t code) +void notify(smg_uart_t* uart, smg_uart_notify_code_t code) { auto callback = notifyCallbacks[uart->uart_nr]; if(callback != nullptr) { @@ -69,11 +71,41 @@ static void notify(smg_uart_t* uart, smg_uart_notify_code_t code) } } -__forceinline static bool smg_uart_isr_enabled(uint8_t nr) +__forceinline bool smg_uart_isr_enabled(uint8_t nr) { return bitRead(isrMask, nr); } +bool realloc_buffer(SerialBuffer*& buffer, size_t new_size) +{ + if(buffer != nullptr) { + if(new_size == 0) { + (void)smg_uart_disable_interrupts(); + delete buffer; + buffer = nullptr; + smg_uart_restore_interrupts(); + return true; + } + + return buffer->resize(new_size) == new_size; + } + + if(new_size == 0) { + return true; + } + + auto new_buf = new SerialBuffer; + if(new_buf != nullptr && new_buf->resize(new_size) == new_size) { + buffer = new_buf; + return true; + } + + delete new_buf; + return false; +} + +} // namespace + smg_uart_t* smg_uart_get_uart(uint8_t uart_nr) { return (uart_nr < UART_COUNT) ? uartInstances[uart_nr] : nullptr; @@ -107,34 +139,6 @@ void smg_uart_set_callback(smg_uart_t* uart, smg_uart_callback_t callback, void* } } -static bool realloc_buffer(SerialBuffer*& buffer, size_t new_size) -{ - if(buffer != nullptr) { - if(new_size == 0) { - (void)smg_uart_disable_interrupts(); - delete buffer; - buffer = nullptr; - smg_uart_restore_interrupts(); - return true; - } - - return buffer->resize(new_size) == new_size; - } - - if(new_size == 0) { - return true; - } - - auto new_buf = new SerialBuffer; - if(new_buf != nullptr && new_buf->resize(new_size) == new_size) { - buffer = new_buf; - return true; - } - - delete new_buf; - return false; -} - size_t smg_uart_resize_rx_buffer(smg_uart_t* uart, size_t new_size) { if(smg_uart_rx_enabled(uart)) { @@ -289,30 +293,16 @@ void smg_uart_wait_tx_empty(smg_uart_t* uart) void smg_uart_set_break(smg_uart_t* uart, bool state) { - // uart = get_physical(uart); - // if(uart != nullptr) { - // bitWrite(USC0(uart->uart_nr), UCBRK, state); - // } + (void)uart; + (void)state; + // Not implemented } uint8_t smg_uart_get_status(smg_uart_t* uart) { - uint8_t status = 0; - // if(uart != nullptr) { - // uart_disable_interrupts(); - // // Get break/overflow flags from actual uart (physical or otherwise) - // status = uart->status & (_BV(UIBD) | _BV(UIOF)); - // uart->status = 0; - // // Read raw status register directly from real uart, masking out non-error bits - // uart = get_physical(uart); - // if(uart != nullptr) { - // status |= USIR(uart->uart_nr) & (_BV(UIBD) | _BV(UIOF) | _BV(UIFR) | _BV(UIPE)); - // // Clear errors - // USIC(uart->uart_nr) = status; - // } - // uart_restore_interrupts(); - // } - return status; + // Not implemented + (void)uart; + return 0; } void smg_uart_flush(smg_uart_t* uart, smg_uart_mode_t mode) @@ -338,14 +328,6 @@ void smg_uart_flush(smg_uart_t* uart, smg_uart_mode_t mode) uint32_t smg_uart_set_baudrate_reg(int uart_nr, uint32_t baud_rate) { - // if(!is_physical(uart_nr) || baud_rate == 0) { - // return 0; - // } - // - // uint32_t clkdiv = ESP8266_CLOCK / baud_rate; - // USD(uart_nr) = clkdiv; - // // Return the actual baud rate in use - // baud_rate = clkdiv ? ESP8266_CLOCK / clkdiv : 0; return baud_rate; } @@ -488,6 +470,9 @@ smg_uart_t* smg_uart_init(uint8_t uart_nr, uint32_t baudrate, uint32_t config, s void smg_uart_swap(smg_uart_t* uart, int tx_pin) { + (void)uart; + (void)tx_pin; + // Not implemented } bool smg_uart_set_tx(smg_uart_t* uart, int tx_pin) @@ -544,11 +529,5 @@ void smg_uart_detach(int uart_nr) void smg_uart_detach_all() { - // uart_disable_interrupts(); - // for(unsigned uart_nr = 0; uart_nr < UART_PHYSICAL_COUNT; ++uart_nr) { - // USC1(uart_nr) = 0; - // USIC(uart_nr) = 0xffff; - // USIE(uart_nr) = 0; - // } - // isrMask = 0; + // Not implemented } From e35b7de2a86340c561870f65510be7c0e526a940 Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 17 Sep 2021 14:05:24 +0100 Subject: [PATCH 057/130] Add `verifyflash` build target (#2368) * Don't default to 80Mhz flash speed in Basic_Storage sample May cause problems which are hard to diagnose * Add `verifyflash` build target --- Sming/Arch/Host/Components/vflash/component.mk | 6 ++++++ Sming/Components/esptool/component.mk | 9 +++++++++ Sming/component.mk | 5 +++++ docs/source/troubleshooting/random-restart.rst | 4 ++++ samples/Basic_Storage/basic_storage.hw | 2 +- 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Sming/Arch/Host/Components/vflash/component.mk b/Sming/Arch/Host/Components/vflash/component.mk index 98410de609..12eea0f2c4 100644 --- a/Sming/Arch/Host/Components/vflash/component.mk +++ b/Sming/Arch/Host/Components/vflash/component.mk @@ -24,6 +24,12 @@ define WriteFlash $(if $1,$(Q) $(VFLASH) write-chunks $1) endef +# Verify one or more chunks against flash content +# $1 -> List of `Offset=File` chunks +define VerifyFlash + @echo VerifyFlash not implemented for Host +endef + # Read flash memory into file # $1 -> `Offset,Size` chunk # $2 -> Output filename diff --git a/Sming/Components/esptool/component.mk b/Sming/Components/esptool/component.mk index 3c936f186f..bb5464e2da 100644 --- a/Sming/Components/esptool/component.mk +++ b/Sming/Components/esptool/component.mk @@ -52,6 +52,15 @@ define WriteFlash ) endef +# Verify flash against file contents +# $1 -> List of `Offset=File` chunks +define VerifyFlash + $(if $1,\ + $(info VerifyFlash $1) \ + $(call ESPTOOL_EXECUTE,verify_flash $(flashimageoptions) $(subst =, ,$1)) \ + ) +endef + # Read flash memory into file # $1 -> `Offset,Size` chunk # $2 -> Output filename diff --git a/Sming/component.mk b/Sming/component.mk index f97cb98ceb..c8caaef428 100644 --- a/Sming/component.mk +++ b/Sming/component.mk @@ -151,3 +151,8 @@ ifeq ($(ENABLE_GDB), 1) else ifneq ($(SMING_ARCH),Host) $(TERMINAL) endif + +.PHONY: verifyflash +verifyflash: ##Read all flash sections and verify against source + $(Q) $(call CheckPartitionChunks,$(FLASH_PARTITION_CHUNKS)) + $(call VerifyFlash,$(FLASH_BOOT_CHUNKS) $(FLASH_MAP_CHUNK) $(FLASH_PARTITION_CHUNKS)) diff --git a/docs/source/troubleshooting/random-restart.rst b/docs/source/troubleshooting/random-restart.rst index 10b5ec2a70..39698bc8d8 100644 --- a/docs/source/troubleshooting/random-restart.rst +++ b/docs/source/troubleshooting/random-restart.rst @@ -26,3 +26,7 @@ To achieve this do the following: 3) Re-program your device:: make flash + +4) Verify flash data has been written successfully + + make verifyflash diff --git a/samples/Basic_Storage/basic_storage.hw b/samples/Basic_Storage/basic_storage.hw index 4d28b7fa28..14e3419269 100644 --- a/samples/Basic_Storage/basic_storage.hw +++ b/samples/Basic_Storage/basic_storage.hw @@ -11,7 +11,7 @@ * https://sming.readthedocs.io/en/latest/_inc/Sming/Components/esptool/index.html#envvar-SPI_MODE. */ // "mode": "qio", - "speed": 80 + // "speed": 80 } }, "partitions": { From 90babe3c1f1e2580e847e0cf213eb2b58ee6e699 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 20 Sep 2021 08:21:59 +0100 Subject: [PATCH 058/130] Add `flashid` build target (#2369) Run make flashid to read flash manufacturer ID and infer actual size. Also adds config for Basic_Storage sample to support 2M flash. --- .../Arch/Host/Components/vflash/component.mk | 5 ++++ Sming/Components/esptool/component.mk | 6 ++++ Sming/component.mk | 4 +++ .../source/troubleshooting/random-restart.rst | 4 +++ samples/Basic_Storage/basic_storage_2m.hw | 28 +++++++++++++++++++ 5 files changed, 47 insertions(+) create mode 100644 samples/Basic_Storage/basic_storage_2m.hw diff --git a/Sming/Arch/Host/Components/vflash/component.mk b/Sming/Arch/Host/Components/vflash/component.mk index 12eea0f2c4..65f9df027a 100644 --- a/Sming/Arch/Host/Components/vflash/component.mk +++ b/Sming/Arch/Host/Components/vflash/component.mk @@ -30,6 +30,11 @@ define VerifyFlash @echo VerifyFlash not implemented for Host endef +# Read flash manufacturer ID and determine actual size +define ReadFlashID + $(info ReadFlashID: Flash backing file "$(FLASH_BIN)", size $(SPI_SIZE)) +endef + # Read flash memory into file # $1 -> `Offset,Size` chunk # $2 -> Output filename diff --git a/Sming/Components/esptool/component.mk b/Sming/Components/esptool/component.mk index bb5464e2da..ba8df84f7a 100644 --- a/Sming/Components/esptool/component.mk +++ b/Sming/Components/esptool/component.mk @@ -43,6 +43,12 @@ endif comma := , +# Read flash manufacturer ID and determine actual size +define ReadFlashID + $(info Reading Flash ID) + $(call ESPTOOL_EXECUTE,flash_id) +endef + # Write file contents to Flash # $1 -> List of `Offset=File` chunks define WriteFlash diff --git a/Sming/component.mk b/Sming/component.mk index c8caaef428..b24c7014e1 100644 --- a/Sming/component.mk +++ b/Sming/component.mk @@ -156,3 +156,7 @@ endif verifyflash: ##Read all flash sections and verify against source $(Q) $(call CheckPartitionChunks,$(FLASH_PARTITION_CHUNKS)) $(call VerifyFlash,$(FLASH_BOOT_CHUNKS) $(FLASH_MAP_CHUNK) $(FLASH_PARTITION_CHUNKS)) + +.PHONY: flashid +flashid: ##Read flash identifier and determine actual size + $(call ReadFlashID) diff --git a/docs/source/troubleshooting/random-restart.rst b/docs/source/troubleshooting/random-restart.rst index 39698bc8d8..749b29bcab 100644 --- a/docs/source/troubleshooting/random-restart.rst +++ b/docs/source/troubleshooting/random-restart.rst @@ -15,6 +15,10 @@ To achieve this do the following: 1) Check the :ref:`hardware_config` especially ``flash_size`` setting. +.. note:: + + If you're not sure what size the flash memory is on your device, run `make flashid` to check. + 2) Run ``flashinit``:: cd $SMING_HOME/../samples/Basic_Blink diff --git a/samples/Basic_Storage/basic_storage_2m.hw b/samples/Basic_Storage/basic_storage_2m.hw new file mode 100644 index 0000000000..bdbae34b1c --- /dev/null +++ b/samples/Basic_Storage/basic_storage_2m.hw @@ -0,0 +1,28 @@ +{ + "name": "Basic Storage sample (2M)", + "base_config": "basic_storage", + "devices": { + "spiFlash": { + "size": "2M" + } + }, + "partitions": { + "user0": { + "address": "0x000fa000" + }, + "user1": { + "address": "0x000fe000" + }, + "spiffs0": { + "address": "0x00102000" + }, + "spiffs1": { + "address": "0x00182000", + "size": "240K" + }, + "spiffs2": { + "address": "0x001be000", + "size": "216K" + } + } +} \ No newline at end of file From 8ff67a7d7685003bdd0d2bb9bb6d84d08fab7887 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 20 Sep 2021 08:26:50 +0100 Subject: [PATCH 059/130] Add ESP32 support to HardwareSPI library, add SPI ethernet (#2370) This PR adds support for ESP32 to the `HardwareSPI` library. Current implementation uses the existing ESP-IDF driver, with the addition of an `spi_device_queue_trans_from_isr` function. This ensures compatibility with other code using the IDF. ESP32 hardware has DMA which extends the maximum size of transactions from 64 bytes to 4092 bytes (one DMA buffer). Note: The API has changed so that the device clock speed is passed to `Device::begin()` and can no longer be changed dynamically for each request. This isn't supported by the IDF and is of dubious value anyway, so this approach is a bit simpler. The `Graphics` and `TFT_S1D13781` libraries have been updated accordingly. The Graphics samples (mostly) work OK on ESP32 hardware, tested using esp32-s2. Implementations for SPI ethernet interfaces have been added to the `Network` Component: **Wiznet W5500** Tested and working on esp32-wroom, esp32-s2. Doesn't work on esp32c3, but neither does IDF example. Pin allocations are taken from the IDF example spi_master/lcd. **DM9051** Not tested on hardware. **CI Testing** Add `Basic_Ethernet` sample to ESP32 builds. Allow override of IDF branch and repository for installation. Use `sming/dev/v4.3` branch for integration testing: Once PR is accepted changes can be pushed to `sming/release/v4.3`. --- .../Esp32/Components/esp32/sdk/config/common | 6 + .../esp32/src/include/esp_systemapi.h | 2 + Sming/Arch/Esp32/Tools/ci/build.run.cmd | 11 + Sming/Arch/Esp32/Tools/ci/build.run.sh | 8 +- Sming/Arch/Esp32/Tools/ci/install.cmd | 5 +- Sming/Arch/Esp32/Tools/install.sh | 4 +- .../Network/Arch/Esp32/Network/DM9051.cpp | 79 ++++++++ .../Network/Arch/Esp32/Network/W5500.cpp | 79 ++++++++ .../Network/Arch/Esp32/Network/spi_config.h | 43 ++++ .../Arch/Esp32/Platform/EmbeddedEthernet.cpp | 185 +---------------- .../Arch/Esp32/Platform/EthernetPhy.cpp | 6 + .../Arch/Esp32/Platform/IdfService.cpp | 191 ++++++++++++++++++ .../Esp32/include/Network/Ethernet/DM9051.h | 43 ++++ .../include/Network/Ethernet/SpiService.h | 34 ++++ .../Esp32/include/Network/Ethernet/W5500.h | 43 ++++ .../include/Platform/EmbeddedEthernet.h | 45 ++--- .../Arch/Esp32/include/Platform/IdfService.h | 58 ++++++ Sming/Components/Network/component.mk | 6 +- .../Network/src/Platform/Ethernet.h | 25 --- Sming/Libraries/Graphics | 2 +- Sming/Libraries/HardwareSPI | 2 +- Sming/Libraries/TFT_S1D13781 | 2 +- appveyor.yml | 1 + docs/source/framework/platform/ethernet.rst | 3 + samples/Basic_Ethernet/README.rst | 35 +++- samples/Basic_Ethernet/app/application.cpp | 39 +++- samples/Basic_Ethernet/component.mk | 1 + 27 files changed, 702 insertions(+), 256 deletions(-) create mode 100644 Sming/Components/Network/Arch/Esp32/Network/DM9051.cpp create mode 100644 Sming/Components/Network/Arch/Esp32/Network/W5500.cpp create mode 100644 Sming/Components/Network/Arch/Esp32/Network/spi_config.h create mode 100644 Sming/Components/Network/Arch/Esp32/Platform/IdfService.cpp create mode 100644 Sming/Components/Network/Arch/Esp32/include/Network/Ethernet/DM9051.h create mode 100644 Sming/Components/Network/Arch/Esp32/include/Network/Ethernet/SpiService.h create mode 100644 Sming/Components/Network/Arch/Esp32/include/Network/Ethernet/W5500.h rename Sming/Components/Network/Arch/Esp32/{Platform => }/include/Platform/EmbeddedEthernet.h (56%) create mode 100644 Sming/Components/Network/Arch/Esp32/include/Platform/IdfService.h diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/config/common b/Sming/Arch/Esp32/Components/esp32/sdk/config/common index 4ac31502b3..2d78a855f8 100644 --- a/Sming/Arch/Esp32/Components/esp32/sdk/config/common +++ b/Sming/Arch/Esp32/Components/esp32/sdk/config/common @@ -20,12 +20,18 @@ CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER=n # Ethernet CONFIG_ETH_USE_SPI_ETHERNET=y +CONFIG_ETH_SPI_ETHERNET_W5500=y +CONFIG_ETH_SPI_ETHERNET_DM9051=y # Mandatory Sming framework changes CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 CONFIG_ESP_TIMER_IMPL_FRC2=y +# Required by HardwareSPI library +CONFIG_SPI_MASTER_IN_IRAM=y +CONFIG_SPI_MASTER_ISR_IN_IRAM=y + # Disable VFS CONFIG_VFS_SUPPORT_IO=n CONFIG_VFS_SUPPORT_DIR=n diff --git a/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h b/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h index 04897d7545..89482e7071 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h +++ b/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include diff --git a/Sming/Arch/Esp32/Tools/ci/build.run.cmd b/Sming/Arch/Esp32/Tools/ci/build.run.cmd index 49677d701a..ad6fc47873 100644 --- a/Sming/Arch/Esp32/Tools/ci/build.run.cmd +++ b/Sming/Arch/Esp32/Tools/ci/build.run.cmd @@ -3,6 +3,17 @@ REM Esp32 build.run.cmd %MAKE_PARALLEL% Basic_Blink Basic_WiFi HttpServer_ConfigNetwork DEBUG_VERBOSE_LEVEL=3 STRICT=1 || goto :error %MAKE_PARALLEL% Basic_Ssl ENABLE_SSL=Bearssl DEBUG_VERBOSE_LEVEL=3 STRICT=1 || goto :error +ESP32_PROJECTS="Basic_Blink Basic_Ethernet" + +REM esp32s2 +%MAKE_PARALLEL% ESP_VARIANT=esp32s2 %ESP32_PROJECTS% || goto :error + +REM esp32c3 +%MAKE_PARALLEL% ESP_VARIANT=esp32c3 %ESP32_PROJECTS% || goto :error + +REM esp32s3 +%MAKE_PARALLEL% ESP_VARIANT=esp32s3 %ESP32_PROJECTS% || goto :error + goto :EOF diff --git a/Sming/Arch/Esp32/Tools/ci/build.run.sh b/Sming/Arch/Esp32/Tools/ci/build.run.sh index 12de1fb80e..2bd9758347 100644 --- a/Sming/Arch/Esp32/Tools/ci/build.run.sh +++ b/Sming/Arch/Esp32/Tools/ci/build.run.sh @@ -3,11 +3,13 @@ $MAKE_PARALLEL Basic_Blink Basic_WiFi HttpServer_ConfigNetwork DEBUG_VERBOSE_LEVEL=3 STRICT=1 $MAKE_PARALLEL Basic_Ssl ENABLE_SSL=Bearssl DEBUG_VERBOSE_LEVEL=3 STRICT=1 +ESP32_PROJECTS="Basic_Blink Basic_Ethernet" + # esp32s2 -$MAKE_PARALLEL Basic_Blink ESP_VARIANT=esp32s2 +$MAKE_PARALLEL ESP_VARIANT=esp32s2 $ESP32_PROJECTS # esp32c3 -$MAKE_PARALLEL Basic_Blink ESP_VARIANT=esp32c3 +$MAKE_PARALLEL ESP_VARIANT=esp32c3 $ESP32_PROJECTS # esp32s3 -$MAKE_PARALLEL Basic_Blink ESP_VARIANT=esp32s3 +$MAKE_PARALLEL ESP_VARIANT=esp32s3 $ESP32_PROJECTS diff --git a/Sming/Arch/Esp32/Tools/ci/install.cmd b/Sming/Arch/Esp32/Tools/ci/install.cmd index 6c633c55da..dd93195491 100644 --- a/Sming/Arch/Esp32/Tools/ci/install.cmd +++ b/Sming/Arch/Esp32/Tools/ci/install.cmd @@ -3,7 +3,10 @@ REM Esp32 install.cmd if "%IDF_PATH%"=="" goto :EOF if "%IDF_TOOLS_PATH%"=="" goto :EOF -git clone -b sming/release/v4.3 https://github.com/mikee47/esp-idf.git %IDF_PATH% +if "%IDF_REPO%"=="" set IDF_REPO="https://github.com/mikee47/esp-idf.git" +if "%IDF_BRANCH%"=="" set IDF_BRANCH="sming/release/v4.3" + +git clone -b %IDF_BRANCH% %IDF_REPO% %IDF_PATH% REM Install IDF tools and packages python %IDF_PATH%\tools\idf_tools.py install diff --git a/Sming/Arch/Esp32/Tools/install.sh b/Sming/Arch/Esp32/Tools/install.sh index a617db2482..d087b4bfa0 100644 --- a/Sming/Arch/Esp32/Tools/install.sh +++ b/Sming/Arch/Esp32/Tools/install.sh @@ -35,11 +35,13 @@ if [ ! -L "$IDF_PATH" ] && [ -d "$IDF_PATH" ]; then fi IDF_CLONE_PATH="$(readlink -m "$IDF_PATH/..")/esp-idf-4.3" +IDF_REPO="${IDF_REPO:=https://github.com/mikee47/esp-idf.git}" +IDF_BRANCH="${IDF_BRANCH:=sming/release/v4.3}" if [ -d "$IDF_CLONE_PATH" ]; then printf "\n\n** Skipping ESP-IDF clone: '$IDF_CLONE_PATH' exists\n\n" else - git clone -b sming/release/v4.3 https://github.com/mikee47/esp-idf.git "$IDF_CLONE_PATH" + git clone -b "$IDF_BRANCH" "$IDF_REPO" "$IDF_CLONE_PATH" fi # Create link to clone diff --git a/Sming/Components/Network/Arch/Esp32/Network/DM9051.cpp b/Sming/Components/Network/Arch/Esp32/Network/DM9051.cpp new file mode 100644 index 0000000000..5b9de8b7cb --- /dev/null +++ b/Sming/Components/Network/Arch/Esp32/Network/DM9051.cpp @@ -0,0 +1,79 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * DM9051.cpp + * + ****/ + +#include +#include "spi_config.h" + +namespace Ethernet +{ +DM9051PhyFactory DM9051Service::dm9051PhyFactory; + +#define CHECK_RET(err) \ + if(ESP_ERROR_CHECK_WITHOUT_ABORT(err) != ESP_OK) { \ + return false; \ + } + +bool DM9051Service::begin(const Config& config) +{ + esp_netif_init(); + esp_event_loop_create_default(); + + esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH(); + netif = esp_netif_new(&netif_cfg); + + // Set default handlers to process TCP/IP stuffs + CHECK_RET(esp_eth_set_default_handlers(netif)); + + // And register our own event handlers + enableEventCallback(true); + enableGotIpCallback(true); + + auto spiHost = (config.spiHost < 0) ? DEFAULT_HOST : spi_host_device_t(config.spiHost); + + auto getPin = [](int pin, uint8_t defaultPin) -> int { return (pin < 0) ? defaultPin : pin; }; + + gpio_install_isr_service(0); + spi_device_interface_config_t devcfg = { + .command_bits = 1, + .address_bits = 7, + .mode = 0, + .clock_speed_hz = int(config.clockSpeed), + .spics_io_num = getPin(config.chipSelectPin, DEFAULT_PIN_CS), + .queue_size = 20, + }; + spi_device_handle_t spi_handle{nullptr}; + CHECK_RET(spi_bus_add_device(spiHost, &devcfg, &spi_handle)); + + eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle); + dm9051_config.int_gpio_num = getPin(config.interruptPin, DEFAULT_PIN_INT); + eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); + mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config); + + auto phy_cfg = config.phy; + phy_cfg.resetPin = getPin(phy_cfg.resetPin, DEFAULT_PIN_RESET); + phy = reinterpret_cast(phyFactory.create(phy_cfg)); + if(phy == nullptr) { + debug_e("[ETH] Failed to construct PHY"); + return false; + } + + esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy); + CHECK_RET(esp_eth_driver_install(ð_config, &handle)); + + setMacAddress(MacAddress({0x02, 0x00, 0x00, 0x12, 0x34, 0x56})); + + netif_glue = esp_eth_new_netif_glue(handle); + CHECK_RET(esp_netif_attach(netif, netif_glue)); + CHECK_RET(esp_eth_start(handle)); + + return true; +} + +} // namespace Ethernet diff --git a/Sming/Components/Network/Arch/Esp32/Network/W5500.cpp b/Sming/Components/Network/Arch/Esp32/Network/W5500.cpp new file mode 100644 index 0000000000..51d0229a52 --- /dev/null +++ b/Sming/Components/Network/Arch/Esp32/Network/W5500.cpp @@ -0,0 +1,79 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * W5500.cpp + * + ****/ + +#include +#include "spi_config.h" + +namespace Ethernet +{ +W5500PhyFactory W5500Service::w5500PhyFactory; + +#define CHECK_RET(err) \ + if(ESP_ERROR_CHECK_WITHOUT_ABORT(err) != ESP_OK) { \ + return false; \ + } + +bool W5500Service::begin(const Config& config) +{ + esp_netif_init(); + esp_event_loop_create_default(); + + esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH(); + netif = esp_netif_new(&netif_cfg); + + // Set default handlers to process TCP/IP stuffs + CHECK_RET(esp_eth_set_default_handlers(netif)); + + // And register our own event handlers + enableEventCallback(true); + enableGotIpCallback(true); + + auto spiHost = (config.spiHost < 0) ? DEFAULT_HOST : spi_host_device_t(config.spiHost); + + auto getPin = [](int pin, uint8_t defaultPin) -> int { return (pin < 0) ? defaultPin : pin; }; + + gpio_install_isr_service(0); + spi_device_interface_config_t devcfg = { + .command_bits = 16, // W5500 address + .address_bits = 8, // W5500 control + .mode = 0, + .clock_speed_hz = int(config.clockSpeed), + .spics_io_num = getPin(config.chipSelectPin, DEFAULT_PIN_CS), + .queue_size = 20, + }; + spi_device_handle_t spi_handle{nullptr}; + CHECK_RET(spi_bus_add_device(spiHost, &devcfg, &spi_handle)); + + eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_handle); + w5500_config.int_gpio_num = getPin(config.interruptPin, DEFAULT_PIN_INT); + eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); + mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config); + + auto phy_cfg = config.phy; + phy_cfg.resetPin = getPin(phy_cfg.resetPin, DEFAULT_PIN_RESET); + phy = reinterpret_cast(phyFactory.create(phy_cfg)); + if(phy == nullptr) { + debug_e("[ETH] Failed to construct PHY"); + return false; + } + + esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy); + CHECK_RET(esp_eth_driver_install(ð_config, &handle)); + + setMacAddress(MacAddress({0x02, 0x00, 0x00, 0x12, 0x34, 0x56})); + + netif_glue = esp_eth_new_netif_glue(handle); + CHECK_RET(esp_netif_attach(netif, netif_glue)); + CHECK_RET(esp_eth_start(handle)); + + return true; +} + +} // namespace Ethernet diff --git a/Sming/Components/Network/Arch/Esp32/Network/spi_config.h b/Sming/Components/Network/Arch/Esp32/Network/spi_config.h new file mode 100644 index 0000000000..9ddea94d42 --- /dev/null +++ b/Sming/Components/Network/Arch/Esp32/Network/spi_config.h @@ -0,0 +1,43 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * spi_config.h + * + * Common definitions for SPI ethernet devices + * + ****/ + +#include +#include +#include +#include "driver/spi_master.h" + +#if defined(SUBARCH_ESP32) +#define DEFAULT_HOST SPI2_HOST +#define DEFAULT_PIN_CS 22 +#define DEFAULT_PIN_INT 4 +#define DEFAULT_PIN_RESET 5 +#elif defined(SUBARCH_ESP32S2) +#define DEFAULT_HOST SPI2_HOST +#define DEFAULT_PIN_CS 34 +#define DEFAULT_PIN_INT 19 +#define DEFAULT_PIN_RESET 18 +#elif defined(SUBARCH_ESP32C3) +#define DEFAULT_HOST SPI2_HOST +#define DEFAULT_PIN_CS 10 +#define DEFAULT_PIN_INT 19 +#define DEFAULT_PIN_RESET 18 +#elif defined(SUBARCH_ESP32S3) +#define DEFAULT_HOST SPI2_HOST +#define DEFAULT_PIN_CS 10 +#define DEFAULT_PIN_INT 19 +#define DEFAULT_PIN_RESET 18 +#endif + +#define CHECK_RET(err) \ + if(ESP_ERROR_CHECK_WITHOUT_ABORT(err) != ESP_OK) { \ + return false; \ + } diff --git a/Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp b/Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp index af6d8e7dad..ad4870a0ff 100644 --- a/Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp +++ b/Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp @@ -9,10 +9,9 @@ ****/ #include -// #include #include +#include #include -#include using namespace Ethernet; @@ -39,11 +38,11 @@ bool EmbeddedEthernet::begin(const Config& config) enableGotIpCallback(true); eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); - if(config.mac.smiMdcPin != PIN_DEFAULT) { - mac_config.smi_mdc_gpio_num = config.mac.smiMdcPin; + if(config.smiMdcPin != PIN_DEFAULT) { + mac_config.smi_mdc_gpio_num = config.smiMdcPin; } - if(config.mac.smiMdioPin != PIN_DEFAULT) { - mac_config.smi_mdio_gpio_num = config.mac.smiMdioPin; + if(config.smiMdioPin != PIN_DEFAULT) { + mac_config.smi_mdio_gpio_num = config.smiMdioPin; } mac = esp_eth_mac_new_esp32(&mac_config); if(mac == nullptr) { @@ -66,177 +65,3 @@ bool EmbeddedEthernet::begin(const Config& config) return true; #endif } - -void EmbeddedEthernet::end() -{ - if(handle == nullptr) { - return; - } - - ESP_ERROR_CHECK(esp_eth_stop(handle)); - ESP_ERROR_CHECK(esp_eth_del_netif_glue(netif_glue)); - netif_glue = nullptr; - ESP_ERROR_CHECK(esp_eth_clear_default_handlers(netif)); - - ESP_ERROR_CHECK(esp_eth_driver_uninstall(handle)); - handle = nullptr; - - phyFactory.destroy(reinterpret_cast(phy)); - phy = nullptr; - - ESP_ERROR_CHECK(mac->del(mac)); - mac = nullptr; - - esp_netif_destroy(netif); - netif = nullptr; - - enableEventCallback(false); - enableGotIpCallback(false); -} - -void EmbeddedEthernet::enableEventCallback(bool enable) -{ - auto handler = [](void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { - auto ethernet = static_cast(arg); - ethernet->state = Event(event_id); - if(!ethernet->eventCallback) { - return; - } - ethernet->eventCallback(Event(event_id)); - }; - - if(enable) { - auto err = esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, handler, this); - ESP_ERROR_CHECK(err); - } else { - esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, handler); - } -} - -void EmbeddedEthernet::enableGotIpCallback(bool enable) -{ - auto handler = [](void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { - auto ethernet = static_cast(arg); - if(!ethernet->gotIpCallback) { - return; - } - ip_event_got_ip_t* event = (ip_event_got_ip_t*)event_data; - auto& ip_info = event->ip_info; - ethernet->gotIpCallback(ip_info.ip.addr, ip_info.netmask.addr, ip_info.gw.addr); - }; - - if(enable) { - auto err = esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, handler, this); - ESP_ERROR_CHECK(err); - } else { - esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, handler); - } -} - -MacAddress EmbeddedEthernet::getMacAddress() const -{ - MacAddress addr; - if(mac != nullptr) { - mac->get_addr(mac, &addr[0]); - } - return addr; -} - -bool EmbeddedEthernet::setMacAddress(const MacAddress& addr) -{ - if(mac == nullptr) { - return false; - } - return mac->set_addr(mac, &const_cast(addr)[0]) == ESP_OK; -} - -bool EmbeddedEthernet::setSpeed(Speed speed) -{ - if(mac == nullptr) { - return false; - } - return mac->set_speed(mac, eth_speed_t(speed)) == ESP_OK; -} - -bool EmbeddedEthernet::setFullDuplex(bool enable) -{ - if(mac == nullptr) { - return false; - } - return mac->set_duplex(mac, enable ? ETH_DUPLEX_FULL : ETH_DUPLEX_HALF) == ESP_OK; -} - -bool EmbeddedEthernet::setLinkState(bool up) -{ - if(mac == nullptr) { - return false; - } - return mac->set_link(mac, up ? ETH_LINK_UP : ETH_LINK_DOWN) == ESP_OK; -} - -bool EmbeddedEthernet::setPromiscuous(bool enable) -{ - if(mac == nullptr) { - return false; - } - return mac->set_promiscuous(mac, enable) == ESP_OK; -} - -void EmbeddedEthernet::setHostname(const String& hostname) -{ - ESP_ERROR_CHECK(esp_netif_set_hostname(netif, hostname.c_str())); -} - -String EmbeddedEthernet::getHostname() const -{ - const char* hostName; - ESP_ERROR_CHECK(esp_netif_get_hostname(netif, &hostName)); - return hostName; -} - -IpAddress EmbeddedEthernet::getIP() const -{ - IpAddress addr; - esp_netif_ip_info_t info; - if(esp_netif_get_ip_info(netif, &info) == ESP_OK) { - addr = info.ip.addr; - } - return addr; -} - -extern "C" esp_err_t esp_netif_up(esp_netif_t* esp_netif); - -bool EmbeddedEthernet::setIP(IpAddress address, IpAddress netmask, IpAddress gateway) -{ - if(!enableDHCP(false)) { - return false; - } - esp_netif_ip_info_t ipinfo{address, netmask, gateway}; - if(esp_netif_set_ip_info(netif, &ipinfo) == ESP_OK) { - debug_i("Ethernet IP successfully updated"); - esp_netif_up(netif); - } else { - debug_e("Ethernet IP can't be updated"); - enableDHCP(true); - } - return true; -} - -bool EmbeddedEthernet::isEnabledDHCP() const -{ - esp_netif_dhcp_status_t status; - if(esp_netif_dhcps_get_status(netif, &status) != ESP_OK) { - return false; - } - - return status == ESP_NETIF_DHCP_STARTED; -} - -bool EmbeddedEthernet::enableDHCP(bool enable) -{ - if(enable) { - return esp_netif_dhcpc_start(netif) == ESP_OK; - } else { - return esp_netif_dhcpc_stop(netif) == ESP_OK; - } -} diff --git a/Sming/Components/Network/Arch/Esp32/Platform/EthernetPhy.cpp b/Sming/Components/Network/Arch/Esp32/Platform/EthernetPhy.cpp index 339330a7ac..596dff13e5 100644 --- a/Sming/Components/Network/Arch/Esp32/Platform/EthernetPhy.cpp +++ b/Sming/Components/Network/Arch/Esp32/Platform/EthernetPhy.cpp @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #define PHY_IMPL(class_name, func_name) \ @@ -61,4 +63,8 @@ PHY_IMPL(Lan8720, lan8720) PHY_IMPL(Dp83848, dp83848) PHY_IMPL(Ksz8041, ksz8041) +// For internal use +PHY_IMPL(W5500PhyFactory, w5500) +PHY_IMPL(DM9051PhyFactory, dm9051) + } // namespace Ethernet diff --git a/Sming/Components/Network/Arch/Esp32/Platform/IdfService.cpp b/Sming/Components/Network/Arch/Esp32/Platform/IdfService.cpp new file mode 100644 index 0000000000..05297312d5 --- /dev/null +++ b/Sming/Components/Network/Arch/Esp32/Platform/IdfService.cpp @@ -0,0 +1,191 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * IdfService.cpp + * + ****/ + +#include +#include +#include + +namespace Ethernet +{ +void IdfService::end() +{ + if(handle == nullptr) { + return; + } + + ESP_ERROR_CHECK(esp_eth_stop(handle)); + ESP_ERROR_CHECK(esp_eth_del_netif_glue(netif_glue)); + netif_glue = nullptr; + ESP_ERROR_CHECK(esp_eth_clear_default_handlers(netif)); + + ESP_ERROR_CHECK(esp_eth_driver_uninstall(handle)); + handle = nullptr; + + phyFactory.destroy(reinterpret_cast(phy)); + phy = nullptr; + + ESP_ERROR_CHECK(mac->del(mac)); + mac = nullptr; + + esp_netif_destroy(netif); + netif = nullptr; + + enableEventCallback(false); + enableGotIpCallback(false); +} + +void IdfService::enableEventCallback(bool enable) +{ + auto handler = [](void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { + auto service = static_cast(arg); + service->state = Event(event_id); + if(!service->eventCallback) { + return; + } + service->eventCallback(Event(event_id)); + }; + + if(enable) { + auto err = esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, handler, this); + ESP_ERROR_CHECK(err); + } else { + esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, handler); + } +} + +void IdfService::enableGotIpCallback(bool enable) +{ + auto handler = [](void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { + auto service = static_cast(arg); + if(!service->gotIpCallback) { + return; + } + ip_event_got_ip_t* event = (ip_event_got_ip_t*)event_data; + auto& ip_info = event->ip_info; + service->gotIpCallback(ip_info.ip.addr, ip_info.netmask.addr, ip_info.gw.addr); + }; + + if(enable) { + auto err = esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, handler, this); + ESP_ERROR_CHECK(err); + } else { + esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, handler); + } +} + +MacAddress IdfService::getMacAddress() const +{ + MacAddress addr; + if(mac != nullptr) { + mac->get_addr(mac, &addr[0]); + } + return addr; +} + +bool IdfService::setMacAddress(const MacAddress& addr) +{ + if(mac == nullptr) { + return false; + } + return mac->set_addr(mac, &const_cast(addr)[0]) == ESP_OK; +} + +bool IdfService::setSpeed(Speed speed) +{ + if(mac == nullptr) { + return false; + } + return mac->set_speed(mac, eth_speed_t(speed)) == ESP_OK; +} + +bool IdfService::setFullDuplex(bool enable) +{ + if(mac == nullptr) { + return false; + } + return mac->set_duplex(mac, enable ? ETH_DUPLEX_FULL : ETH_DUPLEX_HALF) == ESP_OK; +} + +bool IdfService::setLinkState(bool up) +{ + if(mac == nullptr) { + return false; + } + return mac->set_link(mac, up ? ETH_LINK_UP : ETH_LINK_DOWN) == ESP_OK; +} + +bool IdfService::setPromiscuous(bool enable) +{ + if(mac == nullptr) { + return false; + } + return mac->set_promiscuous(mac, enable) == ESP_OK; +} + +void IdfService::setHostname(const String& hostname) +{ + ESP_ERROR_CHECK(esp_netif_set_hostname(netif, hostname.c_str())); +} + +String IdfService::getHostname() const +{ + const char* hostName; + ESP_ERROR_CHECK(esp_netif_get_hostname(netif, &hostName)); + return hostName; +} + +IpAddress IdfService::getIP() const +{ + IpAddress addr; + esp_netif_ip_info_t info; + if(esp_netif_get_ip_info(netif, &info) == ESP_OK) { + addr = info.ip.addr; + } + return addr; +} + +extern "C" esp_err_t esp_netif_up(esp_netif_t* esp_netif); + +bool IdfService::setIP(IpAddress address, IpAddress netmask, IpAddress gateway) +{ + if(!enableDHCP(false)) { + return false; + } + esp_netif_ip_info_t ipinfo{address, netmask, gateway}; + if(esp_netif_set_ip_info(netif, &ipinfo) == ESP_OK) { + debug_i("Ethernet IP successfully updated"); + esp_netif_up(netif); + } else { + debug_e("Ethernet IP can't be updated"); + enableDHCP(true); + } + return true; +} + +bool IdfService::isEnabledDHCP() const +{ + esp_netif_dhcp_status_t status; + if(esp_netif_dhcps_get_status(netif, &status) != ESP_OK) { + return false; + } + + return status == ESP_NETIF_DHCP_STARTED; +} + +bool IdfService::enableDHCP(bool enable) +{ + if(enable) { + return esp_netif_dhcpc_start(netif) == ESP_OK; + } else { + return esp_netif_dhcpc_stop(netif) == ESP_OK; + } +} + +} // namespace Ethernet diff --git a/Sming/Components/Network/Arch/Esp32/include/Network/Ethernet/DM9051.h b/Sming/Components/Network/Arch/Esp32/include/Network/Ethernet/DM9051.h new file mode 100644 index 0000000000..e8ad755d14 --- /dev/null +++ b/Sming/Components/Network/Arch/Esp32/include/Network/Ethernet/DM9051.h @@ -0,0 +1,43 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * DM9051.h + * + ****/ + +#pragma once + +#include "SpiService.h" + +namespace Ethernet +{ +class DM9051PhyFactory : public Ethernet::PhyFactory +{ +public: + using PhyFactory::PhyFactory; + + PhyInstance* create(const PhyConfig& config) override; + void destroy(PhyInstance* inst) override; +}; + +/** + * @brief Ethernet provider using W5500 SPI. + * + */ +class DM9051Service : public SpiService +{ +public: + DM9051Service() : SpiService(dm9051PhyFactory) + { + } + + bool begin(const Config& config); + +private: + static DM9051PhyFactory dm9051PhyFactory; +}; + +} // namespace Ethernet diff --git a/Sming/Components/Network/Arch/Esp32/include/Network/Ethernet/SpiService.h b/Sming/Components/Network/Arch/Esp32/include/Network/Ethernet/SpiService.h new file mode 100644 index 0000000000..abaa84877c --- /dev/null +++ b/Sming/Components/Network/Arch/Esp32/include/Network/Ethernet/SpiService.h @@ -0,0 +1,34 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * SpiService.h + * + ****/ + +#pragma once + +#include + +namespace Ethernet +{ +/** + * @brief SPI ethernet provider + */ +class SpiService : public IdfService +{ +public: + struct Config { + Ethernet::PhyConfig phy; + uint32_t clockSpeed = 16 * 1000000; + int8_t spiHost = -1; + int8_t chipSelectPin = Ethernet::PIN_DEFAULT; + int8_t interruptPin = Ethernet::PIN_DEFAULT; + }; + + using IdfService::IdfService; +}; + +} // namespace Ethernet diff --git a/Sming/Components/Network/Arch/Esp32/include/Network/Ethernet/W5500.h b/Sming/Components/Network/Arch/Esp32/include/Network/Ethernet/W5500.h new file mode 100644 index 0000000000..423c703f57 --- /dev/null +++ b/Sming/Components/Network/Arch/Esp32/include/Network/Ethernet/W5500.h @@ -0,0 +1,43 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * W5500.h + * + ****/ + +#pragma once + +#include "SpiService.h" + +namespace Ethernet +{ +class W5500PhyFactory : public Ethernet::PhyFactory +{ +public: + using PhyFactory::PhyFactory; + + PhyInstance* create(const PhyConfig& config) override; + void destroy(PhyInstance* inst) override; +}; + +/** + * @brief Ethernet provider using W5500 SPI. + * + */ +class W5500Service : public SpiService +{ +public: + W5500Service() : SpiService(w5500PhyFactory) + { + } + + bool begin(const Config& config); + +private: + static W5500PhyFactory w5500PhyFactory; +}; + +} // namespace Ethernet diff --git a/Sming/Components/Network/Arch/Esp32/Platform/include/Platform/EmbeddedEthernet.h b/Sming/Components/Network/Arch/Esp32/include/Platform/EmbeddedEthernet.h similarity index 56% rename from Sming/Components/Network/Arch/Esp32/Platform/include/Platform/EmbeddedEthernet.h rename to Sming/Components/Network/Arch/Esp32/include/Platform/EmbeddedEthernet.h index 8b11f3e64e..45988c3434 100644 --- a/Sming/Components/Network/Arch/Esp32/Platform/include/Platform/EmbeddedEthernet.h +++ b/Sming/Components/Network/Arch/Esp32/include/Platform/EmbeddedEthernet.h @@ -10,7 +10,7 @@ #pragma once -#include +#include "IdfService.h" struct esp_eth_mac_s; struct esp_eth_phy_s; @@ -46,37 +46,22 @@ struct esp_netif_obj; * * PHY_RESET - OUT (set via PhyConfig) */ -class EmbeddedEthernet : public Ethernet::Service +class EmbeddedEthernet : public Ethernet::IdfService { public: - EmbeddedEthernet(Ethernet::PhyFactory& phyFactory) : phyFactory(phyFactory) - { - } + struct Config { + Ethernet::PhyConfig phy; + int8_t smiMdcPin = Ethernet::PIN_DEFAULT; //< SMI MDC GPIO number + int8_t smiMdioPin = Ethernet::PIN_DEFAULT; //< SMI MDIO GPIO number + }; - bool begin(const Ethernet::Config& config) override; - void end() override; - MacAddress getMacAddress() const override; - bool setMacAddress(const MacAddress& addr) override; - bool setSpeed(Ethernet::Speed speed) override; - bool setFullDuplex(bool enable) override; - bool setLinkState(bool up) override; - bool setPromiscuous(bool enable) override; - void setHostname(const String& hostname) override; - String getHostname() const override; - IpAddress getIP() const override; - bool setIP(IpAddress address, IpAddress netmask, IpAddress gateway) override; - bool isEnabledDHCP() const override; - bool enableDHCP(bool enable) override; + using IdfService::IdfService; -private: - void enableEventCallback(bool enable); - void enableGotIpCallback(bool enable); - - Ethernet::PhyFactory& phyFactory; - void* handle{nullptr}; - esp_netif_obj* netif{nullptr}; - void* netif_glue{nullptr}; - esp_eth_mac_s* mac{nullptr}; - esp_eth_phy_s* phy{nullptr}; - Ethernet::Event state{Ethernet::Event::Disconnected}; + /** + * @brief Configure and start the ethernet service + * @param config Configuration options + * + * Applications should expect to receive Start and Connected events following this call. + */ + bool begin(const Config& config); }; diff --git a/Sming/Components/Network/Arch/Esp32/include/Platform/IdfService.h b/Sming/Components/Network/Arch/Esp32/include/Platform/IdfService.h new file mode 100644 index 0000000000..d2fa68929f --- /dev/null +++ b/Sming/Components/Network/Arch/Esp32/include/Platform/IdfService.h @@ -0,0 +1,58 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * IdfService.h + * + ****/ + +#pragma once + +#include + +struct esp_eth_mac_s; +struct esp_eth_phy_s; +struct esp_netif_obj; + +namespace Ethernet +{ +/** + * @brief Base Ethernet service for IDF SDK + */ +class IdfService : public Service +{ +public: + IdfService(PhyFactory& phyFactory) : phyFactory(phyFactory) + { + } + + void end() override; + MacAddress getMacAddress() const override; + bool setMacAddress(const MacAddress& addr) override; + bool setSpeed(Ethernet::Speed speed) override; + bool setFullDuplex(bool enable) override; + bool setLinkState(bool up) override; + bool setPromiscuous(bool enable) override; + void setHostname(const String& hostname) override; + String getHostname() const override; + IpAddress getIP() const override; + bool setIP(IpAddress address, IpAddress netmask, IpAddress gateway) override; + bool isEnabledDHCP() const override; + bool enableDHCP(bool enable) override; + +protected: + void enableEventCallback(bool enable); + void enableGotIpCallback(bool enable); + + PhyFactory& phyFactory; + void* handle{nullptr}; + esp_netif_obj* netif{nullptr}; + void* netif_glue{nullptr}; + esp_eth_mac_s* mac{nullptr}; + esp_eth_phy_s* phy{nullptr}; + Event state{Event::Disconnected}; +}; + +} // namespace Ethernet diff --git a/Sming/Components/Network/component.mk b/Sming/Components/Network/component.mk index 268da4aca1..69d0d4bb02 100644 --- a/Sming/Components/Network/component.mk +++ b/Sming/Components/Network/component.mk @@ -5,9 +5,11 @@ COMPONENT_SRCDIRS := \ COMPONENT_INCDIRS := \ src \ - Arch/$(SMING_ARCH)/Platform/include + Arch/$(SMING_ARCH)/include -COMPONENT_DOXYGEN_INPUT := src +COMPONENT_DOXYGEN_INPUT := \ + src \ + Arch/Esp32/Platform/include COMPONENT_DEPENDS := \ ssl \ diff --git a/Sming/Components/Network/src/Platform/Ethernet.h b/Sming/Components/Network/src/Platform/Ethernet.h index f3662ed01a..e947056bf5 100644 --- a/Sming/Components/Network/src/Platform/Ethernet.h +++ b/Sming/Components/Network/src/Platform/Ethernet.h @@ -40,7 +40,6 @@ enum class Event { /** * @brief Delegate type for Ethernet events * @param event Which event occurred - * @param mac Provided on 'Connected' event only */ using EventDelegate = Delegate; @@ -61,14 +60,6 @@ constexpr int8_t PIN_DEFAULT{-2}; */ constexpr int8_t PIN_UNUSED{-1}; -/** - * @brief Configuration for Ethernet MAC - */ -struct MacConfig { - int8_t smiMdcPin = PIN_DEFAULT; //< SMI MDC GPIO number - int8_t smiMdioPin = PIN_DEFAULT; //< SMI MDIO GPIO number -}; - /** * @brief Link speed */ @@ -119,14 +110,6 @@ class PhyFactory virtual void destroy(PhyInstance* inst) = 0; }; -/** - * @brief Service configuration options - */ -struct Config { - MacConfig mac; - PhyConfig phy; -}; - /** * @brief Abstract Service class * @@ -144,14 +127,6 @@ struct Config { class Service { public: - /** - * @brief Configure and start the ethernet service - * @param config Configuration options - * - * Applications should expect to receive Start and Connected events following this call. - */ - virtual bool begin(const Config& config) = 0; - /** * @brief Tear down the ethernet connection */ diff --git a/Sming/Libraries/Graphics b/Sming/Libraries/Graphics index 2375f5fa3c..f272a5eade 160000 --- a/Sming/Libraries/Graphics +++ b/Sming/Libraries/Graphics @@ -1 +1 @@ -Subproject commit 2375f5fa3c8cc92727968fa2c983a6d799220373 +Subproject commit f272a5eade55561dea6b959146fb630a57e58927 diff --git a/Sming/Libraries/HardwareSPI b/Sming/Libraries/HardwareSPI index 7a5e89bf2d..d464c20324 160000 --- a/Sming/Libraries/HardwareSPI +++ b/Sming/Libraries/HardwareSPI @@ -1 +1 @@ -Subproject commit 7a5e89bf2dfcb189f7a4cc1ace86706aa5b0ead0 +Subproject commit d464c203240144db1acfb1fb3343accdd28cb6cd diff --git a/Sming/Libraries/TFT_S1D13781 b/Sming/Libraries/TFT_S1D13781 index 673acb185c..902f8daba3 160000 --- a/Sming/Libraries/TFT_S1D13781 +++ b/Sming/Libraries/TFT_S1D13781 @@ -1 +1 @@ -Subproject commit 673acb185c8e9a9d68e3d27367b39fa45e168dd3 +Subproject commit 902f8daba3e03b6ed85d1f06e231c31212fac95b diff --git a/appveyor.yml b/appveyor.yml index 413d69e599..ed86a8b7fd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -44,6 +44,7 @@ install: # Esp32 $env:IDF_PATH = Join-Path $env:CI_BUILD_DIR "opt/esp-idf" $env:IDF_TOOLS_PATH = Join-Path $env:CI_BUILD_DIR "opt/tools/esp32" + $env:IDF_BRANCH = "sming/dev/v4.3" # General $env:SMING_HOME = Join-Path $env:CI_BUILD_DIR "Sming" diff --git a/docs/source/framework/platform/ethernet.rst b/docs/source/framework/platform/ethernet.rst index 74bcde4b3e..6c1e96c0b1 100644 --- a/docs/source/framework/platform/ethernet.rst +++ b/docs/source/framework/platform/ethernet.rst @@ -3,5 +3,8 @@ Ethernet Currently only supported on ESP32 using embedded MAC. +.. doxygenclass:: EmbeddedEthernet + :members: + .. doxygennamespace:: Ethernet :members: diff --git a/samples/Basic_Ethernet/README.rst b/samples/Basic_Ethernet/README.rst index 798fef75c9..5c54844740 100644 --- a/samples/Basic_Ethernet/README.rst +++ b/samples/Basic_Ethernet/README.rst @@ -3,14 +3,33 @@ Basic Ethernet Demonstrates creating an ethernet connection. -Currently only supported for ESP32 with internal MAC. -An external PHY is required. This demonstration uses a commonly available LAN8270 module. +Currently only supported for ESP32 using C++ wrappers around the ESP-IDF implementation. -.. note: +See https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32/api-reference/network/esp_eth.html. - The MAC/PHY interface uses high-speed signalling so connections must be solid. - The connection may *appear* to work but fail, for example, to obtain network address. - If this happens, check connections. - In the current configuration a 50MHz synchronisation clock is received from the PHY on GPIO0. - Not all ESP32 development boards have this pin available! +Embedded MAC + + The standard ESP32 contains an embedded ethernet MAC but requires an external PHY. + This demonstration uses a commonly available LAN8270 module. + + See `Components/Network/Arch/Esp32/include/Platform/EmbeddedEthernet.h` for connections. + + .. note: + + The MAC/PHY interface uses high-speed signalling so connections must be solid. + The connection may *appear* to work but fail, for example, to obtain network address. + If this happens, check connections. + + In the current configuration a 50MHz synchronisation clock is received from the PHY on GPIO0. + Not all ESP32 development boards have this pin available! + + +SPI ethernet + + SPI connections depend on the device variant being used. + See `Sming/Libraries/HardwareSPI/src/Arch/Esp32/Controller.cpp` for details. + + See `Sming/Components/Network/Arch/Esp32/Network/W5500.cpp` for additional pin connections + for W5500-based devices. + diff --git a/samples/Basic_Ethernet/app/application.cpp b/samples/Basic_Ethernet/app/application.cpp index 858b9abb1f..6abde8a8b6 100644 --- a/samples/Basic_Ethernet/app/application.cpp +++ b/samples/Basic_Ethernet/app/application.cpp @@ -1,13 +1,35 @@ #include -#ifdef SUBARCH_ESP32 +// #define USE_EMBEDDED ARCH_ESP32 +#ifdef ARCH_ESP32 + +#if USE_EMBEDDED + +// The Embedded MAC #include + +// PHY: See `Sming/Components/Network/src/Network/Ethernet` for others #include Ethernet::Lan8720 phy; EmbeddedEthernet ethernet(phy); +#else + +// Use the HardwareSPI library to initialise SPI bus +#include +HSPI::Controller spi; + +// The Wiznet W5500 controller with integrated PHY +#include +Ethernet::W5500Service ethernet; + +// #include +// Ethernet::DM9051Service ethernet; + +#endif + static void ethernetEventHandler(Ethernet::Event event) { Serial.print(toString(event)); @@ -33,9 +55,20 @@ void init() ethernet.onEvent(ethernetEventHandler); ethernet.onGotIp(ethernetGotIp); +#if USE_EMBEDDED // Modify default config as required - Ethernet::Config config; + EmbeddedEthernet::Config config; ethernet.begin(config); +#else + if(!spi.begin()) { + return; + } + Ethernet::SpiService::Config config; + config.spiHost = spi.getHost(); + if(!ethernet.begin(config)) { + return; + } +#endif // Change the advertised hostname for DHCP ethernet.setHostname("sming-ethernet"); @@ -44,7 +77,7 @@ void init() // ethernet.setIP(IpAddress("192.168.1.12"), IpAddress("255.255.255.0"), IpAddress("192.168.1.254")); } -#else +#else // ARCH_ESP32 void init() { diff --git a/samples/Basic_Ethernet/component.mk b/samples/Basic_Ethernet/component.mk index f7504e5dd6..664701618a 100644 --- a/samples/Basic_Ethernet/component.mk +++ b/samples/Basic_Ethernet/component.mk @@ -1 +1,2 @@ DISABLE_WIFI := 1 +COMPONENT_DEPENDS := HardwareSPI From fea192a8a06228cc0707631e06d513aa47a47c25 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 23 Sep 2021 10:17:29 +0100 Subject: [PATCH 060/130] ESP32 task management (#2371) This PR aims to fix some issues relating to tasks and Sming. Using an esp32-s2, here's the task list as it looks presently: ``` # | Core | Prio | Run Time | % Time | Name 9 | 0 | 23 | 40591 | 2% | wifi 2 | 0 | 22 | 28009 | 1% | esp_timer 1 | 0 | 20 | 0 | 0% | sys_evt 8 | 0 | 18 | 3003 | 0% | tiT 5 | 0 | 1 | 0 | 0% | Tmr Svc 6 | 0 | 1 | 1928397 | 96% | Sming 4 | 0 | 0 | 0 | 0% | IDLE ``` *tiT == TCP/IP (LWIP) *Tmr Svc == FreeRTOS software timers (low resolution, runs at tick rate 1kHz) And for this PR: ``` # | Core | Prio | Run Time | % Time | Name 7 | 0 | 23 | 0 | 0% | wifi 1 | 0 | 22 | 46 | 0% | esp_timer 5 | 0 | 20 | 21243 | 1% | Sming 6 | 0 | 18 | 375 | 0% | tiT 4 | 0 | 1 | 0 | 0% | Tmr Svc 3 | 0 | 0 | 1978336 | 98% | IDLE ``` This is for a dual-core ESP32: ``` # | Core | Prio | Run Time | % Time | Name 1 | 0 | 24 | 0 | 0% | ipc0 14 | 0 | 23 | 25717 | 0% | wifi 3 | 0 | 22 | 2537 | 0% | esp_timer 8 | 0 | 1 | 0 | 0% | Tmr Svc 6 | 0 | 0 | 1971743 | 49% | IDLE 2 | 1 | 24 | 0 | 0% | ipc1 12 | 1 | 20 | 31857 | 0% | Sming 13 | 1 | 18 | 2088 | 0% | tiT 7 | 1 | 0 | 1966055 | 49% | IDLE ``` *ipcN == InterProcess Communication allows IDF to run code in the context of a specific CPU. The FreeRTOS scheduler will run the highest-priority thread which is ready to do work. It reverts to 'round-robin' if two ready threads have the same priority. Sming is now a high priority task, so is now capable of blocking other tasks. Sming takes priority over the tcpip thread. The Sming task implements the standard event loop. This ensures events are handled in the correct context. When there are no events to process it will idle correctly and allow lower priority tasks to run. The task watchdog is configured to fire if an event takes more than about 7 seconds. Sming code makes good use of stack for performance reasons and needs a reasonably-sized stack (currently 16KBytes). This applies to any tasks which run Sming code, currently the tcpip and Sming tasks. We now have one less task to deal with. **Default event queue** Once the default event queue is running we could just use the existing code as-is. However, to get the task watchdog to work correctly we need a custom event handling loop. That involves replacing all the default event loop functions. Another benefit of this approach is that where there are no events to process the CPU is allowed to idle. This is more consistent with how the IDF is supposed to work and may be important for power saving. **Software timers** Sming uses its `Timer2` definition for this which now no longer uses the 'legacy' FRC timer implementation. The IDF provides a queue for software timers managed by the `esp_timer` task. As this is a higher priority than Sming it behaves like an interrupt, pre-empting Sming when it has work to do. Instead of handling the timer event directly, we post it to the event queue so it is handled in the main Sming task. **Hardware timers** Sming uses its `Timer1` definition for this, which has now been implemented so the HardwareTimer class works as expected. It uses Timer Group 0, Index 0 for this. **Other changes** Wait for UART FIFOs to empty at startup to avoid truncated debug messages. Found `cache2phys` SDK call which implements `flashmem_get_address`. SDK config only needs to specify `CONFIG_BOOTLOADER_LOG_LEVEL` setting, others are handled automatically. Add `Wrap` build function to replace multiple uses of `-Wl,-wrap,XXXX` in makefiles. WifiEventsImpl attempts to register handlers in static constructor, so provide `wifi_set_event_handler_cb` to defer this (as for ESP8266). Add `TaskStat` class to allow simple monitoring of task usage. Add example use to Basic_IFS sample. --- .../Arch/Esp32/Components/driver/hw_timer.cpp | 104 +++++++++++--- .../driver/include/driver/hw_timer.h | 106 ++++---------- .../driver/include/driver/os_timer.h | 59 ++++++-- .../Arch/Esp32/Components/driver/os_timer.cpp | 102 ++++++++++++- Sming/Arch/Esp32/Components/driver/uart.cpp | 3 + .../Arch/Esp32/Components/esp32/component.mk | 8 ++ .../Esp32/Components/esp32/sdk/config/common | 5 +- .../Esp32/Components/esp32/sdk/config/debug | 16 +-- .../Esp32/Components/esp32/sdk/config/release | 12 -- .../Esp32/Components/esp32/src/event_loop.cpp | 110 ++++++++++++++ .../Components/esp32/src/include/esp_tasks.h | 13 -- .../Esp32/Components/esp32/src/startup.cpp | 65 ++++++--- .../Arch/Esp32/Components/esp32/src/tasks.cpp | 117 +++++---------- Sming/Arch/Esp32/Components/libc/component.mk | 4 +- .../Esp32/Components/sming-arch/component.mk | 3 +- .../Esp32/Components/spi_flash/flashmem.cpp | 30 ---- .../spi_flash/include/esp_spi_flash.h | 6 +- .../Esp32/Services/Profiling/TaskStat.cpp | 135 ++++++++++++++++++ .../driver/include/driver/os_timer.h | 24 ++++ .../Esp8266/Components/esp8266/component.mk | 2 +- .../Components/sming-arch/component.mk | 3 +- .../Esp8266/Services/Profiling/TaskStat.cpp | 32 +++++ .../driver/include/driver/os_timer.h | 26 +++- .../Arch/Host/Components/driver/os_timer.cpp | 5 + .../Host/Components/sming-arch/component.mk | 3 +- .../Arch/Host/Services/Profiling/TaskStat.cpp | 32 +++++ Sming/Components/Hosted/component.mk | 2 +- .../Network/Arch/Esp32/Network/DM9051.cpp | 1 - .../Network/Arch/Esp32/Network/W5500.cpp | 1 - .../Arch/Esp32/Platform/EmbeddedEthernet.cpp | 2 - .../Arch/Esp32/Platform/WifiEventsImpl.cpp | 7 +- Sming/Components/malloc_count/component.mk | 32 +++-- Sming/Core/SimpleTimer.h | 12 +- Sming/Services/Profiling/TaskStat.h | 59 ++++++++ Sming/build.mk | 5 + .../framework/services/profiling/taskstat.rst | 24 ++++ samples/Basic_IFS/app/application.cpp | 7 + 37 files changed, 851 insertions(+), 326 deletions(-) create mode 100644 Sming/Arch/Esp32/Components/esp32/src/event_loop.cpp create mode 100644 Sming/Arch/Esp32/Services/Profiling/TaskStat.cpp create mode 100644 Sming/Arch/Esp8266/Services/Profiling/TaskStat.cpp create mode 100644 Sming/Arch/Host/Services/Profiling/TaskStat.cpp create mode 100644 Sming/Services/Profiling/TaskStat.h create mode 100644 docs/source/framework/services/profiling/taskstat.rst diff --git a/Sming/Arch/Esp32/Components/driver/hw_timer.cpp b/Sming/Arch/Esp32/Components/driver/hw_timer.cpp index ce02f29378..73c1351342 100644 --- a/Sming/Arch/Esp32/Components/driver/hw_timer.cpp +++ b/Sming/Arch/Esp32/Components/driver/hw_timer.cpp @@ -9,33 +9,103 @@ ****/ #include +#include +#include -static struct { - hw_timer_callback_t func = nullptr; - void* arg = nullptr; -} nmi_callback; +namespace +{ +struct TimerConfig { + timer_group_t group; + timer_idx_t index; + timer_hal_context_t hal; + intr_handle_t isr_handle; + hw_timer_callback_t callback; + void* arg; + bool autoload; +}; + +TimerConfig timer; -static void IRAM_ATTR nmi_handler() +void IRAM_ATTR timerIsr(void* arg) { - nmi_callback.func(nmi_callback.arg); + auto& timer = *static_cast(arg); + + if(timer.callback != nullptr) { + timer.callback(arg); + } + + timer_hal_clear_intr_status(&timer.hal); + + if(timer.autoload) { + timer_hal_set_alarm_enable(&timer.hal, true); + } else { + timer_hal_set_counter_enable(&timer.hal, false); + } } +} // namespace + void hw_timer1_attach_interrupt(hw_timer_source_type_t source_type, hw_timer_callback_t callback, void* arg) { - if(source_type == TIMER_NMI_SOURCE) { - if(arg == NULL) { - // ETS_FRC_TIMER1_NMI_INTR_ATTACH(reinterpret_cast(callback)); - } else { - nmi_callback.func = callback; - nmi_callback.arg = arg; - // ETS_FRC_TIMER1_NMI_INTR_ATTACH(nmi_handler); - } - } else { - // ETS_FRC_TIMER1_INTR_ATTACH(callback, arg); + if(timer.isr_handle != nullptr) { + hw_timer1_detach_interrupt(); + } + + if(callback == nullptr) { + return; } + + timer.callback = callback; + + uint32_t status_reg{0}; + uint32_t mask{0}; + timer_hal_get_status_reg_mask_bit(&timer.hal, &status_reg, &mask); + esp_intr_alloc_intrstatus(timer_group_periph_signals.groups[timer.group].t0_irq_id + timer.index, + ESP_INTR_FLAG_IRAM, status_reg, mask, timerIsr, &timer, &timer.isr_handle); + timer_hal_clear_intr_status(&timer.hal); + timer_hal_intr_enable(&timer.hal); +} + +void hw_timer1_detach_interrupt(void) +{ + timer_hal_intr_disable(&timer.hal); + esp_intr_free(timer.isr_handle); + timer.isr_handle = nullptr; +} + +void hw_timer1_enable(hw_timer_clkdiv_t div, hw_timer_intr_type_t intr_type, bool auto_load) +{ + timer_hal_set_auto_reload(&timer.hal, auto_load); + timer_hal_set_divider(&timer.hal, 1 << div); + timer.autoload = auto_load; +} + +void IRAM_ATTR hw_timer1_write(uint32_t ticks) +{ + timer_hal_set_counter_value(&timer.hal, ticks); + timer_hal_set_counter_enable(&timer.hal, true); +} + +void IRAM_ATTR hw_timer1_disable(void) +{ + timer_hal_set_counter_enable(&timer.hal, false); +} + +uint32_t hw_timer1_read(void) +{ + uint64_t val{0}; + timer_hal_get_counter_value(&timer.hal, &val); + return val; } void hw_timer_init(void) { - ets_timer_init(); + timer.group = HW_TIMER1_GROUP; + timer.index = HW_TIMER1_INDEX; + timer_hal_init(&timer.hal, timer.group, timer.index); + timer_hal_set_counter_enable(&timer.hal, false); + timer_hal_set_alarm_enable(&timer.hal, true); + timer_hal_set_alarm_value(&timer.hal, 0); + timer_hal_set_level_int_enable(&timer.hal, true); + timer_hal_set_counter_increase(&timer.hal, false); } diff --git a/Sming/Arch/Esp32/Components/driver/include/driver/hw_timer.h b/Sming/Arch/Esp32/Components/driver/include/driver/hw_timer.h index b0a7a7e577..be15811eb3 100644 --- a/Sming/Arch/Esp32/Components/driver/include/driver/hw_timer.h +++ b/Sming/Arch/Esp32/Components/driver/include/driver/hw_timer.h @@ -10,23 +10,12 @@ #pragma once -#if defined(SUBARCH_ESP32) -#define FRC_TIMER_ENABLED -#endif - -#include -#ifdef FRC_TIMER_ENABLED -#include -#else -#include -#endif +#include +#include +#include #define HW_TIMER_BASE_CLK APB_CLK_FREQ -#ifdef __cplusplus -extern "C" { -#endif - /** * @defgroup hw_timer Hardware Timer Driver * @ingroup drivers @@ -35,21 +24,28 @@ extern "C" { /************************************* * - * FRC1 timer - * - * This is a 23-bit countdown timer + * Timer1 + * + * Used to implement HardwareTimer class. * *************************************/ +// Timer group/index to use: available on all ESP32 variants +#define HW_TIMER1_GROUP TIMER_GROUP_0 +#define HW_TIMER1_INDEX TIMER_0 + /** * @brief Maximum timer interval in ticks * @note The corresponding time interval depends on the prescaler in use: + * + * /1 - 26.84s + * /16 - 429.50s + * /256 - 6871.95s * - * /1 - 0.1048s - * /16 - 1.677s - * /256 - 26.84s + * ESP32 supports a wide range of prescalers and uses 54-bit counter value. + * Limiting the range 31 bits avoids issues with overflows and moving to 64-bit calculations. */ -#define MAX_HW_TIMER1_INTERVAL 0x7fffff +#define MAX_HW_TIMER1_INTERVAL 0x7fffffff /** * @brief Minimum hardware interval in microseconds @@ -79,7 +75,7 @@ typedef enum { /** * @brief Attach an interrupt for the timer - * @param source_type + * @param source_type Ignored, uses APB clock source * @param callback Callback function invoked via timer interrupt * @param arg Passed to callback function */ @@ -88,82 +84,42 @@ void IRAM_ATTR hw_timer1_attach_interrupt(hw_timer_source_type_t source_type, hw /** * @brief Enable the timer * @param div - * @param intr_type + * @param intr_type Ignored, always level-triggered * @param auto_load */ -inline void IRAM_ATTR hw_timer1_enable(hw_timer_clkdiv_t div, hw_timer_intr_type_t intr_type, bool auto_load) -{ -#ifdef FRC_TIMER_ENABLED - uint32_t ctrl = (div & 0x0C) | (intr_type & 0x01) | FRC_TIMER_ENABLE; - if(auto_load) { - ctrl |= FRC_TIMER_AUTOLOAD; - } - - REG_WRITE(FRC_TIMER_CTRL_REG(0), ctrl); - // TM1_EDGE_INT_ENABLE(); - // ETS_FRC1_INTR_ENABLE(); -#endif -} +void IRAM_ATTR hw_timer1_enable(hw_timer_clkdiv_t div, hw_timer_intr_type_t intr_type, bool auto_load); /** * @brief Set the timer interval * @param ticks */ -__forceinline void IRAM_ATTR hw_timer1_write(uint32_t ticks) -{ -#ifdef FRC_TIMER_ENABLED - REG_WRITE(FRC_TIMER_LOAD_REG(0), ticks); -#endif -} +void IRAM_ATTR hw_timer1_write(uint32_t ticks); /** * @brief Disable the timer */ -__forceinline void IRAM_ATTR hw_timer1_disable(void) -{ - // TM1_EDGE_INT_DISABLE(); - // ETS_FRC1_INTR_DISABLE(); -} +void IRAM_ATTR hw_timer1_disable(void); /** * @brief Detach interrupt from the timer */ -__forceinline void IRAM_ATTR hw_timer1_detach_interrupt(void) -{ - hw_timer1_disable(); - // ETS_FRC_TIMER1_NMI_INTR_ATTACH(NULL); - // ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL); -} +void IRAM_ATTR hw_timer1_detach_interrupt(void); /** * @brief Get timer1 count * @retval uint32_t Current count value, counts from initial value down to 0 */ -__forceinline uint32_t hw_timer1_read(void) -{ -#ifdef FRC_TIMER_ENABLED - return REG_READ(FRC_TIMER_COUNT_REG(0)); -#else - return 0; -#endif -} +uint32_t hw_timer1_read(void); /************************************* * - * FRC2 timer - * - * This is a 32-bit count-up timer - * - * See idf components/esp32/esp_timer_esp32.c + * Timer2 uses the idf `esp_timer` component for software-based timers (os_timer.cpp). * *************************************/ -#ifdef FRC_TIMER_ENABLED -constexpr uint32_t HW_TIMER2_CLKDIV = TIMER_CLKDIV_1; -constexpr uint32_t HW_TIMER2_CLK = HW_TIMER_BASE_CLK >> HW_TIMER2_CLKDIV; -#else constexpr uint32_t HW_TIMER2_CLK = 1000000; -#endif + +extern "C" int64_t esp_timer_get_time(void); /** * @brief Read current timer2 value @@ -171,11 +127,7 @@ constexpr uint32_t HW_TIMER2_CLK = 1000000; */ __forceinline uint32_t hw_timer2_read(void) { -#ifdef FRC_TIMER_ENABLED - return REG_READ(FRC_TIMER_COUNT_REG(1)); -#else return esp_timer_get_time(); -#endif } #define NOW() hw_timer2_read() @@ -187,7 +139,3 @@ __forceinline uint32_t hw_timer2_read(void) void hw_timer_init(void); /** @} */ - -#ifdef __cplusplus -} -#endif diff --git a/Sming/Arch/Esp32/Components/driver/include/driver/os_timer.h b/Sming/Arch/Esp32/Components/driver/include/driver/os_timer.h index 74eae9f135..1fab8b588f 100644 --- a/Sming/Arch/Esp32/Components/driver/include/driver/os_timer.h +++ b/Sming/Arch/Esp32/Components/driver/include/driver/os_timer.h @@ -7,7 +7,7 @@ * os_timer.h * * This implementation mimics the behaviour of the ESP8266 Non-OS SDK timers, - * using Timer2 as the reference (which is _not_ in microseconds!) + * using Timer2 as the reference. The ESP32 uses a 1MHz reference so all times * * The ESP32 IDF contains more sophisticated timer implementations, but also * this same API which it refers to as the 'legacy' timer API. @@ -15,11 +15,12 @@ #pragma once -#include +#include -#ifdef __cplusplus -extern "C" { -#endif +// Disarmed +#define OS_TIMER_DEFAULT() \ + { \ + } /** * @defgroup os_timer OS Timer API @@ -27,13 +28,30 @@ extern "C" { * @{ */ -typedef ETSTimerFunc os_timer_func_t; -typedef ETSTimer os_timer_t; +using smg_timer_func_t = void (*)(void* arg); -#define os_timer_arm(ptimer, ms, repeat_flag) ets_timer_arm(ptimer, ms, repeat_flag) -#define os_timer_arm_us(ptimer, us, repeat_flag) ets_timer_arm_us(ptimer, us, repeat_flag) -#define os_timer_disarm(ptimer) ets_timer_disarm(ptimer) -#define os_timer_setfn(ptimer, pfunction, parg) ets_timer_setfn(ptimer, pfunction, parg) +struct esp_timer; + +struct smg_timer_t { + struct esp_timer* handle; + smg_timer_func_t timer_func; + void* timer_arg; +}; + +/* + * Re-map the os_timer_* definitions to avoid conflict with the real SDK implementations. + * Those are provided as a 'legacy' API which seems to be used only by WiFi in the SDK. + */ +#define os_timer_func_t smg_timer_func_t +#define os_timer_t smg_timer_t + +#define os_timer_arm smg_timer_arm +#define os_timer_arm_us smg_timer_arm_us +#define os_timer_disarm smg_timer_disarm +#define os_timer_setfn smg_timer_setfn +#define os_timer_arm_ticks smg_timer_arm_ticks +#define os_timer_expire smg_timer_expire +#define os_timer_done smg_timer_done /** * @brief Set a software timer using the Timer2 tick value @@ -44,10 +62,21 @@ typedef ETSTimer os_timer_t; * This function has been added to Sming for more efficient and flexible use of * software timers. It can be used alongside the SDK `os_timer_arm_new()` function. */ -void os_timer_arm_ticks(os_timer_t* ptimer, uint32_t ticks, bool repeat_flag); +void smg_timer_arm_ticks(os_timer_t* ptimer, uint32_t ticks, bool repeat_flag); -/** @} */ +void smg_timer_setfn(os_timer_t* ptimer, os_timer_func_t pfunction, void* parg); +void smg_timer_arm_us(os_timer_t* ptimer, uint32_t time_us, bool repeat_flag); +void smg_timer_arm(os_timer_t* ptimer, uint32_t time_ms, bool repeat_flag); +void smg_timer_disarm(os_timer_t* ptimer); +void smg_timer_done(os_timer_t* ptimer); -#ifdef __cplusplus +static inline uint64_t smg_timer_expire(const os_timer_t* ptimer) +{ + if(ptimer == nullptr || ptimer->handle == nullptr) { + return 0; + } + // First field is 'alarm': See esp_timer.c. + return *reinterpret_cast(ptimer->handle); } -#endif + +/** @} */ diff --git a/Sming/Arch/Esp32/Components/driver/os_timer.cpp b/Sming/Arch/Esp32/Components/driver/os_timer.cpp index 783c0d6249..83a714f65b 100644 --- a/Sming/Arch/Esp32/Components/driver/os_timer.cpp +++ b/Sming/Arch/Esp32/Components/driver/os_timer.cpp @@ -1,9 +1,107 @@ +// Copyright 2010-2017 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * ets_timer module implements a set of legacy timer APIs which are + * used by the WiFi driver. This is done on top of the newer esp_timer APIs. + * Applications should not use ets_timer functions, as they may change without + * notice. + */ + #include +#include #include #include -void IRAM_ATTR os_timer_arm_ticks(os_timer_t* ptimer, uint32_t ticks, bool repeat_flag) +static bool isTimerValid(smg_timer_t* ptimer) +{ + return ptimer != nullptr && ptimer->handle != nullptr; +} + +void smg_timer_setfn(smg_timer_t* ptimer, os_timer_func_t pfunction, void* parg) +{ + assert(ptimer != nullptr); + + if(ptimer == nullptr) { + return; + } + if(ptimer->handle != nullptr) { + smg_timer_done(ptimer); + } + + const esp_timer_create_args_t create_args = { + .callback = [](void* arg) -> void { + auto t = static_cast(arg); + System.queueCallback(t->timer_func, t->timer_arg); + }, + .arg = ptimer, + .dispatch_method = ESP_TIMER_TASK, + .name = "smg_timer_t", + }; + + ESP_ERROR_CHECK(esp_timer_create(&create_args, &ptimer->handle)); + + ptimer->timer_func = pfunction; + ptimer->timer_arg = parg; +} + +void IRAM_ATTR smg_timer_arm_ticks(smg_timer_t* ptimer, uint32_t ticks, bool repeat_flag) { uint32_t us = OsTimerClock::ticksToTime(ticks); - ets_timer_arm_us(ptimer, us, repeat_flag); + smg_timer_arm_us(ptimer, us, repeat_flag); +} + +void IRAM_ATTR smg_timer_arm_us(smg_timer_t* ptimer, uint32_t time_us, bool repeat_flag) +{ + assert(isTimerValid(ptimer)); + + esp_timer_stop(ptimer->handle); // no error check + if(repeat_flag) { + ESP_ERROR_CHECK(esp_timer_start_periodic(ptimer->handle, time_us)); + } else { + ESP_ERROR_CHECK(esp_timer_start_once(ptimer->handle, time_us)); + } +} + +void IRAM_ATTR smg_timer_arm(smg_timer_t* ptimer, uint32_t time_ms, bool repeat_flag) +{ + assert(isTimerValid(ptimer)); + + uint64_t time_us = 1000LL * uint64_t(time_ms); + esp_timer_stop(ptimer->handle); // no error check + if(repeat_flag) { + ESP_ERROR_CHECK(esp_timer_start_periodic(ptimer->handle, time_us)); + } else { + ESP_ERROR_CHECK(esp_timer_start_once(ptimer->handle, time_us)); + } +} + +void smg_timer_done(smg_timer_t* ptimer) +{ + if(!isTimerValid(ptimer)) { + return; + } + + esp_timer_delete(ptimer->handle); + *ptimer = smg_timer_t{}; +} + +void IRAM_ATTR smg_timer_disarm(smg_timer_t* ptimer) +{ + if(!isTimerValid(ptimer)) { + return; + } + + esp_timer_stop(ptimer->handle); } diff --git a/Sming/Arch/Esp32/Components/driver/uart.cpp b/Sming/Arch/Esp32/Components/driver/uart.cpp index 8538732951..34752c9d76 100644 --- a/Sming/Arch/Esp32/Components/driver/uart.cpp +++ b/Sming/Arch/Esp32/Components/driver/uart.cpp @@ -880,6 +880,9 @@ void smg_uart_detach_all() inst.handle = nullptr; } auto dev = getDevice(uart_nr); + // Wait for any outgoing data to finish sending (e.g. at boot time) + while(uart_txfifo_count(dev) != 0) { + } dev->conf1.val = 0; dev->int_clr.val = 0x0007ffff; dev->int_ena.val = 0; diff --git a/Sming/Arch/Esp32/Components/esp32/component.mk b/Sming/Arch/Esp32/Components/esp32/component.mk index 70ccb9f217..b363b93cda 100644 --- a/Sming/Arch/Esp32/Components/esp32/component.mk +++ b/Sming/Arch/Esp32/Components/esp32/component.mk @@ -214,6 +214,14 @@ EXTRA_LDFLAGS := \ -u pthread_include_pthread_cond_impl \ -u pthread_include_pthread_local_storage_impl \ -Wl,--undefined=uxTopUsedPriority \ + $(call Wrap,\ + esp_event_loop_create_default \ + esp_event_handler_register \ + esp_event_handler_unregister \ + esp_event_handler_instance_register \ + esp_event_handler_instance_unregister \ + esp_event_post \ + esp_event_isr_post) \ $(LDFLAGS_$(ESP_VARIANT)) SDK_DEFAULT_PATH := $(COMPONENT_PATH)/sdk diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/config/common b/Sming/Arch/Esp32/Components/esp32/sdk/config/common index 2d78a855f8..eb0ee896e9 100644 --- a/Sming/Arch/Esp32/Components/esp32/sdk/config/common +++ b/Sming/Arch/Esp32/Components/esp32/sdk/config/common @@ -24,9 +24,8 @@ CONFIG_ETH_SPI_ETHERNET_W5500=y CONFIG_ETH_SPI_ETHERNET_DM9051=y # Mandatory Sming framework changes -CONFIG_ESP_TIMER_TASK_STACK_SIZE=16384 -CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=8192 -CONFIG_ESP_TIMER_IMPL_FRC2=y +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=16384 +CONFIG_ESP_TASK_WDT_TIMEOUT_S=8 # Required by HardwareSPI library CONFIG_SPI_MASTER_IN_IRAM=y diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/config/debug b/Sming/Arch/Esp32/Components/esp32/sdk/config/debug index 14f1c0a1b3..55e48a89ac 100644 --- a/Sming/Arch/Esp32/Components/esp32/sdk/config/debug +++ b/Sming/Arch/Esp32/Components/esp32/sdk/config/debug @@ -3,23 +3,15 @@ # # The bootloader logs all type of messages -CONFIG_BOOTLOADER_LOG_LEVEL_NONE= -CONFIG_BOOTLOADER_LOG_LEVEL_ERROR= -CONFIG_BOOTLOADER_LOG_LEVEL_WARN= -CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y -CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= -CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= CONFIG_BOOTLOADER_LOG_LEVEL=3 # ESP-IDF logs all type of messages -CONFIG_LOG_DEFAULT_LEVEL_NONE= -CONFIG_LOG_DEFAULT_LEVEL_ERROR= -CONFIG_LOG_DEFAULT_LEVEL_WARN= -CONFIG_LOG_DEFAULT_LEVEL_INFO=y -CONFIG_LOG_DEFAULT_LEVEL_DEBUG= -CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= CONFIG_LOG_DEFAULT_LEVEL=3 # Sets watchpoint index 1 (the second of two) at the end of any task stack. This is the most accurate way to debug task stack overflows. CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y +# Used by TaskStat class to track task usage +CONFIG_FREERTOS_USE_TRACE_FACILITY=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/config/release b/Sming/Arch/Esp32/Components/esp32/sdk/config/release index f130372a9e..2e3743a7c1 100644 --- a/Sming/Arch/Esp32/Components/esp32/sdk/config/release +++ b/Sming/Arch/Esp32/Components/esp32/sdk/config/release @@ -3,20 +3,8 @@ # # The bootloader logs only errors -CONFIG_BOOTLOADER_LOG_LEVEL_NONE= -CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y -CONFIG_BOOTLOADER_LOG_LEVEL_WARN= -CONFIG_BOOTLOADER_LOG_LEVEL_INFO= -CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= -CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= CONFIG_BOOTLOADER_LOG_LEVEL=1 # ESP-IDF logs only errors -CONFIG_LOG_DEFAULT_LEVEL_NONE= -CONFIG_LOG_DEFAULT_LEVEL_ERROR=y -CONFIG_LOG_DEFAULT_LEVEL_WARN= -CONFIG_LOG_DEFAULT_LEVEL_INFO= -CONFIG_LOG_DEFAULT_LEVEL_DEBUG= -CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= CONFIG_LOG_DEFAULT_LEVEL=1 diff --git a/Sming/Arch/Esp32/Components/esp32/src/event_loop.cpp b/Sming/Arch/Esp32/Components/esp32/src/event_loop.cpp new file mode 100644 index 0000000000..0972f7c008 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/src/event_loop.cpp @@ -0,0 +1,110 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * event_loop.cpp + * + * This code replaces the standard IDF event loop with our own, *without* associated task. + * This not only reduces the system overhead but avoids the need for additional synchronisation + * management because WiFi events, etc. are all called in the context of the main Sming task. + */ + +#include + +namespace +{ +esp_event_loop_handle_t sming_event_loop; +} + +esp_event_loop_handle_t sming_create_event_loop() +{ + esp_event_loop_args_t loop_args = { + .queue_size = CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE, + .task_name = nullptr, + }; + + ESP_ERROR_CHECK(esp_event_loop_create(&loop_args, &sming_event_loop)); + + return sming_event_loop; +} + +namespace +{ +#define WRAP(name) esp_err_t __wrap_##name + +extern "C" { + +WRAP(esp_event_loop_create_default)() +{ + return ESP_ERR_INVALID_STATE; +} + +WRAP(esp_event_handler_register) +(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void* event_handler_arg) +{ + if(sming_event_loop == nullptr) { + return ESP_ERR_INVALID_STATE; + } + + return esp_event_handler_register_with(sming_event_loop, event_base, event_id, event_handler, event_handler_arg); +} + +WRAP(esp_event_handler_unregister) +(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler) +{ + if(sming_event_loop == nullptr) { + return ESP_ERR_INVALID_STATE; + } + + return esp_event_handler_unregister_with(sming_event_loop, event_base, event_id, event_handler); +} + +WRAP(esp_event_handler_instance_register) +(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void* event_handler_arg, + esp_event_handler_instance_t* instance) +{ + if(sming_event_loop == nullptr) { + return ESP_ERR_INVALID_STATE; + } + + return esp_event_handler_instance_register_with(sming_event_loop, event_base, event_id, event_handler, + event_handler_arg, instance); +} + +WRAP(esp_event_handler_instance_unregister) +(esp_event_base_t event_base, int32_t event_id, esp_event_handler_instance_t context) +{ + if(sming_event_loop == nullptr) { + return ESP_ERR_INVALID_STATE; + } + + return esp_event_handler_instance_unregister_with(sming_event_loop, event_base, event_id, context); +} + +WRAP(esp_event_post) +(esp_event_base_t event_base, int32_t event_id, void* event_data, size_t event_data_size, TickType_t ticks_to_wait) +{ + if(sming_event_loop == nullptr) { + return ESP_ERR_INVALID_STATE; + } + + return esp_event_post_to(sming_event_loop, event_base, event_id, event_data, event_data_size, ticks_to_wait); +} + +#if CONFIG_ESP_EVENT_POST_FROM_ISR +IRAM_ATTR WRAP(esp_event_isr_post)(esp_event_base_t event_base, int32_t event_id, void* event_data, + size_t event_data_size, BaseType_t* task_unblocked) +{ + if(sming_event_loop == nullptr) { + return ESP_ERR_INVALID_STATE; + } + + return esp_event_isr_post_to(sming_event_loop, event_base, event_id, event_data, event_data_size, task_unblocked); +} +#endif + +} // extern "C" + +} // namespace diff --git a/Sming/Arch/Esp32/Components/esp32/src/include/esp_tasks.h b/Sming/Arch/Esp32/Components/esp32/src/include/esp_tasks.h index 33da8e2dfe..ca7bf7f89a 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/include/esp_tasks.h +++ b/Sming/Arch/Esp32/Components/esp32/src/include/esp_tasks.h @@ -14,10 +14,7 @@ typedef struct { } os_event_t; enum { - USER_TASK_PRIO_0, USER_TASK_PRIO_1, - USER_TASK_PRIO_2, - USER_TASK_PRIO_MAX, }; typedef void (*os_task_t)(os_event_t* e); @@ -25,16 +22,6 @@ typedef void (*os_task_t)(os_event_t* e); bool system_os_task(os_task_t task, uint8_t prio, os_event_t* queue, uint8_t qlen); bool system_os_post(uint8_t prio, os_signal_t sig, os_param_t par); -// Setup default task queues -void ets_init_tasks(); - -// Hook function to process task queues -void ets_service_tasks(); - -typedef void (*host_task_callback_t)(uint32_t param); - -bool host_queue_callback(host_task_callback_t callback, uint32_t param); - #ifdef __cplusplus } #endif diff --git a/Sming/Arch/Esp32/Components/esp32/src/startup.cpp b/Sming/Arch/Esp32/Components/esp32/src/startup.cpp index 13908fa2e9..a754cfa261 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/startup.cpp +++ b/Sming/Arch/Esp32/Components/esp32/src/startup.cpp @@ -11,7 +11,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -22,23 +23,22 @@ #include #ifndef DISABLE_WIFI #include -#include #include #endif #endif -#ifndef ESP32_STACK_SIZE -#define ESP32_STACK_SIZE 16384U -#endif - extern void init(); +extern esp_event_loop_handle_t sming_create_event_loop(); namespace { -static constexpr uint32_t WDT_TIMEOUT_MS{8000}; - #ifndef DISABLE_WIFI -void esp_init_flash() +esp_event_handler_t wifiEventHandler; + +/* + * Initalise NVS which IDF WiFi uses to store configuration parameters. + */ +void esp_init_nvs() { esp_err_t ret = nvs_flash_init(); if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { @@ -48,11 +48,16 @@ void esp_init_flash() ESP_ERROR_CHECK(ret); } +/* + * Initialise default WiFi stack + */ void esp_init_wifi() { esp_netif_init(); - esp_event_loop_create_default(); - + if(wifiEventHandler != nullptr) { + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifiEventHandler, nullptr)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifiEventHandler, nullptr)); + } wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); } @@ -60,39 +65,57 @@ void esp_init_wifi() void main(void*) { - assert(esp_task_wdt_init(WDT_TIMEOUT_MS, true) == ESP_OK); + assert(esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, true) == ESP_OK); assert(esp_task_wdt_add(NULL) == ESP_OK); assert(esp_task_wdt_status(NULL) == ESP_OK); hw_timer_init(); smg_uart_detach_all(); - esp_log_set_vprintf(m_vprintf); + auto loop = sming_create_event_loop(); + #ifndef DISABLE_WIFI - esp_init_flash(); + esp_init_nvs(); esp_init_wifi(); #endif - ets_init_tasks(); - Storage::initialize(); + System.initialize(); + Storage::initialize(); init(); + constexpr unsigned maxEventLoopInterval{1000 / portTICK_PERIOD_MS}; while(true) { esp_task_wdt_reset(); - ets_service_tasks(); + esp_event_loop_run(loop, maxEventLoopInterval); } } } // namespace +/* + * Called from WiFi event implementation constructor. + * Cannot register directly as event queue hasn't been created yet. + * NOTE: May only be called once. + */ +void wifi_set_event_handler_cb(esp_event_handler_t eventHandler) +{ +#ifndef DISABLE_WIFI + wifiEventHandler = eventHandler; +#endif +} + +extern void sming_create_task(TaskFunction_t); + extern "C" void app_main(void) { -#ifdef CONFIG_FREERTOS_UNICORE - xTaskCreate(main, "Sming", ESP32_STACK_SIZE, nullptr, 1, nullptr); +#if defined(SUBARCH_ESP32) && !CONFIG_FREERTOS_UNICORE + constexpr unsigned core_id{1}; #else - esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(1)); - xTaskCreatePinnedToCore(main, "Sming", ESP32_STACK_SIZE, nullptr, 1, nullptr, 1); + constexpr unsigned core_id{0}; #endif + + esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(core_id)); + xTaskCreatePinnedToCore(main, "Sming", ESP_TASKD_EVENT_STACK, nullptr, ESP_TASKD_EVENT_PRIO, nullptr, core_id); } diff --git a/Sming/Arch/Esp32/Components/esp32/src/tasks.cpp b/Sming/Arch/Esp32/Components/esp32/src/tasks.cpp index 9cacd1000e..579275c86b 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/tasks.cpp +++ b/Sming/Arch/Esp32/Components/esp32/src/tasks.cpp @@ -1,105 +1,66 @@ #include "include/esp_tasks.h" -#include +#include #include -class TaskQueue +namespace { -public: - TaskQueue(os_task_t callback, os_event_t* events, uint8_t length) - { - this->callback = callback; - this->events = events; - this->length = length; - read = count = 0; - } +ESP_EVENT_DEFINE_BASE(TaskEvt); - bool IRAM_ATTR post(os_signal_t sig, os_param_t par) - { - bool full = (count == length); - if(!full) { - events[(read + count) % length] = os_event_t{sig, par}; - ++count; - } - return !full; - } +os_task_t taskCallback; - void process() - { - // Don't service any newly queued events - for(unsigned n = count; n != 0; --n) { - auto evt = events[read]; - read = (read + 1) % length; - --count; - callback(&evt); - } - } +} // namespace -private: - os_task_t callback; - os_event_t* events; - uint8_t read; - uint8_t count; - uint8_t length; -}; +bool system_os_task(os_task_t callback, uint8_t prio, os_event_t* events, uint8_t qlen) +{ + auto handler = [](void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { + assert(taskCallback != nullptr); -static TaskQueue* task_queues[USER_TASK_PRIO_MAX + 1]; + os_event_t ev{os_signal_t(event_id), 0}; + if(event_data != nullptr) { + ev.par = *static_cast(event_data); + } -const uint8_t HOST_TASK_PRIO = USER_TASK_PRIO_MAX; + taskCallback(&ev); + }; -bool system_os_task(os_task_t callback, uint8_t prio, os_event_t* events, uint8_t qlen) -{ - if(prio >= USER_TASK_PRIO_MAX) { - debug_e("TQ: Invalid priority %u", prio); + if(callback == nullptr) { + debug_e("TQ: Callback missing"); return false; } - auto& queue = task_queues[prio]; - if(queue != nullptr) { - debug_w("TQ: Queue %u already initialised", prio); + + if(prio != USER_TASK_PRIO_1) { + debug_e("TQ: Invalid priority %u", prio); return false; } - queue = new TaskQueue(callback, events, qlen); - return queue != nullptr; -} - -bool IRAM_ATTR system_os_post(uint8_t prio, os_signal_t sig, os_param_t par) -{ - if(prio >= USER_TASK_PRIO_MAX) { + if(taskCallback != nullptr) { + debug_w("TQ: Queue %u already initialised", prio); return false; } - auto& queue = task_queues[prio]; - if(queue == nullptr) { + + auto err = esp_event_handler_instance_register(TaskEvt, ESP_EVENT_ANY_ID, handler, nullptr, nullptr); + if(err != ESP_OK) { + debug_e("TQ: Failed to register handler"); return false; } - return task_queues[prio]->post(sig, par); -} - -void ets_init_tasks() -{ - static os_event_t events[32]; + taskCallback = callback; - auto hostTaskCallback = [](os_event_t* event) { - auto callback = host_task_callback_t(event->sig); - if(callback != nullptr) { - callback(event->par); - } - }; + debug_i("TQ: Registered %s", TaskEvt); - task_queues[HOST_TASK_PRIO] = new TaskQueue(hostTaskCallback, events, ARRAY_SIZE(events)); + return true; } -void ets_service_tasks() +bool IRAM_ATTR system_os_post(uint8_t prio, os_signal_t sig, os_param_t par) { - for(int prio = HOST_TASK_PRIO; prio >= 0; --prio) { - auto queue = task_queues[prio]; - if(queue != nullptr) { - queue->process(); - } + if(prio != USER_TASK_PRIO_1) { + return false; } -} - -bool host_queue_callback(host_task_callback_t callback, uint32_t param) -{ - return task_queues[HOST_TASK_PRIO]->post(os_signal_t(callback), param); + esp_err_t err; + if(par == 0) { + err = esp_event_isr_post(TaskEvt, sig, nullptr, 0, nullptr); + } else { + err = esp_event_isr_post(TaskEvt, sig, &par, sizeof(par), nullptr); + } + return (err == ESP_OK); } diff --git a/Sming/Arch/Esp32/Components/libc/component.mk b/Sming/Arch/Esp32/Components/libc/component.mk index e7bac1bcb1..3980983c81 100644 --- a/Sming/Arch/Esp32/Components/libc/component.mk +++ b/Sming/Arch/Esp32/Components/libc/component.mk @@ -3,6 +3,4 @@ COMPONENT_INCDIRS := src/include COMPONENT_DOXYGEN_INPUT := src/include/sys -EXTRA_LDFLAGS := \ - -Wl,-wrap,_write_r \ - -Wl,-wrap,_read_r \ +EXTRA_LDFLAGS := $(call Wrap,_write_r _read_r) diff --git a/Sming/Arch/Esp32/Components/sming-arch/component.mk b/Sming/Arch/Esp32/Components/sming-arch/component.mk index 1051b13cb9..24c3b98a72 100644 --- a/Sming/Arch/Esp32/Components/sming-arch/component.mk +++ b/Sming/Arch/Esp32/Components/sming-arch/component.mk @@ -4,7 +4,8 @@ IDF_TARGET ?= esp32 COMPONENT_SRCDIRS := \ $(ARCH_CORE) $(call ListAllSubDirs,$(ARCH_CORE)) \ $(ARCH_SYS) \ - $(ARCH_BASE)/Platform + $(ARCH_BASE)/Platform \ + $(ARCH_BASE)/Services/Profiling COMPONENT_INCDIRS := \ $(ARCH_BASE) \ diff --git a/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp b/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp index 0079c0c690..76f01d16c0 100644 --- a/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp +++ b/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp @@ -15,36 +15,6 @@ #include #include -/* - * Physical <-> Virtual address mapping is handled in `$IDF_COMPONENTS/spi_flash/flash_mmap.c`. - * - * See esp32 technical reference. - */ -uint32_t flashmem_get_address(const void* memptr) -{ - auto vaddr = reinterpret_cast(memptr); - if(vaddr < SOC_DROM_LOW || vaddr >= SOC_DROM_HIGH) { - return 0; - } - - auto offset = vaddr - SOC_MMU_VADDR0_START_ADDR; - uint32_t page = SOC_MMU_DROM0_PAGES_START + (offset / SPI_FLASH_MMU_PAGE_SIZE); -#if CONFIG_IDF_TARGET_ESP32 && !CONFIG_FREERTOS_UNICORE - uint32_t entry = DPORT_APP_FLASH_MMU_TABLE[page]; -#else - uint32_t entry = SOC_MMU_DPORT_PRO_FLASH_MMU_TABLE[page]; -#endif - - if(entry == SOC_MMU_INVALID_ENTRY_VAL) { - debug_e("Invalid flash address %p (page %u, entry 0x%08x)", memptr, page, entry); - return 0; - } - - entry &= SOC_MMU_ADDR_MASK; - uint32_t paddr = (entry * SPI_FLASH_MMU_PAGE_SIZE) + (vaddr % SPI_FLASH_MMU_PAGE_SIZE); - return paddr; -} - uint32_t flashmem_write(const void* from, uint32_t toaddr, uint32_t size) { esp_err_t r = spi_flash_write(toaddr, from, size); diff --git a/Sming/Arch/Esp32/Components/spi_flash/include/esp_spi_flash.h b/Sming/Arch/Esp32/Components/spi_flash/include/esp_spi_flash.h index 6cf2883115..f4b34017ef 100644 --- a/Sming/Arch/Esp32/Components/spi_flash/include/esp_spi_flash.h +++ b/Sming/Arch/Esp32/Components/spi_flash/include/esp_spi_flash.h @@ -86,7 +86,11 @@ typedef struct { * the internal flash memory size. * @note The flash location is dependent on where rBoot has mapped the firmware. */ -uint32_t flashmem_get_address(const void* memptr); +static inline uint32_t flashmem_get_address(const void* memptr) +{ + auto phys = spi_flash_cache2phys(memptr); + return (phys == SPI_FLASH_CACHE2PHYS_FAIL) ? 0 : phys; +} /** @brief Write a block of data to flash * @param from Buffer to obtain data from diff --git a/Sming/Arch/Esp32/Services/Profiling/TaskStat.cpp b/Sming/Arch/Esp32/Services/Profiling/TaskStat.cpp new file mode 100644 index 0000000000..03754e121e --- /dev/null +++ b/Sming/Arch/Esp32/Services/Profiling/TaskStat.cpp @@ -0,0 +1,135 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * TaskStat.cpp + * + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include + +namespace Profiling +{ +struct TaskStat::Info { +#if CONFIG_FREERTOS_USE_TRACE_FACILITY && CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS + unsigned count{0}; + uint32_t runTime; + TaskStatus_t status[maxTasks]; +#endif +}; + +TaskStat::TaskStat(Print& out) : out(out) +{ +#if CONFIG_FREERTOS_USE_TRACE_FACILITY && CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS + taskInfo.reset(new Info[2]); +#endif +} + +TaskStat::~TaskStat() +{ +} + +bool TaskStat::update() +{ +#if CONFIG_FREERTOS_USE_TRACE_FACILITY && CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS + + // Get current task states + auto& info = taskInfo[endIndex]; + info.count = uxTaskGetSystemState(info.status, maxTasks, &info.runTime); + if(info.count == 0) { + out.println(_F("[TaskStat] uxTaskGetSystemState returned 0")); + return false; + } + + std::qsort(info.status, info.count, sizeof(TaskStatus_t), [](const void* a, const void* b) -> int { + auto s1 = static_cast(a); + auto s2 = static_cast(b); + int res = 0; +#if CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID + res = s1->xCoreID - s2->xCoreID; +#endif + if(res == 0) { + res = s2->uxCurrentPriority - s1->uxCurrentPriority; + } + if(res == 0) { + res = s1->xTaskNumber - s2->xTaskNumber; + } + return res; + }); + + if(startIndex == endIndex) { + endIndex = 1; + return true; + } + + auto& startInfo = taskInfo[startIndex]; + auto& endInfo = taskInfo[endIndex]; + + // Set indices for next update + endIndex = startIndex; + startIndex = 1 - endIndex; + + // Calculate totalElapsedTime in units of run time stats clock period. + uint32_t totalElapsedTime = endInfo.runTime - startInfo.runTime; + if(totalElapsedTime == 0) { + out.println(_F("[TaskStat] Run time was 0: Have you set CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS?")); + return false; + } + + std::bitset startMatched, endMatched; + + PSTR_ARRAY(hdrfmt, "# | Core | Prio | Run Time | % Time | Name"); + PSTR_ARRAY(datfmt, "%-3u | %c | %4u | %8u | %3u%% | %s\r\n"); + out.println(); + out.println(hdrfmt); + // Match each task in startInfo.status to those in the endInfo.status + for(unsigned i = 0; i < startInfo.count; i++) { + int k = -1; + for(unsigned j = 0; j < endInfo.count; j++) { + if(startInfo.status[i].xHandle == endInfo.status[j].xHandle) { + k = j; + startMatched[i] = endMatched[j] = true; + break; + } + } + // Check if matching task found + if(k >= 0) { + auto& status = startInfo.status[i]; + uint32_t taskElapsedTime = endInfo.status[k].ulRunTimeCounter - status.ulRunTimeCounter; + uint32_t percentageTime = (taskElapsedTime * 100UL) / (totalElapsedTime * portNUM_PROCESSORS); + BaseType_t coreId{CONFIG_FREERTOS_NO_AFFINITY}; +#if CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID + coreId = status.xCoreID; +#endif + out.printf(datfmt, status.xTaskNumber, (coreId == CONFIG_FREERTOS_NO_AFFINITY) ? '-' : ('0' + coreId), + status.uxCurrentPriority, taskElapsedTime, percentageTime, status.pcTaskName); + } + } + + // Print unmatched tasks + for(unsigned i = 0; i < startInfo.count; i++) { + if(!startMatched[i]) { + out.printf("Deleted: %s\r\n", startInfo.status[i].pcTaskName); + } + } + for(unsigned i = 0; i < endInfo.count; i++) { + if(!endMatched[i]) { + out.printf("Created: %s\r\n", endInfo.status[i].pcTaskName); + } + } + + return true; + +#else + out.println("[TaskStat] Tracing disabled"); + return false; +#endif +} + +} // namespace Profiling diff --git a/Sming/Arch/Esp8266/Components/driver/include/driver/os_timer.h b/Sming/Arch/Esp8266/Components/driver/include/driver/os_timer.h index e00ea28047..431075a973 100644 --- a/Sming/Arch/Esp8266/Components/driver/include/driver/os_timer.h +++ b/Sming/Arch/Esp8266/Components/driver/include/driver/os_timer.h @@ -16,6 +16,12 @@ #include +// Disarmed +#define OS_TIMER_DEFAULT() \ + { \ + .timer_next = (os_timer_t*)-1, \ + } + #ifdef __cplusplus extern "C" { #endif @@ -37,6 +43,24 @@ extern "C" { */ void os_timer_arm_ticks(os_timer_t* ptimer, uint32_t ticks, bool repeat_flag); +static inline bool os_timer_is_armed(const os_timer_t* ptimer) +{ + return ptimer != nullptr && int(ptimer->timer_next) != -1; +} + +static inline uint64_t os_timer_expire(const os_timer_t* ptimer) +{ + if(ptimer == nullptr || int(ptimer->timer_next) == -1) { + return 0; + } + return ptimer->timer_expire; +} + +static inline void os_timer_done(os_timer_t* ptimer) +{ + ets_timer_disarm(ptimer); +} + /** @} */ #ifdef __cplusplus diff --git a/Sming/Arch/Esp8266/Components/esp8266/component.mk b/Sming/Arch/Esp8266/Components/esp8266/component.mk index ff8ddb3495..6470a76db2 100644 --- a/Sming/Arch/Esp8266/Components/esp8266/component.mk +++ b/Sming/Arch/Esp8266/Components/esp8266/component.mk @@ -52,7 +52,7 @@ COMPONENT_DOXYGEN_INPUT := \ $(SDK_INCDIR)/pwm.h # Crash handler hooks this so debugger can be invoked -EXTRA_LDFLAGS := -Wl,-wrap,system_restart_local +EXTRA_LDFLAGS := $(call Wrap,system_restart_local) # LIBDIRS += $(SDK_LIBDIR) diff --git a/Sming/Arch/Esp8266/Components/sming-arch/component.mk b/Sming/Arch/Esp8266/Components/sming-arch/component.mk index 19e419c499..ba606f85aa 100644 --- a/Sming/Arch/Esp8266/Components/sming-arch/component.mk +++ b/Sming/Arch/Esp8266/Components/sming-arch/component.mk @@ -1,7 +1,8 @@ COMPONENT_SRCDIRS := \ $(ARCH_CORE) $(call ListAllSubDirs,$(ARCH_CORE)) \ $(ARCH_SYS) \ - $(ARCH_BASE)/Platform + $(ARCH_BASE)/Platform \ + $(ARCH_BASE)/Services/Profiling COMPONENT_INCDIRS := \ $(ARCH_BASE) \ diff --git a/Sming/Arch/Esp8266/Services/Profiling/TaskStat.cpp b/Sming/Arch/Esp8266/Services/Profiling/TaskStat.cpp new file mode 100644 index 0000000000..dcedcfcec3 --- /dev/null +++ b/Sming/Arch/Esp8266/Services/Profiling/TaskStat.cpp @@ -0,0 +1,32 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * TaskStat.cpp + * + */ + +#include + +namespace Profiling +{ +struct TaskStat::Info { +}; + +TaskStat::TaskStat(Print& out) : out(out) +{ +} + +TaskStat::~TaskStat() +{ +} + +bool TaskStat::update() +{ + out.println("[TaskStat] Not Implemented"); + return false; +} + +} // namespace Profiling diff --git a/Sming/Arch/Host/Components/driver/include/driver/os_timer.h b/Sming/Arch/Host/Components/driver/include/driver/os_timer.h index d145418828..e63933a3df 100644 --- a/Sming/Arch/Host/Components/driver/include/driver/os_timer.h +++ b/Sming/Arch/Host/Components/driver/include/driver/os_timer.h @@ -10,6 +10,12 @@ #include +// Disarmed +#define OS_TIMER_DEFAULT() \ + { \ + .timer_next = (os_timer_t*)-1, \ + } + #ifdef __cplusplus extern "C" { #endif @@ -36,13 +42,23 @@ struct os_timer_t { void* timer_arg; }; -void os_timer_arm_ticks(struct os_timer_t* ptimer, uint32_t ticks, bool repeat_flag); +void os_timer_arm_ticks(os_timer_t* ptimer, uint32_t ticks, bool repeat_flag); + +void os_timer_arm(os_timer_t* ptimer, uint32_t time, bool repeat_flag); +void os_timer_arm_us(os_timer_t* ptimer, uint32_t time, bool repeat_flag); -void os_timer_arm(struct os_timer_t* ptimer, uint32_t time, bool repeat_flag); -void os_timer_arm_us(struct os_timer_t* ptimer, uint32_t time, bool repeat_flag); +void os_timer_disarm(os_timer_t* ptimer); +void os_timer_setfn(os_timer_t* ptimer, os_timer_func_t* pfunction, void* parg); + +static inline uint64_t os_timer_expire(const os_timer_t* ptimer) +{ + if(ptimer == nullptr || int(ptimer->timer_next) == -1) { + return 0; + } + return ptimer->timer_expire; +} -void os_timer_disarm(struct os_timer_t* ptimer); -void os_timer_setfn(struct os_timer_t* ptimer, os_timer_func_t* pfunction, void* parg); +void os_timer_done(os_timer_t* ptimer); // Hook function to service timers void host_service_timers(); diff --git a/Sming/Arch/Host/Components/driver/os_timer.cpp b/Sming/Arch/Host/Components/driver/os_timer.cpp index 2737ae4b77..97779d0a53 100644 --- a/Sming/Arch/Host/Components/driver/os_timer.cpp +++ b/Sming/Arch/Host/Components/driver/os_timer.cpp @@ -86,6 +86,11 @@ void os_timer_setfn(struct os_timer_t* ptimer, os_timer_func_t* pfunction, void* } } +void os_timer_done(struct os_timer_t* ptimer) +{ + os_timer_disarm(ptimer); +} + // Called with mutex locked static os_timer_t* find_expired_timer() { diff --git a/Sming/Arch/Host/Components/sming-arch/component.mk b/Sming/Arch/Host/Components/sming-arch/component.mk index 6c49459dad..4ff9dec5c8 100644 --- a/Sming/Arch/Host/Components/sming-arch/component.mk +++ b/Sming/Arch/Host/Components/sming-arch/component.mk @@ -17,7 +17,8 @@ export ESP8266_COMPONENTS COMPONENT_SRCDIRS := \ $(ARCH_CORE) $(call ListAllSubDirs,$(ARCH_CORE)) \ $(ARCH_SYS) \ - $(ARCH_BASE)/Platform + $(ARCH_BASE)/Platform \ + $(ARCH_BASE)/Services/Profiling COMPONENT_INCDIRS := \ $(ARCH_BASE) \ diff --git a/Sming/Arch/Host/Services/Profiling/TaskStat.cpp b/Sming/Arch/Host/Services/Profiling/TaskStat.cpp new file mode 100644 index 0000000000..dcedcfcec3 --- /dev/null +++ b/Sming/Arch/Host/Services/Profiling/TaskStat.cpp @@ -0,0 +1,32 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * TaskStat.cpp + * + */ + +#include + +namespace Profiling +{ +struct TaskStat::Info { +}; + +TaskStat::TaskStat(Print& out) : out(out) +{ +} + +TaskStat::~TaskStat() +{ +} + +bool TaskStat::update() +{ + out.println("[TaskStat] Not Implemented"); + return false; +} + +} // namespace Profiling diff --git a/Sming/Components/Hosted/component.mk b/Sming/Components/Hosted/component.mk index 29dde196ff..3ae054f19c 100644 --- a/Sming/Components/Hosted/component.mk +++ b/Sming/Components/Hosted/component.mk @@ -12,7 +12,7 @@ ENABLE_HOSTED ?= ifneq ($(ENABLE_HOSTED),) COMPONENT_SRCDIRS += $(COMPONENT_PATH)/init/$(ENABLE_HOSTED) - EXTRA_LDFLAGS := -Wl,-wrap,host_init + EXTRA_LDFLAGS := $(call Wrap,host_init) COMPONENT_DEPENDS += SerialLib endif diff --git a/Sming/Components/Network/Arch/Esp32/Network/DM9051.cpp b/Sming/Components/Network/Arch/Esp32/Network/DM9051.cpp index 5b9de8b7cb..3544fe0864 100644 --- a/Sming/Components/Network/Arch/Esp32/Network/DM9051.cpp +++ b/Sming/Components/Network/Arch/Esp32/Network/DM9051.cpp @@ -23,7 +23,6 @@ DM9051PhyFactory DM9051Service::dm9051PhyFactory; bool DM9051Service::begin(const Config& config) { esp_netif_init(); - esp_event_loop_create_default(); esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH(); netif = esp_netif_new(&netif_cfg); diff --git a/Sming/Components/Network/Arch/Esp32/Network/W5500.cpp b/Sming/Components/Network/Arch/Esp32/Network/W5500.cpp index 51d0229a52..49662df6b7 100644 --- a/Sming/Components/Network/Arch/Esp32/Network/W5500.cpp +++ b/Sming/Components/Network/Arch/Esp32/Network/W5500.cpp @@ -23,7 +23,6 @@ W5500PhyFactory W5500Service::w5500PhyFactory; bool W5500Service::begin(const Config& config) { esp_netif_init(); - esp_event_loop_create_default(); esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH(); netif = esp_netif_new(&netif_cfg); diff --git a/Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp b/Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp index ad4870a0ff..c6ae63d901 100644 --- a/Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp +++ b/Sming/Components/Network/Arch/Esp32/Platform/EmbeddedEthernet.cpp @@ -25,8 +25,6 @@ bool EmbeddedEthernet::begin(const Config& config) #else esp_netif_init(); - esp_event_loop_create_default(); - esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); netif = esp_netif_new(&cfg); diff --git a/Sming/Components/Network/Arch/Esp32/Platform/WifiEventsImpl.cpp b/Sming/Components/Network/Arch/Esp32/Platform/WifiEventsImpl.cpp index b79a267b53..af37053c58 100644 --- a/Sming/Components/Network/Arch/Esp32/Platform/WifiEventsImpl.cpp +++ b/Sming/Components/Network/Arch/Esp32/Platform/WifiEventsImpl.cpp @@ -19,6 +19,7 @@ static WifiEventsImpl events; WifiEventsClass& WifiEvents = events; StationConnectionStatus WifiEventsImpl::stationConnectionStatus = eSCS_Idle; +extern void wifi_set_event_handler_cb(esp_event_handler_t eventHandler); ip_addr_t ip(esp_ip4_addr_t ip) { @@ -31,11 +32,7 @@ WifiEventsImpl::WifiEventsImpl() auto eventHandler = [](void* arg, esp_event_base_t base, int32_t id, void* data) -> void { events.WifiEventHandler(arg, base, id, data); }; - - esp_event_loop_create_default(); - - ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, eventHandler, NULL)); - ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, eventHandler, NULL)); + wifi_set_event_handler_cb(eventHandler); } void WifiEventsImpl::WifiEventHandler(void* arg, esp_event_base_t base, int32_t id, void* data) diff --git a/Sming/Components/malloc_count/component.mk b/Sming/Components/malloc_count/component.mk index 67ca9df614..4306eb9552 100644 --- a/Sming/Components/malloc_count/component.mk +++ b/Sming/Components/malloc_count/component.mk @@ -7,23 +7,25 @@ ifeq ($(ENABLE_MALLOC_COUNT),1) COMPONENT_CXXFLAGS += -DENABLE_MALLOC_COUNT=1 # Hook all the memory allocation functions we need to monitor heap activity -EXTRA_LDFLAGS := \ - -Wl,-wrap,malloc \ - -Wl,-wrap,calloc \ - -Wl,-wrap,realloc \ - -Wl,-wrap,free +MC_WRAP_FUNCS := \ + malloc \ + calloc \ + realloc \ + free ifeq ($(SMING_ARCH),Esp8266) -EXTRA_LDFLAGS += \ - -Wl,-wrap,pvPortMalloc \ - -Wl,-wrap,pvPortCalloc \ - -Wl,-wrap,pvPortRealloc \ - -Wl,-wrap,pvPortZalloc \ - -Wl,-wrap,pvPortZallocIram \ - -Wl,-wrap,vPortFree +MC_WRAP_FUNCS += \ + pvPortMalloc \ + pvPortCalloc \ + pvPortRealloc \ + pvPortZalloc \ + pvPortZallocIram \ + vPortFree else -EXTRA_LDFLAGS += \ - -Wl,-wrap,strdup +MC_WRAP_FUNCS += \ + strdup endif -endif \ No newline at end of file +EXTRA_LDFLAGS := $(call Wrap,$(MC_WRAP_FUNCS)) + +endif diff --git a/Sming/Core/SimpleTimer.h b/Sming/Core/SimpleTimer.h index 9293ed1732..c86e482946 100644 --- a/Sming/Core/SimpleTimer.h +++ b/Sming/Core/SimpleTimer.h @@ -54,22 +54,24 @@ class OsTimerApi : public CallbackTimerApi __forceinline bool isArmed() const { - return int(osTimer.timer_next) != -1; + return os_timer_expire(&osTimer) != 0; } TickType ticks() const { - if(!isArmed()) { + uint64_t expiry = os_timer_expire(&osTimer); + if(expiry == 0) { return 0; } - auto remain = int(osTimer.timer_expire - Clock::ticks()); + int remain = expiry - Clock::ticks(); return (remain > 0) ? remain : 0; } ~OsTimerApi() { disarm(); + os_timer_done(&osTimer); } __forceinline void IRAM_ATTR setCallback(TimerCallback callback, void* arg) @@ -100,9 +102,7 @@ class OsTimerApi : public CallbackTimerApi } private: - os_timer_t osTimer = { - reinterpret_cast(-1), // Disarmed - }; + os_timer_t osTimer = OS_TIMER_DEFAULT(); TickType interval = 0; }; diff --git a/Sming/Services/Profiling/TaskStat.h b/Sming/Services/Profiling/TaskStat.h new file mode 100644 index 0000000000..ca6c761338 --- /dev/null +++ b/Sming/Services/Profiling/TaskStat.h @@ -0,0 +1,59 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * TaskStat.h + * + */ + +#pragma once + +#include +#include + +namespace Profiling +{ +/** + * @brief Helper class to support printing of real-time task information + * + * Code is refactored from the FreeRTOS Real Time Stats Example. + * + * Requires these SDK configuration settings to be set: + * + * - FREERTOS_USE_TRACE_FACILITY + * - FREERTOS_GENERATE_RUN_TIME_STATS + * - FREERTOS_VTASKLIST_INCLUDE_COREID (optional) + * + */ +class TaskStat +{ +public: + /** + * @brief Constructor + * @param out Where to print reports (e.g. Serial) + */ + TaskStat(Print& out); + + ~TaskStat(); + + /** + * @brief Update the report + * + * Nothing will be output the first time this is called. + * From then on, the stats will show the difference in task usage + * from the previous call. + */ + bool update(); + +private: + Print& out; + static constexpr size_t maxTasks{32}; + struct Info; + std::unique_ptr taskInfo; + uint8_t startIndex{0}; + uint8_t endIndex{0}; +}; + +} // namespace Profiling diff --git a/Sming/build.mk b/Sming/build.mk index 0d3a480527..c3b8958cca 100644 --- a/Sming/build.mk +++ b/Sming/build.mk @@ -238,6 +238,11 @@ CLIB_PREFIX := clib- ToUpper = $(shell echo "$1" | tr 'a-z' 'A-Z') ToLower = $(shell echo "$1" | tr 'A-Z' 'a-z') +# Use with LDFLAGS to undefine and wrap a list of functions +define Wrap +$(foreach n,$1,-u $n -Wl,-wrap,$n) +endef + # Apply coding style to list of files using clang-format # $1 -> List of files define ClangFormat diff --git a/docs/source/framework/services/profiling/taskstat.rst b/docs/source/framework/services/profiling/taskstat.rst new file mode 100644 index 0000000000..43fcf79cff --- /dev/null +++ b/docs/source/framework/services/profiling/taskstat.rst @@ -0,0 +1,24 @@ +TaskStat +======== + +.. highlight:: c++ + +Example of use:: + + #include + + Profiling::TaskStat taskStat(Serial); + Timer statTimer; + + void init() + { + ... + + // Set up timer to print task information to Serial terminal every 2 seconds + statTimer.initializeMs<2000>(InterruptCallback([]() { taskStat.update(); })); + statTimer.start(); + } + + +.. doxygenclass:: Profiling::TaskStat + :members: diff --git a/samples/Basic_IFS/app/application.cpp b/samples/Basic_IFS/app/application.cpp index 6c60f80184..7aff2d69af 100644 --- a/samples/Basic_IFS/app/application.cpp +++ b/samples/Basic_IFS/app/application.cpp @@ -11,6 +11,7 @@ #include #include #include +#include // If you want, you can define WiFi settings globally in Eclipse Environment Variables #ifndef WIFI_SSID @@ -351,6 +352,9 @@ void fstest() listAttributes(); } +Profiling::TaskStat taskStat(Serial); +Timer statTimer; + } // namespace void init() @@ -373,4 +377,7 @@ void init() WifiAccessPoint.enable(false); WifiEvents.onStationGotIP(gotIP); + + statTimer.initializeMs<2000>(InterruptCallback([]() { taskStat.update(); })); + statTimer.start(); } From d8ba7d8687dfd1e7debf179734617bf94728d194 Mon Sep 17 00:00:00 2001 From: slaff Date: Fri, 24 Sep 2021 14:07:05 +0200 Subject: [PATCH 061/130] Updated axTLS to latest version. (#2372) [scan:coverity] --- Sming/Components/axtls-8266/axtls-8266 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sming/Components/axtls-8266/axtls-8266 b/Sming/Components/axtls-8266/axtls-8266 index 6c3e87b307..9ebc3ee6ec 160000 --- a/Sming/Components/axtls-8266/axtls-8266 +++ b/Sming/Components/axtls-8266/axtls-8266 @@ -1 +1 @@ -Subproject commit 6c3e87b307ffb67e670909a3c5ac613032eed0ba +Subproject commit 9ebc3ee6ec659a09f08f6e3d8b4d6c19db5ce498 From d33dfa2660f577f40ee3b4c6a9f84efea5c05a97 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 27 Sep 2021 09:14:03 +0100 Subject: [PATCH 062/130] Move `Ota` and `OtaNetwork` Components into `Libraries` (#2373) --- Sming/{Components => Libraries}/Ota/README.rst | 0 Sming/{Components => Libraries}/Ota/component.mk | 0 Sming/{Components => Libraries}/Ota/src/.cs | 0 .../{Components => Libraries}/Ota/src/Arch/Esp32/IdfUpgrader.cpp | 0 .../Ota/src/Arch/Esp32/include/Ota/IdfUpgrader.h | 0 .../Ota/src/Arch/Esp32/include/Ota/Upgrader.h | 0 .../Ota/src/Arch/Esp8266/RbootUpgrader.cpp | 0 .../Ota/src/Arch/Esp8266/include/Ota/RbootUpgrader.h | 0 .../Ota/src/Arch/Esp8266/include/Ota/Upgrader.h | 0 Sming/{Components => Libraries}/Ota/src/Manager.cpp | 0 Sming/{Components => Libraries}/Ota/src/UpgradeOutputStream.cpp | 0 Sming/{Components => Libraries}/Ota/src/include/Ota/Manager.h | 0 .../Ota/src/include/Ota/UpgradeOutputStream.h | 0 .../{Components => Libraries}/Ota/src/include/Ota/UpgraderBase.h | 0 Sming/{Components => Libraries}/OtaNetwork/README.rst | 0 Sming/{Components => Libraries}/OtaNetwork/component.mk | 0 Sming/{Components => Libraries}/OtaNetwork/src/.cs | 0 Sming/{Components => Libraries}/OtaNetwork/src/HttpUpgrader.cpp | 0 .../OtaNetwork/src/include/Ota/Network/HttpUpgrader.h | 0 19 files changed, 0 insertions(+), 0 deletions(-) rename Sming/{Components => Libraries}/Ota/README.rst (100%) rename Sming/{Components => Libraries}/Ota/component.mk (100%) rename Sming/{Components => Libraries}/Ota/src/.cs (100%) rename Sming/{Components => Libraries}/Ota/src/Arch/Esp32/IdfUpgrader.cpp (100%) rename Sming/{Components => Libraries}/Ota/src/Arch/Esp32/include/Ota/IdfUpgrader.h (100%) rename Sming/{Components => Libraries}/Ota/src/Arch/Esp32/include/Ota/Upgrader.h (100%) rename Sming/{Components => Libraries}/Ota/src/Arch/Esp8266/RbootUpgrader.cpp (100%) rename Sming/{Components => Libraries}/Ota/src/Arch/Esp8266/include/Ota/RbootUpgrader.h (100%) rename Sming/{Components => Libraries}/Ota/src/Arch/Esp8266/include/Ota/Upgrader.h (100%) rename Sming/{Components => Libraries}/Ota/src/Manager.cpp (100%) rename Sming/{Components => Libraries}/Ota/src/UpgradeOutputStream.cpp (100%) rename Sming/{Components => Libraries}/Ota/src/include/Ota/Manager.h (100%) rename Sming/{Components => Libraries}/Ota/src/include/Ota/UpgradeOutputStream.h (100%) rename Sming/{Components => Libraries}/Ota/src/include/Ota/UpgraderBase.h (100%) rename Sming/{Components => Libraries}/OtaNetwork/README.rst (100%) rename Sming/{Components => Libraries}/OtaNetwork/component.mk (100%) rename Sming/{Components => Libraries}/OtaNetwork/src/.cs (100%) rename Sming/{Components => Libraries}/OtaNetwork/src/HttpUpgrader.cpp (100%) rename Sming/{Components => Libraries}/OtaNetwork/src/include/Ota/Network/HttpUpgrader.h (100%) diff --git a/Sming/Components/Ota/README.rst b/Sming/Libraries/Ota/README.rst similarity index 100% rename from Sming/Components/Ota/README.rst rename to Sming/Libraries/Ota/README.rst diff --git a/Sming/Components/Ota/component.mk b/Sming/Libraries/Ota/component.mk similarity index 100% rename from Sming/Components/Ota/component.mk rename to Sming/Libraries/Ota/component.mk diff --git a/Sming/Components/Ota/src/.cs b/Sming/Libraries/Ota/src/.cs similarity index 100% rename from Sming/Components/Ota/src/.cs rename to Sming/Libraries/Ota/src/.cs diff --git a/Sming/Components/Ota/src/Arch/Esp32/IdfUpgrader.cpp b/Sming/Libraries/Ota/src/Arch/Esp32/IdfUpgrader.cpp similarity index 100% rename from Sming/Components/Ota/src/Arch/Esp32/IdfUpgrader.cpp rename to Sming/Libraries/Ota/src/Arch/Esp32/IdfUpgrader.cpp diff --git a/Sming/Components/Ota/src/Arch/Esp32/include/Ota/IdfUpgrader.h b/Sming/Libraries/Ota/src/Arch/Esp32/include/Ota/IdfUpgrader.h similarity index 100% rename from Sming/Components/Ota/src/Arch/Esp32/include/Ota/IdfUpgrader.h rename to Sming/Libraries/Ota/src/Arch/Esp32/include/Ota/IdfUpgrader.h diff --git a/Sming/Components/Ota/src/Arch/Esp32/include/Ota/Upgrader.h b/Sming/Libraries/Ota/src/Arch/Esp32/include/Ota/Upgrader.h similarity index 100% rename from Sming/Components/Ota/src/Arch/Esp32/include/Ota/Upgrader.h rename to Sming/Libraries/Ota/src/Arch/Esp32/include/Ota/Upgrader.h diff --git a/Sming/Components/Ota/src/Arch/Esp8266/RbootUpgrader.cpp b/Sming/Libraries/Ota/src/Arch/Esp8266/RbootUpgrader.cpp similarity index 100% rename from Sming/Components/Ota/src/Arch/Esp8266/RbootUpgrader.cpp rename to Sming/Libraries/Ota/src/Arch/Esp8266/RbootUpgrader.cpp diff --git a/Sming/Components/Ota/src/Arch/Esp8266/include/Ota/RbootUpgrader.h b/Sming/Libraries/Ota/src/Arch/Esp8266/include/Ota/RbootUpgrader.h similarity index 100% rename from Sming/Components/Ota/src/Arch/Esp8266/include/Ota/RbootUpgrader.h rename to Sming/Libraries/Ota/src/Arch/Esp8266/include/Ota/RbootUpgrader.h diff --git a/Sming/Components/Ota/src/Arch/Esp8266/include/Ota/Upgrader.h b/Sming/Libraries/Ota/src/Arch/Esp8266/include/Ota/Upgrader.h similarity index 100% rename from Sming/Components/Ota/src/Arch/Esp8266/include/Ota/Upgrader.h rename to Sming/Libraries/Ota/src/Arch/Esp8266/include/Ota/Upgrader.h diff --git a/Sming/Components/Ota/src/Manager.cpp b/Sming/Libraries/Ota/src/Manager.cpp similarity index 100% rename from Sming/Components/Ota/src/Manager.cpp rename to Sming/Libraries/Ota/src/Manager.cpp diff --git a/Sming/Components/Ota/src/UpgradeOutputStream.cpp b/Sming/Libraries/Ota/src/UpgradeOutputStream.cpp similarity index 100% rename from Sming/Components/Ota/src/UpgradeOutputStream.cpp rename to Sming/Libraries/Ota/src/UpgradeOutputStream.cpp diff --git a/Sming/Components/Ota/src/include/Ota/Manager.h b/Sming/Libraries/Ota/src/include/Ota/Manager.h similarity index 100% rename from Sming/Components/Ota/src/include/Ota/Manager.h rename to Sming/Libraries/Ota/src/include/Ota/Manager.h diff --git a/Sming/Components/Ota/src/include/Ota/UpgradeOutputStream.h b/Sming/Libraries/Ota/src/include/Ota/UpgradeOutputStream.h similarity index 100% rename from Sming/Components/Ota/src/include/Ota/UpgradeOutputStream.h rename to Sming/Libraries/Ota/src/include/Ota/UpgradeOutputStream.h diff --git a/Sming/Components/Ota/src/include/Ota/UpgraderBase.h b/Sming/Libraries/Ota/src/include/Ota/UpgraderBase.h similarity index 100% rename from Sming/Components/Ota/src/include/Ota/UpgraderBase.h rename to Sming/Libraries/Ota/src/include/Ota/UpgraderBase.h diff --git a/Sming/Components/OtaNetwork/README.rst b/Sming/Libraries/OtaNetwork/README.rst similarity index 100% rename from Sming/Components/OtaNetwork/README.rst rename to Sming/Libraries/OtaNetwork/README.rst diff --git a/Sming/Components/OtaNetwork/component.mk b/Sming/Libraries/OtaNetwork/component.mk similarity index 100% rename from Sming/Components/OtaNetwork/component.mk rename to Sming/Libraries/OtaNetwork/component.mk diff --git a/Sming/Components/OtaNetwork/src/.cs b/Sming/Libraries/OtaNetwork/src/.cs similarity index 100% rename from Sming/Components/OtaNetwork/src/.cs rename to Sming/Libraries/OtaNetwork/src/.cs diff --git a/Sming/Components/OtaNetwork/src/HttpUpgrader.cpp b/Sming/Libraries/OtaNetwork/src/HttpUpgrader.cpp similarity index 100% rename from Sming/Components/OtaNetwork/src/HttpUpgrader.cpp rename to Sming/Libraries/OtaNetwork/src/HttpUpgrader.cpp diff --git a/Sming/Components/OtaNetwork/src/include/Ota/Network/HttpUpgrader.h b/Sming/Libraries/OtaNetwork/src/include/Ota/Network/HttpUpgrader.h similarity index 100% rename from Sming/Components/OtaNetwork/src/include/Ota/Network/HttpUpgrader.h rename to Sming/Libraries/OtaNetwork/src/include/Ota/Network/HttpUpgrader.h From 9f61debc960cbcb87b3f20d5686fe1a0ffae9f56 Mon Sep 17 00:00:00 2001 From: slaff Date: Mon, 27 Sep 2021 10:14:20 +0200 Subject: [PATCH 063/130] Preparation for the release of Sming version 4.4. (#2364) --- README.md | 2 +- Sming/Core/SmingVersion.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 00a487b03d..02a98e7aa1 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ Supported SDK: ESP-IDF v4.3. See https://sming.readthedocs.io/en/latest/_inc/Smi ### Stable -- [Sming V4.3.0](https://github.com/SmingHub/Sming/releases/tag/4.3.0) - great new features, performance and stability improvements. +- [Sming V4.4.0](https://github.com/SmingHub/Sming/releases/tag/4.4.0) - great new features, performance and stability improvements. ### Long Term Support (LTS) diff --git a/Sming/Core/SmingVersion.h b/Sming/Core/SmingVersion.h index 363d7ebdb1..c3c07d1ff8 100644 --- a/Sming/Core/SmingVersion.h +++ b/Sming/Core/SmingVersion.h @@ -6,7 +6,7 @@ */ #define SMING_MAJOR_VERSION 4 -#define SMING_MINOR_VERSION 3 +#define SMING_MINOR_VERSION 4 #define SMING_PATCH_VERSION 0 #define SMING_PRE_RELEASE "" From 97aa2cdf9579b3abf8b4e3e2b085687ed768ae2e Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 29 Sep 2021 09:58:09 +0100 Subject: [PATCH 064/130] Add Kconfig support (#2374) Type `make manuconfig` in your Sming based application to see the available options. You might need to run `make python-requirements` before that if you are missing the required python libraries. Sample `Basic_IFS` provides an example how to add application specific configuration settings. --- .../Esp32/Components/esp32/sdk/config/debug | 12 ++ .../Esp32/Components/esp32/sdk/config/release | 12 ++ Sming/Arch/Esp8266/Components/driver/Kconfig | 22 ++++ Sming/Arch/Esp8266/Components/heap/Kconfig | 20 ++++ Sming/Components/Hosted/Kconfig | 37 ++++++ .../Hosted/samples/serial/README.rst | 2 +- Sming/Components/Network/Kconfig | 53 ++++++++ Sming/Components/Network/component.mk | 7 ++ Sming/Components/esptool/Kconfig | 9 ++ Sming/Components/rboot/Kconfig | 22 ++++ Sming/Components/ssl/Kconfig | 28 +++++ Sming/Components/terminal/Kconfig | 33 +++++ Sming/Kconfig | 113 ++++++++++++++++++ Sming/Libraries/OtaUpgrade/Kconfig | 41 +++++++ Sming/Libraries/OtaUpgradeMqtt/Kconfig | 20 ++++ Sming/Libraries/Spiffs/Kconfig | 30 +++++ Sming/Libraries/Spiffs/README.rst | 2 - Sming/Libraries/Spiffs/component.mk | 2 +- Sming/Libraries/Spiffs/src/FileSystem.cpp | 2 +- .../src/include/IFS/SPIFFS/FileSystem.h | 9 +- Sming/Platform/System.cpp | 19 +-- Sming/build.mk | 2 + Sming/component.mk | 7 -- Sming/project.mk | 27 ++++- Tools/cfgtool.py | 110 +++++++++++++++++ Tools/requirements.txt | 1 + samples/Basic_IFS/Kconfig | 9 ++ 27 files changed, 624 insertions(+), 27 deletions(-) create mode 100644 Sming/Arch/Esp8266/Components/driver/Kconfig create mode 100644 Sming/Arch/Esp8266/Components/heap/Kconfig create mode 100644 Sming/Components/Hosted/Kconfig create mode 100644 Sming/Components/Network/Kconfig create mode 100644 Sming/Components/esptool/Kconfig create mode 100644 Sming/Components/rboot/Kconfig create mode 100644 Sming/Components/ssl/Kconfig create mode 100644 Sming/Components/terminal/Kconfig create mode 100644 Sming/Kconfig create mode 100644 Sming/Libraries/OtaUpgrade/Kconfig create mode 100644 Sming/Libraries/OtaUpgradeMqtt/Kconfig create mode 100644 Sming/Libraries/Spiffs/Kconfig create mode 100644 Tools/cfgtool.py create mode 100644 samples/Basic_IFS/Kconfig diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/config/debug b/Sming/Arch/Esp32/Components/esp32/sdk/config/debug index 55e48a89ac..c74a1e37de 100644 --- a/Sming/Arch/Esp32/Components/esp32/sdk/config/debug +++ b/Sming/Arch/Esp32/Components/esp32/sdk/config/debug @@ -3,9 +3,21 @@ # # The bootloader logs all type of messages +CONFIG_BOOTLOADER_LOG_LEVEL_NONE= +CONFIG_BOOTLOADER_LOG_LEVEL_ERROR= +CONFIG_BOOTLOADER_LOG_LEVEL_WARN= +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y +CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= +CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= CONFIG_BOOTLOADER_LOG_LEVEL=3 # ESP-IDF logs all type of messages +CONFIG_LOG_DEFAULT_LEVEL_NONE= +CONFIG_LOG_DEFAULT_LEVEL_ERROR= +CONFIG_LOG_DEFAULT_LEVEL_WARN= +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +CONFIG_LOG_DEFAULT_LEVEL_DEBUG= +CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= CONFIG_LOG_DEFAULT_LEVEL=3 # Sets watchpoint index 1 (the second of two) at the end of any task stack. This is the most accurate way to debug task stack overflows. diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/config/release b/Sming/Arch/Esp32/Components/esp32/sdk/config/release index 2e3743a7c1..f130372a9e 100644 --- a/Sming/Arch/Esp32/Components/esp32/sdk/config/release +++ b/Sming/Arch/Esp32/Components/esp32/sdk/config/release @@ -3,8 +3,20 @@ # # The bootloader logs only errors +CONFIG_BOOTLOADER_LOG_LEVEL_NONE= +CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y +CONFIG_BOOTLOADER_LOG_LEVEL_WARN= +CONFIG_BOOTLOADER_LOG_LEVEL_INFO= +CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG= +CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE= CONFIG_BOOTLOADER_LOG_LEVEL=1 # ESP-IDF logs only errors +CONFIG_LOG_DEFAULT_LEVEL_NONE= +CONFIG_LOG_DEFAULT_LEVEL_ERROR=y +CONFIG_LOG_DEFAULT_LEVEL_WARN= +CONFIG_LOG_DEFAULT_LEVEL_INFO= +CONFIG_LOG_DEFAULT_LEVEL_DEBUG= +CONFIG_LOG_DEFAULT_LEVEL_VERBOSE= CONFIG_LOG_DEFAULT_LEVEL=1 diff --git a/Sming/Arch/Esp8266/Components/driver/Kconfig b/Sming/Arch/Esp8266/Components/driver/Kconfig new file mode 100644 index 0000000000..566932a5af --- /dev/null +++ b/Sming/Arch/Esp8266/Components/driver/Kconfig @@ -0,0 +1,22 @@ +menu "Drivers" + config USE_US_TIMER + bool "Enable microsecond precision for software timers (Timer2)" + help + The following functions depend on Timer2: + - NOW() return value, the Timer2 tick count + - Software timers + - System time + + Software timers are driven by Timer2, which by default uses a /256 prescale + providing a resolution of 3.2us and a range of 1' 54". + + Enabling this setting increases the resolution to 200ns but reduces the maximum + software timer to 7" 9.5s. + + config ENABLE_CUSTOM_PWM + bool "Use the *New PWM* driver" + default y + help + New PWM is a drop-in replacement for the version provided in the Espressif SDK. + +endmenu diff --git a/Sming/Arch/Esp8266/Components/heap/Kconfig b/Sming/Arch/Esp8266/Components/heap/Kconfig new file mode 100644 index 0000000000..b78223f531 --- /dev/null +++ b/Sming/Arch/Esp8266/Components/heap/Kconfig @@ -0,0 +1,20 @@ +menu "Heap" + config ENABLE_CUSTOM_HEAP + bool "Enable custom heap using UMM Malloc" + + if ENABLE_CUSTOM_HEAP + + config UMM_POISON_CHECK + bool "UMM Poison Check" + help + Add heap integrity checks + + config UMM_FUNC_IRAM + bool "Store custom heap functions in IRAM" + default y + help + Custom heap functions are stored in IRAM by default for performance reasons. + If you need the IRAM (about 1.5K bytes) then disable this option. + + endif +endmenu diff --git a/Sming/Components/Hosted/Kconfig b/Sming/Components/Hosted/Kconfig new file mode 100644 index 0000000000..2c06ba5ced --- /dev/null +++ b/Sming/Components/Hosted/Kconfig @@ -0,0 +1,37 @@ +menu "Hosted" + choice + depends on SMING_ARCH="Host" + default ENABLE_HOSTED_NONE + prompt "Hosted transport mechanism" + config ENABLE_HOSTED_NONE + bool "None" + config ENABLE_HOSTED_TCP + bool "TCP" + config ENABLE_HOSTED_SERIAL + bool "Serial" + endchoice + + config ENABLE_HOSTED + string + default "" if ENABLE_HOSTED_NONE + default "tcp" if ENABLE_HOSTED_TCP + default "serial" if ENABLE_HOSTED_SERIAL + + if ENABLE_HOSTED="tcp" + config HOSTED_SERVER_IP + depends on ENABLE_HOSTED="tcp" + string "Remote RPC server IP address" + default "192.168.13.1" + endif + + if ENABLE_HOSTED="serial" + config HOSTED_COM_PORT + string "COM port connected to remote RPC server" + default "$(COM_PORT)" + + config HOSTED_COM_SPEED + int "Baud rate for hosted COM port" + default 115200 + endif + +endmenu diff --git a/Sming/Components/Hosted/samples/serial/README.rst b/Sming/Components/Hosted/samples/serial/README.rst index d1635d59fb..0bf048eb84 100644 --- a/Sming/Components/Hosted/samples/serial/README.rst +++ b/Sming/Components/Hosted/samples/serial/README.rst @@ -1,4 +1,4 @@ -Hosted RCP Server over Serial +Hosted RPC Server over Serial ============================= Overview diff --git a/Sming/Components/Network/Kconfig b/Sming/Components/Network/Kconfig new file mode 100644 index 0000000000..2d9d0e353a --- /dev/null +++ b/Sming/Components/Network/Kconfig @@ -0,0 +1,53 @@ +menu "Network" + depends on !DISABLE_NETWORK + + config DISABLE_WIFI + bool "Build without WiFi support" + default n + help + Keeps the core Network library but excludes WiFi code. + Applications using ethernet can use this to reduce code size. + + if !DISABLE_WIFI + config WIFI_SSID + string "Default WiFi SSID" + depends on !DISABLE_WIFI + + config WIFI_PWD + string "Default WiFi Passphrase" + depends on !DISABLE_WIFI + + if SMING_ARCH=Esp8266 + config ENABLE_WPS + bool "Enable WiFi Protected Setup (WPS)" + help + WPS is not enabled by default to preserve resources, and because + there may be security implications which you should consider carefully. + + config ENABLE_SMART_CONFIG + bool "Enable WiFi Smart Configuration API" + help + See :sample:`Basic_SmartConfig` sample application. + endif + endif + + config HTTP_SERVER_EXPOSE_NAME + bool "Add \"HttpServer/Sming\" to the SERVER field in response headers" + default y + help + If disabled, the SERVER field is omitted from all responses. + + config HTTP_SERVER_EXPOSE_VERSION + bool "Include Sming version in HTTP server headers" + depends on HTTP_SERVER_EXPOSE_NAME + help + Adds the current Sming build version to the SERVER field in response headers. + For example, "Sming/4.0.0-rc2". + + config ENABLE_CUSTOM_LWIP + int "LWIP version (0 for SDK, 1 or 2)" + range 0 2 + default 1 + depends on SMING_ARCH=Esp8266 + +endmenu diff --git a/Sming/Components/Network/component.mk b/Sming/Components/Network/component.mk index 69d0d4bb02..e0b6847fb9 100644 --- a/Sming/Components/Network/component.mk +++ b/Sming/Components/Network/component.mk @@ -19,6 +19,13 @@ COMPONENT_DEPENDS := \ mqtt-codec \ libyuarel +# WiFi settings may be provide via Environment variables +CONFIG_VARS += WIFI_SSID WIFI_PWD +ifdef WIFI_SSID + APP_CFLAGS += -DWIFI_SSID=\"$(WIFI_SSID)\" + APP_CFLAGS += -DWIFI_PWD=\"$(WIFI_PWD)\" +endif + # => WPS COMPONENT_VARS += ENABLE_WPS ifeq ($(ENABLE_WPS), 1) diff --git a/Sming/Components/esptool/Kconfig b/Sming/Components/esptool/Kconfig new file mode 100644 index 0000000000..882992e93a --- /dev/null +++ b/Sming/Components/esptool/Kconfig @@ -0,0 +1,9 @@ +menu "esptool" + config COM_PORT_ESPTOOL + string "Port to use for flashing device" + default COM_PORT + + config COM_SPEED_ESPTOOL + string "Baud rate to use for flashing device" + default COM_SPEED +endmenu diff --git a/Sming/Components/rboot/Kconfig b/Sming/Components/rboot/Kconfig new file mode 100644 index 0000000000..730b474ff0 --- /dev/null +++ b/Sming/Components/rboot/Kconfig @@ -0,0 +1,22 @@ +menu "rBoot" + depends on SMING_ARCH = "Esp8266" + + config RBOOT_RTC_ENABLED + bool "Enable extended boot information via RTC memory" + + choice + prompt "GPIO boot behaviour" + default RBOOT_GPIO_DISABLED + config RBOOT_GPIO_DISABLED + bool "Disable GPIO features" + config RBOOT_GPIO_ENABLED + bool "Enable GPIO boot slot 2" + help + rBoot supports booting into a third slot upon explicit user request, + e.g. by pressing a button during reset/power up. + This is especially useful for implementing some sort of recovery mechanism. + config RBOOT_GPIO_SKIP_ENABLED + bool "Use GPIO to skip between boot roms" + endchoice + +endmenu diff --git a/Sming/Components/ssl/Kconfig b/Sming/Components/ssl/Kconfig new file mode 100644 index 0000000000..ea73643ca5 --- /dev/null +++ b/Sming/Components/ssl/Kconfig @@ -0,0 +1,28 @@ +menu "SSL" + depends on !DISABLE_NETWORK + + choice + default SELECT_SSL_NONE + prompt "SSL adapter" + config SELECT_SSL_NONE + bool "None" + config SELECT_SSL_DEFAULT + bool "Default (Axtls)" + config SELECT_SSL_AXTLS + bool "AXTLS" + config SELECT_SSL_BEARSSL + bool "Bear SSL" + endchoice + + config ENABLE_SSL + string + default "0" if SELECT_SSL_NONE + default "1" if SELECT_SSL_DEFAULT + default "Axtls" if SELECT_SSL_AXTLS + default "Bearssl" if SELECT_SSL_BEARSSL + + config SSL_DEBUG + depends on ENABLE_SSL != "0" + bool "Enable SSL debug" + +endmenu diff --git a/Sming/Components/terminal/Kconfig b/Sming/Components/terminal/Kconfig new file mode 100644 index 0000000000..59906f3609 --- /dev/null +++ b/Sming/Components/terminal/Kconfig @@ -0,0 +1,33 @@ +menu "Terminal" + + config COM_PORT + string "Default port for device communications" + help + Default value depends on the development platform being used. + default "/dev/cuaU0" if ("$UNAME" = "FreeBSD") + default "/dev/tty.usbserial" if "$UNAME" = "MacOS" + default "/dev/ttyUSB0" if "$UNAME" = "Linux" + default "COM3" if "$UNAME" = "Windows" + + config COM_SPEED_SERIAL + int "Default baud rate to use for serial communication within application" + default COM_SPEED + + config COM_OPTS + string "Additional options to pass to the terminal" + default "--raw --encoding ascii --rts 0 --dtr 0" + + config TERMINAL + string "Command line to use when running `make terminal`" + help + Redefine if you want to use a different terminal application + default "powershell.exe -Command \"python -m serial.tools.miniterm $(COM_OPTS) $(COM_PORT) $(COM_SPEED_SERIAL)\"" if $WSL_ROOT + default "$(PYTHON) -m serial.tools.miniterm $(COM_OPTS) $(COM_PORT) $(COM_SPEED_SERIAL)" if !$WSL_ROOT + + config KILL_TERM + string "Command line to use to kill the running terminal process" + help + If the terminal never runs in the background and the warnings annoy you, just clear it + default "pkill -9 -f \"$(COM_PORT) $(COM_SPEED_SERIAL)\" || exit 0" + +endmenu diff --git a/Sming/Kconfig b/Sming/Kconfig new file mode 100644 index 0000000000..febd794328 --- /dev/null +++ b/Sming/Kconfig @@ -0,0 +1,113 @@ +# +# For a description of the syntax of this configuration file, +# see kconfig/kconfig-language.txt. +# +mainmenu "Sming Framework Configuration: $(SMING_ARCH) $(ESP_VARIANT)" + + config SMING_ARCH + string + option env="SMING_ARCH" + + menu "Sming" + choice + default SELECT_LOCALE_EN_GB + prompt "Date/Time country code" + help + Sming can format dates/time values based on a country code identified by this value. + This is provided as a #define symbol for your application to use. + See :source:`Sming/Core/SmingLocale.h` for further details. + config SELECT_LOCALE_EN_US + bool "English (US)" + config SELECT_LOCALE_EN_GB + bool "English (UK)" + config SELECT_LOCALE_EN_AU + bool "English (Australia)" + config SELECT_LOCALE_FR_FR + bool "Français" + config SELECT_LOCALE_DE_DE + bool "Deutsch" + endchoice + + config LOCALE + int + default 1 if SELECT_LOCALE_EN_US + default 33 if SELECT_LOCALE_FR_FR + default 44 if SELECT_LOCALE_EN_GB + default 49 if SELECT_LOCALE_DE_DE + default 61 if SELECT_LOCALE_EN_AU + + config COM_SPEED + int "Default baud rate for serial port" + default 115200 + help + This will recompile your application to use the revised baud rate. + Note that this will change the default speed used for both flashing and serial comms. + + config ENABLE_CMD_EXECUTOR + bool "Enable command executor functionality" + default y + help + This facilty requires documenting! + + config TASK_QUEUE_LENGTH + int "Length of task queue" + default 10 + depends on SMING_ARCH="Esp8266" || SMING_ARCH="Host" + + config STRING_OBJECT_SIZE + int "Size of a Wiring String object" + default 12 + help + Change this to increase space for Small String Optimisation (SSO) + + config DISABLE_NETWORK + bool "Build without networking support" + default n + select DISABLE_WIFI if DISABLE_NETWORK + help + Applications which do not require networking can set this flag to avoid building + or linking the core Network library. + + This will reduce build times, application size and RAM usage. + Builds will not succeeded if network code has been inadvertently included. + endmenu + + menu "Debug" + choice + default SELECT_DEBUG_INFO + prompt "Detail level for debug messages" + config SELECT_DEBUG_ERROR + bool "0 Errors only" + config SELECT_DEBUG_WARN + bool "1 Errors and warnings" + config SELECT_DEBUG_INFO + bool "2 Errors, warnings and Information" + config SELECT_DEBUG_DEBUG + bool "3 All debug messages" + endchoice + + config DEBUG_VERBOSE_LEVEL + int + default 0 if SELECT_DEBUG_ERROR + default 1 if SELECT_DEBUG_WARN + default 2 if SELECT_DEBUG_INFO + default 3 if SELECT_DEBUG_DEBUG + + config DEBUG_PRINT_FILENAME_AND_LINE + bool "Include the filename and line number in every line of debug output." + default n + help + This will require extra space on flash + + config ENABLE_GDB + bool "Compile with support for serial debugging using GDB" + + config ENABLE_SPI_DEBUG + bool "Enable SPI debug output" + + config ENABLE_TASK_COUNT + bool "Enable use of System task counting to check for queue overflows" + depends on SMING_ARCH="Esp8266" + endmenu + + source "$KCONFIG_COMPONENTS" diff --git a/Sming/Libraries/OtaUpgrade/Kconfig b/Sming/Libraries/OtaUpgrade/Kconfig new file mode 100644 index 0000000000..3dd5c1893c --- /dev/null +++ b/Sming/Libraries/OtaUpgrade/Kconfig @@ -0,0 +1,41 @@ +menu "OTA Upgrade" + + config ENABLE_OTA_SIGNING + bool "Protect upgrade files using digital signature" + default y + help + If enabled (highly recommended), OTA upgrade files are protected against unauthorized modification by a digital signature. + + You may disable signing in order to save some program memory if your communication channel already establishes a + comparable level of trust, e.g. TLS with a pinned certificate. + + config OTA_ENABLE_ENCRYPTION + bool "Encrypt upgrade files" + help + This option helps to protect any confidential data embedded in your firmware, such as WiFi credentials, server certificates, etc. + + config OTA_KEY + string "Path to secret encryption/signing key" + default "ota.key" + + config ENABLE_OTA_DOWNGRADE + bool "Enable downgrade" + help + By default, BasicStream refuses to downgrade to an older firmware version to prevent an attacker from restoring already patched security vulnerabilities. + + Downgrade protection must be combined with encryption or signing to be effective. + + config OTA_UPLOAD_URL + string "URL used by the `make ota-upload` command" + + + config OTA_UPLOAD_NAME + string "Field name for upgrade file in HTTP POST request" + default "firmware" + help + This relates to the `name` attribute of the HTML input element + used with the `make ota-upload` command: + + + +endmenu diff --git a/Sming/Libraries/OtaUpgradeMqtt/Kconfig b/Sming/Libraries/OtaUpgradeMqtt/Kconfig new file mode 100644 index 0000000000..e9eaf979c4 --- /dev/null +++ b/Sming/Libraries/OtaUpgradeMqtt/Kconfig @@ -0,0 +1,20 @@ +menu "OTA Upgrade MQTT" + + config ENABLE_OTA_VARINT_VERSION + bool "Enable 'varint' encoding for patch version" + default y + help + If enabled, the OTA upgrade mechanism and application will use a `varint `_ + encoding for the patch version. Thus allowing unlimited number of patch versions. Useful for enumerating unstable/nightly releases. + A bit more difficult to read and write but allows for unlimited versions. + + If set to 0 the OTA upgrade mechanism and application will use one byte for the patch version which will limit it to 256 possible patch versions. + Useful for enumarating stable releases. Easier to write and read but limited to 256 versions only. + + config ENABLE_OTA_ADVANCED + bool "Support firmware signing/encryption" + help + Enabling this option allows the library to work with OtaUpgradeStream which supports signature and encryption of the firmware data itself. + In the application the AdvancedPayloadParser can be used to do the MQTT message handling. + +endmenu diff --git a/Sming/Libraries/Spiffs/Kconfig b/Sming/Libraries/Spiffs/Kconfig new file mode 100644 index 0000000000..7aa6d949fe --- /dev/null +++ b/Sming/Libraries/Spiffs/Kconfig @@ -0,0 +1,30 @@ +menu "SPIFFS" + config SPIFF_FILEDESC_COUNT + int "Number of file descriptors allocated" + default 7 + help + This sets the maximum number of files which may be opened at once. + + config SPIFFS_OBJ_META_LEN + int "Maximum size of file metadata" + default 16 + help + Maximum size of metadata which SPIFFS stores in each file index header (after the filename). + If this value is changed, existing SPIFFS images will not be readable. + + The default value is the minimum necessary to support IFS extended file attribute information. + + The first 16 bytes are used for system attributes (e.g. modified time), so setting this to, say, 64 + leaves 48 bytes for user metadata. Each attribute has a 2-byte header (tag + size) so a single user + attribute can be stored of up to 46 bytes, or multiple tags up to this limit. + + Note: LittleFS provides better support for user metadata. + + config SPIFF_FILES + string "Path to source files for default spiffs volume" + default "files" + help + The SPIFFS image is built using files from this directory, which must exist or the build will fail. + If you set this to an empty value, then an empty filesystem will be created. + +endmenu diff --git a/Sming/Libraries/Spiffs/README.rst b/Sming/Libraries/Spiffs/README.rst index 4e50a7c99d..ef7ed673a1 100644 --- a/Sming/Libraries/Spiffs/README.rst +++ b/Sming/Libraries/Spiffs/README.rst @@ -18,8 +18,6 @@ A single SPIFFS partition is defined using :envvar:`HWCONFIG` ``=spiffs``, which Size (in bytes) of the SPIFFS area in Flash memory. To change this, edit the :ref:`hardware_config`. - .. envvar:: SPIFF_FILES - .. envvar:: SPIFF_FILES default: ``files`` diff --git a/Sming/Libraries/Spiffs/component.mk b/Sming/Libraries/Spiffs/component.mk index 946794670c..a482d6ff1f 100644 --- a/Sming/Libraries/Spiffs/component.mk +++ b/Sming/Libraries/Spiffs/component.mk @@ -19,7 +19,7 @@ SPIFF_BIN_OUT := $(FW_BASE)/$(SPIFF_BIN).bin COMPONENT_RELINK_VARS += SPIFF_FILEDESC_COUNT SPIFF_FILEDESC_COUNT ?= 7 -COMPONENT_CFLAGS += -DSPIFF_FILEDESC_COUNT=$(SPIFF_FILEDESC_COUNT) +COMPONENT_CXXFLAGS += -DSPIFF_FILEDESC_COUNT=$(SPIFF_FILEDESC_COUNT) COMPONENT_CFLAGS += -Wno-tautological-compare diff --git a/Sming/Libraries/Spiffs/src/FileSystem.cpp b/Sming/Libraries/Spiffs/src/FileSystem.cpp index 710a3adab6..c17b14cfb2 100644 --- a/Sming/Libraries/Spiffs/src/FileSystem.cpp +++ b/Sming/Libraries/Spiffs/src/FileSystem.cpp @@ -409,7 +409,7 @@ SpiffsMetaBuffer* FileSystem::initMetaBuffer(FileHandle file) SpiffsMetaBuffer* FileSystem::getMetaBuffer(FileHandle file) { unsigned off = SPIFFS_FH_UNOFFS(handle(), file) - 1; - if(off >= FFS_MAX_FILEDESC) { + if(off >= SPIFF_FILEDESC_COUNT) { debug_e("getMetaBuffer(%d) - bad file", file); return nullptr; } diff --git a/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileSystem.h b/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileSystem.h index be59da5e25..ed9279218e 100644 --- a/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileSystem.h +++ b/Sming/Libraries/Spiffs/src/include/IFS/SPIFFS/FileSystem.h @@ -48,11 +48,6 @@ extern "C" { #include "../../../../spiffs/src/spiffs_nucleus.h" } -/* - * Maxmimum number of open files - */ -#define FFS_MAX_FILEDESC 8 - namespace IFS { namespace SPIFFS @@ -139,10 +134,10 @@ class FileSystem : public IFileSystem Storage::Partition partition; IProfiler* profiler{nullptr}; - SpiffsMetaBuffer metaCache[FFS_MAX_FILEDESC]; + SpiffsMetaBuffer metaCache[SPIFF_FILEDESC_COUNT]; spiffs fs; uint16_t workBuffer[LOG_PAGE_SIZE]; - spiffs_fd fileDescriptors[FFS_MAX_FILEDESC]; + spiffs_fd fileDescriptors[SPIFF_FILEDESC_COUNT]; uint8_t cache[CACHE_SIZE]; }; diff --git a/Sming/Platform/System.cpp b/Sming/Platform/System.cpp index 52472fa658..f661d942fd 100644 --- a/Sming/Platform/System.cpp +++ b/Sming/Platform/System.cpp @@ -11,20 +11,25 @@ #include "Platform/System.h" #include "Timer.h" -#ifndef TASK_QUEUE_LENGTH +SystemClass System; +SystemState SystemClass::state = eSS_None; + +#ifdef ARCH_ESP32 +#undef TASK_QUEUE_LENGTH +#define TASK_QUEUE_LENGTH 0 +#define taskQueue nullptr +#else +#ifdef TASK_QUEUE_LENGTH +static_assert(TASK_QUEUE_LENGTH >= 8, "Task queue too small"); +#else /** @brief default number of tasks in global queue * @note tasks are usually short-lived and executed very promptly, so a large queue is * normally un-necessry. If queue overrun is suspected, check `SystemClass::getMaxTaskCount()`. */ #define TASK_QUEUE_LENGTH 10 #endif - -SystemClass System; - -SystemState SystemClass::state = eSS_None; os_event_t SystemClass::taskQueue[TASK_QUEUE_LENGTH]; - -static_assert(TASK_QUEUE_LENGTH >= 8, "Task queue too small"); +#endif #ifdef ENABLE_TASK_COUNT volatile uint8_t SystemClass::taskCount; diff --git a/Sming/build.mk b/Sming/build.mk index c3b8958cca..4c06fdabd9 100644 --- a/Sming/build.mk +++ b/Sming/build.mk @@ -31,6 +31,8 @@ else endif export SMING_RELEASE +SMING_TOOLS := $(realpath $(SMING_HOME)/../Tools) + # Detect OS and build environment TOOL_EXT := DEBUG_VARS += UNAME diff --git a/Sming/component.mk b/Sming/component.mk index b24c7014e1..1ea550f99a 100644 --- a/Sming/component.mk +++ b/Sming/component.mk @@ -62,13 +62,6 @@ ifeq ($(DISABLE_WIFI),1) GLOBAL_CFLAGS += -DDISABLE_WIFI=1 endif -# WiFi settings may be provide via Environment variables -CONFIG_VARS += WIFI_SSID WIFI_PWD -ifdef WIFI_SSID - APP_CFLAGS += -DWIFI_SSID=\"$(WIFI_SSID)\" - APP_CFLAGS += -DWIFI_PWD=\"$(WIFI_PWD)\" -endif - # => LOCALE COMPONENT_VARS += LOCALE ifdef LOCALE diff --git a/Sming/project.mk b/Sming/project.mk index 828ef31c58..37a393d241 100644 --- a/Sming/project.mk +++ b/Sming/project.mk @@ -536,7 +536,7 @@ export HOST_PARAMETERS .PHONY: ide-vscode-update ide-vscode-update: $(Q) SMING_HOME=$(SMING_HOME) OUT_BASE=$(OUT_BASE) \ - $(PYTHON) $(SMING_HOME)/../Tools/vscode/setup.py + $(PYTHON) $(SMING_TOOLS)/vscode/setup.py ##@Testing @@ -671,3 +671,28 @@ CACHED_VAR_NAMES := $(sort $(CACHED_VAR_NAMES) $(CONFIG_VARS) $(RELINK_VARS) $(C $(eval $(call WriteConfigCache,$(CONFIG_CACHE_FILE),CACHED_VAR_NAMES)) $(eval $(call WriteCacheValues,$(CONFIG_DEBUG_FILE),$(sort $(DEBUG_VARS)))) endif + + +##@Configuration + +export UNAME +# List of all Kconfig files +export KCONFIG_FILES = $(wildcard $(foreach c,$(filter-out Sming,$(COMPONENTS)),$(CMP_$c_PATH)/Kconfig)) +# File containing list of component Kconfig file paths (created by cfgtool) +export KCONFIG_COMPONENTS = $(OUT_BASE)/kconfig.in + +KCONFIG := $(SMING_HOME)/Kconfig +KCONFIG_CONFIG := $(OUT_BASE)/kconfig.config + +KCONFIG_ENV := \ + CONFIG_= \ + KCONFIG=$(KCONFIG) \ + KCONFIG_CONFIG=$(KCONFIG_CONFIG) + +CFGTOOL_CMDLINE = $(KCONFIG_ENV) $(PYTHON) $(SMING_TOOLS)/cfgtool.py $(CONFIG_CACHE_FILE) + +.PHONY: menuconfig +menuconfig: ##Run option editor + $(Q) $(CFGTOOL_CMDLINE) --to-kconfig + $(Q) $(KCONFIG_ENV) $(PYTHON) -m menuconfig $(SMING_HOME)/Kconfig + $(Q) $(CFGTOOL_CMDLINE) --from-kconfig diff --git a/Tools/cfgtool.py b/Tools/cfgtool.py new file mode 100644 index 0000000000..a40a0bc7e6 --- /dev/null +++ b/Tools/cfgtool.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +# +# Sming configuration management tool +# +# Used by build system to enable compatibility with Kconfig +# +# + +import argparse, configparser, os, sys, platform, kconfiglib + + +def load_config_vars(filename): + parser = configparser.ConfigParser() + parser.optionxform = str # preserve case + with open(filename) as f: + data = "[config]\n" + f.read() + data = data.replace(' := ', ' = ') + parser.read_string(data) + return parser['config'] + + +def set_kconfig_value(symbol, v): + """Convert values from Sming format to Kconfig format. + + Hidden variables are used to store choice selections and cannot be set directly. + Instead, locate the corresponding choice variable and set that. + """ + if symbol.visibility == 2: + if symbol.type is kconfiglib.BOOL: + v = 'y' if v == '1' else 'n' + elif symbol.type == kconfiglib._T_INT: + v = 0 if v == '' else v + symbol.set_value(v) + else: + for sym, cond in symbol.defaults: + if isinstance(cond, tuple): + cond = cond[1] + if not isinstance(cond, kconfiglib.Symbol): + continue + if v == sym.name and cond.type is kconfiglib.BOOL: + cond.set_value('y') + break + + +def fixpath(path): + """Paths in Windows can get a little weird""" + if len(path) > 2 and path[1] != ':' and platform.system() == 'Windows' and path[2] == '/': + return path[1] + ':' + path[2:] + return path + + +def createComponentsFile(): + fileList = os.environ['KCONFIG_FILES'].split() + compFile = os.environ['KCONFIG_COMPONENTS'] + with open(compFile, "w") as f: + for s in fileList: + f.write('source "%s"\n' % fixpath(s)) + + +def main(): + parser = argparse.ArgumentParser(description='Sming configuration management tool') + parser.add_argument('--to-kconfig', help="Convert Sming configuration to Kconfig format", action='store_true') + parser.add_argument('--from-kconfig', help="Convert Kconfig configration to Sming format", action='store_true') + parser.add_argument('config_file', help='Source configuration file') + + args = parser.parse_args() + + if args.to_kconfig: + createComponentsFile() + + conf = kconfiglib.Kconfig(os.environ['KCONFIG']) + src = load_config_vars(args.config_file) + for k, v in src.items(): + c = conf.syms.get(k) + if c: + set_kconfig_value(c, v) + conf.write_config(os.environ['KCONFIG_CONFIG']) + elif args.from_kconfig: + conf = kconfiglib.Kconfig(os.environ['KCONFIG']) + conf.load_config(os.environ['KCONFIG_CONFIG']) + dst = load_config_vars(args.config_file) + varnames = set(dst.pop('CACHED_VAR_NAMES').split()) + + for k, sym in conf.syms.items(): + if sym.type is kconfiglib.UNKNOWN: + continue + if sym.env_var is not None: + continue + v = str(sym.user_value) if sym.user_value else "" + if sym.type is kconfiglib.BOOL: + v = "1" if v in ['y', '2'] else "0" + dst[sym.name] = v + varnames.add(sym.name) + with open(args.config_file, "w") as f: + for k, v in dst.items(): + f.write("%s=%s\n" % (k, v)) + f.write("\n") + varnames = list(varnames) + varnames.sort() + f.write("CACHED_VAR_NAMES := " + " ".join(varnames)) + else: + raise RuntimeError("No command specified") + + +if __name__ == '__main__': + try: + main() + except Exception as e: + print("** ERROR! %s" % e, file=sys.stderr) + sys.exit(2) diff --git a/Tools/requirements.txt b/Tools/requirements.txt index ee96c8f8e1..bc47cf1a0c 100644 --- a/Tools/requirements.txt +++ b/Tools/requirements.txt @@ -1,2 +1,3 @@ pyserial jsonschema +kconfiglib diff --git a/samples/Basic_IFS/Kconfig b/samples/Basic_IFS/Kconfig new file mode 100644 index 0000000000..9cf1c781ba --- /dev/null +++ b/samples/Basic_IFS/Kconfig @@ -0,0 +1,9 @@ +menu "Basic IFS sample" + config ENABLE_FLASHSTRING_IMAGE + bool "Store filesystem image in a FlashString object instead of partition" + + config HWCONFIG + string "Project hardware configuration" + default "basic_ifs_$(SMING_ARCH)" if ENABLE_FLASHSTRING_IMAGE + default "spiffs" if !ENABLE_FLASHSTRING_IMAGE +endmenu From 6b1cc4ebd07893c12994b666f8018aae1961f984 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 30 Sep 2021 09:20:34 +0100 Subject: [PATCH 065/130] Fix Hosted operation for Windows (#2378) This PR updates the Hosted Component so it builds and runs for Windows and also Esp32 architecture. I've done some basic testing with Hosted running on an ESP32-S2 target, which pretty much worked straight away (apart from `pulseIn` signature). However, as I use WSL if using a serial interface the Host application must be run in a Windows build. That needed a few fixes. This also seems like a good time to get the Kconfig settings for this Component updated! * Move `simpleRPC` into Components (Hosted is a Component so should not depend on a Library) * Fix compilation problems for Windows * Update environment variable handling & Kconfig settings * Fix `pulseIn` signature for Esp32 --- .gitmodules | 2 +- Sming/Arch/Esp32/Core/Digital.cpp | 2 +- Sming/Arch/Host/Components/SerialLib/SerialLib.h | 3 +++ Sming/Components/Hosted-Lib/README.rst | 8 ++++++++ Sming/Components/Hosted/README.rst | 2 +- Sming/Components/Hosted/component.mk | 15 +++++---------- Sming/Components/Hosted/include/Hosted/Client.h | 2 +- Sming/Components/Hosted/include/Hosted/Serial.h | 5 +++-- .../Components/Hosted/init/serial/InitClient.cpp | 13 +++++-------- Sming/Components/Hosted/samples/tcp/Kconfig | 7 +++++++ .../Hosted/samples/tcp/app/application.cpp | 4 ++-- Sming/Components/Hosted/samples/tcp/component.mk | 2 +- .../simpleRPC/component.mk | 0 .../simpleRPC/include/simpleRPC/.cs | 0 .../simpleRPC/include/simpleRPC/parser.h | 0 Sming/Components/simpleRPC/simpleRPC | 1 + .../simpleRPC/simpleRPC.patch | 13 +++++++++++++ Sming/{Libraries => Components}/simpleRPC/src/.cs | 0 .../simpleRPC/src/parser.cpp | 0 Sming/Libraries/simpleRPC/simpleRPC | 1 - tests/HostTests/Arch/Host/Hosted.cpp | 2 +- 21 files changed, 53 insertions(+), 29 deletions(-) create mode 100644 Sming/Components/Hosted-Lib/README.rst create mode 100644 Sming/Components/Hosted/samples/tcp/Kconfig rename Sming/{Libraries => Components}/simpleRPC/component.mk (100%) rename Sming/{Libraries => Components}/simpleRPC/include/simpleRPC/.cs (100%) rename Sming/{Libraries => Components}/simpleRPC/include/simpleRPC/parser.h (100%) create mode 160000 Sming/Components/simpleRPC/simpleRPC rename Sming/{Libraries => Components}/simpleRPC/simpleRPC.patch (85%) rename Sming/{Libraries => Components}/simpleRPC/src/.cs (100%) rename Sming/{Libraries => Components}/simpleRPC/src/parser.cpp (100%) delete mode 160000 Sming/Libraries/simpleRPC/simpleRPC diff --git a/.gitmodules b/.gitmodules index f0fd7431a8..99b5694b90 100644 --- a/.gitmodules +++ b/.gitmodules @@ -268,7 +268,7 @@ url = https://github.com/mikee47/SignalGenerator ignore = dirty [submodule "Libraries.simpleRPC"] - path = Sming/Libraries/simpleRPC/simpleRPC + path = Sming/Components/simpleRPC/simpleRPC url = https://github.com/jfjlaros/simpleRPC.git ignore = dirty [submodule "Libraries.SmingTest"] diff --git a/Sming/Arch/Esp32/Core/Digital.cpp b/Sming/Arch/Esp32/Core/Digital.cpp index 6469e6707c..73ad7e190b 100644 --- a/Sming/Arch/Esp32/Core/Digital.cpp +++ b/Sming/Arch/Esp32/Core/Digital.cpp @@ -127,7 +127,7 @@ void noPullup(uint16_t pin) } \ } -unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout) +unsigned long pulseIn(uint16_t pin, uint8_t state, unsigned long timeout) { const uint32_t max_timeout_us = clockCyclesToMicroseconds(UINT_MAX); if(timeout > max_timeout_us) { diff --git a/Sming/Arch/Host/Components/SerialLib/SerialLib.h b/Sming/Arch/Host/Components/SerialLib/SerialLib.h index 6f57aed795..258b6697b5 100644 --- a/Sming/Arch/Host/Components/SerialLib/SerialLib.h +++ b/Sming/Arch/Host/Components/SerialLib/SerialLib.h @@ -1,5 +1,8 @@ #pragma once +#include +#undef interface + #include "seriallib/lib/serialib.h" class SerialDevice : public serialib diff --git a/Sming/Components/Hosted-Lib/README.rst b/Sming/Components/Hosted-Lib/README.rst new file mode 100644 index 0000000000..04fd3c39b3 --- /dev/null +++ b/Sming/Components/Hosted-Lib/README.rst @@ -0,0 +1,8 @@ +Hosted-Lib +========== + +This library provides replacement implementations for low-level operations which may then be +accessed via the selected RPC interface. + +See :component:Hosted for further details. + diff --git a/Sming/Components/Hosted/README.rst b/Sming/Components/Hosted/README.rst index 66f47eef3b..0b1e02ac6b 100644 --- a/Sming/Components/Hosted/README.rst +++ b/Sming/Components/Hosted/README.rst @@ -59,7 +59,7 @@ Configuration .. envvar:: HOSTED_COM_PORT - Default: /dev/ttyUSB0 or the value of the environment variable COM_PORT if defined + Default: :envvar:`COM_PORT` Used only when ENABLE_HOSTED=serial is specified. Specifies which local communication port should be used to connect to the remote RPC server. diff --git a/Sming/Components/Hosted/component.mk b/Sming/Components/Hosted/component.mk index 3ae054f19c..32c823d456 100644 --- a/Sming/Components/Hosted/component.mk +++ b/Sming/Components/Hosted/component.mk @@ -5,9 +5,7 @@ COMPONENT_DEPENDS := simpleRPC # Architecture of the device where the hosted service will be flashed HOSTED_ARCH ?= Esp8266 -COMPONENT_RELINK_VARS += ENABLE_HOSTED COMPONENT_VARS := ENABLE_HOSTED - ENABLE_HOSTED ?= ifneq ($(ENABLE_HOSTED),) @@ -16,16 +14,13 @@ ifneq ($(ENABLE_HOSTED),) COMPONENT_DEPENDS += SerialLib endif -COMPONENT_VARS += HOSTED_SERVER_IP +COMPONENT_RELINK_VARS += HOSTED_SERVER_IP -COMPONENT_VARS += HOSTED_COM_PORT -HOSTED_COM_PORT := "/dev/ttyUSB0" -ifneq ($(COM_PORT),) - HOSTED_COM_PORT:=$(COM_PORT) -endif +COMPONENT_RELINK_VARS += HOSTED_COM_PORT +HOSTED_COM_PORT ?= $(COM_PORT) -COMPONENT_VARS += HOSTED_COM_SPEED -HOSTED_COM_SPEED := 115200 +COMPONENT_RELINK_VARS += HOSTED_COM_SPEED +HOSTED_COM_SPEED ?= 115200 COMPONENT_CFLAGS = -DHOSTED_SERVER_IP=$(HOSTED_SERVER_IP) -DHOSTED_COM_PORT="\"$(HOSTED_COM_PORT)"\" -DHOSTED_COM_SPEED=$(HOSTED_COM_SPEED) COMPONENT_CXXFLAGS := $(COMPONENT_CFLAGS) diff --git a/Sming/Components/Hosted/include/Hosted/Client.h b/Sming/Components/Hosted/include/Hosted/Client.h index d736e82c28..80f3e97dbb 100644 --- a/Sming/Components/Hosted/include/Hosted/Client.h +++ b/Sming/Components/Hosted/include/Hosted/Client.h @@ -127,7 +127,7 @@ class Client } if(result == ParserResult::error) { - debug_e("Invalid header"); + host_debug_e("Invalid header"); return false; } } while(true); diff --git a/Sming/Components/Hosted/include/Hosted/Serial.h b/Sming/Components/Hosted/include/Hosted/Serial.h index 8bdfc94b10..3f65340e8b 100644 --- a/Sming/Components/Hosted/include/Hosted/Serial.h +++ b/Sming/Components/Hosted/include/Hosted/Serial.h @@ -17,9 +17,10 @@ #error "Hosted::Serial can be used only on the Host architecture!" #endif +#include #include #include -#include +#include namespace Hosted { @@ -55,7 +56,7 @@ class Serial : public Stream return true; } - debug_w("Hosted::Serial:begin error: %d", result); + host_debug_w("Hosted::Serial:begin error: %d", result); return false; } diff --git a/Sming/Components/Hosted/init/serial/InitClient.cpp b/Sming/Components/Hosted/init/serial/InitClient.cpp index 9a9d22cf19..3b4c980548 100644 --- a/Sming/Components/Hosted/init/serial/InitClient.cpp +++ b/Sming/Components/Hosted/init/serial/InitClient.cpp @@ -11,9 +11,8 @@ * ****/ -#include -#include #include +#include #ifndef HOSTED_COM_PORT #define HOSTED_COM_PORT "/dev/ttyUSB0" @@ -36,14 +35,12 @@ Hosted::Serial hostedSerial(HOSTED_COM_PORT); void __wrap_host_init() { - Serial.begin(115200); - Serial.printf("Connecting to: %s ...\n", HOSTED_COM_PORT); + host_printf("Connecting to: %s ...\r\n", HOSTED_COM_PORT); bool serialReady = false; - do { - serialReady = hostedSerial.begin(HOSTED_COM_SPEED); - usleep(200); - } while(!serialReady); + while(!(serialReady = hostedSerial.begin(HOSTED_COM_SPEED))) { + msleep(50); + } hostedClient = new Hosted::Client(hostedSerial); hostedClient->getRemoteCommands(); diff --git a/Sming/Components/Hosted/samples/tcp/Kconfig b/Sming/Components/Hosted/samples/tcp/Kconfig new file mode 100644 index 0000000000..e71cf8c6fc --- /dev/null +++ b/Sming/Components/Hosted/samples/tcp/Kconfig @@ -0,0 +1,7 @@ +menu "Hosted TCP sample" + config CONNECT_TO_WIFI + bool "Connect to WiFi access point" + help + Make sure to provide WIFI_SSID and WIFI_PWD values + +endmenu diff --git a/Sming/Components/Hosted/samples/tcp/app/application.cpp b/Sming/Components/Hosted/samples/tcp/app/application.cpp index 3702dc2580..f49967295b 100644 --- a/Sming/Components/Hosted/samples/tcp/app/application.cpp +++ b/Sming/Components/Hosted/samples/tcp/app/application.cpp @@ -58,7 +58,7 @@ void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) return true; }); - Serial.printf("Running RCP server on: %s:%u", ip.toString().c_str(), port); + Serial.printf("Running RPC server on: %s:%u", ip.toString().c_str(), port); } } // namespace @@ -75,7 +75,7 @@ void init() WifiEvents.onStationGotIP(connectOk); #else WifiAccessPoint.enable(true); - WifiAccessPoint.config(_F("RCP Server"), nullptr, AUTH_OPEN); + WifiAccessPoint.config(_F("RPC Server"), nullptr, AUTH_OPEN); connectOk(WifiAccessPoint.getIP(), WifiAccessPoint.getNetworkMask(), WifiAccessPoint.getNetworkGateway()); #endif } diff --git a/Sming/Components/Hosted/samples/tcp/component.mk b/Sming/Components/Hosted/samples/tcp/component.mk index a3b186f112..8883932d4c 100644 --- a/Sming/Components/Hosted/samples/tcp/component.mk +++ b/Sming/Components/Hosted/samples/tcp/component.mk @@ -4,7 +4,7 @@ ENABLE_HOSTED := # If set the application should connect to a WIFI access point # otherwise it will set its own access point -COMPONENT_VARS := CONNECT_TO_WIFI +COMPONENT_RELINK_VARS := CONNECT_TO_WIFI CONNECT_TO_WIFI ?= 0 APP_CFLAGS = -DCONNECT_TO_WIFI=$(CONNECT_TO_WIFI) diff --git a/Sming/Libraries/simpleRPC/component.mk b/Sming/Components/simpleRPC/component.mk similarity index 100% rename from Sming/Libraries/simpleRPC/component.mk rename to Sming/Components/simpleRPC/component.mk diff --git a/Sming/Libraries/simpleRPC/include/simpleRPC/.cs b/Sming/Components/simpleRPC/include/simpleRPC/.cs similarity index 100% rename from Sming/Libraries/simpleRPC/include/simpleRPC/.cs rename to Sming/Components/simpleRPC/include/simpleRPC/.cs diff --git a/Sming/Libraries/simpleRPC/include/simpleRPC/parser.h b/Sming/Components/simpleRPC/include/simpleRPC/parser.h similarity index 100% rename from Sming/Libraries/simpleRPC/include/simpleRPC/parser.h rename to Sming/Components/simpleRPC/include/simpleRPC/parser.h diff --git a/Sming/Components/simpleRPC/simpleRPC b/Sming/Components/simpleRPC/simpleRPC new file mode 160000 index 0000000000..d76e9fa54e --- /dev/null +++ b/Sming/Components/simpleRPC/simpleRPC @@ -0,0 +1 @@ +Subproject commit d76e9fa54ef816519693322c832af8bb9f186fd7 diff --git a/Sming/Libraries/simpleRPC/simpleRPC.patch b/Sming/Components/simpleRPC/simpleRPC.patch similarity index 85% rename from Sming/Libraries/simpleRPC/simpleRPC.patch rename to Sming/Components/simpleRPC/simpleRPC.patch index ba6edc8500..5657d6fc78 100644 --- a/Sming/Libraries/simpleRPC/simpleRPC.patch +++ b/Sming/Components/simpleRPC/simpleRPC.patch @@ -63,3 +63,16 @@ index d51cdd1..abf1e7b 100644 //! \defgroup write +diff --git a/src/defs.h b/src/defs.h +index 8d2ec2a..93edaf5 100644 +--- a/src/defs.h ++++ b/src/defs.h +@@ -1,7 +1,7 @@ + #ifndef SIMPLE_RPC_DEFS_H_ + #define SIMPLE_RPC_DEFS_H_ + +-#include ++#include + + #define _PROTOCOL "simpleRPC" + #define _VERSION "\3\0\0" diff --git a/Sming/Libraries/simpleRPC/src/.cs b/Sming/Components/simpleRPC/src/.cs similarity index 100% rename from Sming/Libraries/simpleRPC/src/.cs rename to Sming/Components/simpleRPC/src/.cs diff --git a/Sming/Libraries/simpleRPC/src/parser.cpp b/Sming/Components/simpleRPC/src/parser.cpp similarity index 100% rename from Sming/Libraries/simpleRPC/src/parser.cpp rename to Sming/Components/simpleRPC/src/parser.cpp diff --git a/Sming/Libraries/simpleRPC/simpleRPC b/Sming/Libraries/simpleRPC/simpleRPC deleted file mode 160000 index 5696f34c42..0000000000 --- a/Sming/Libraries/simpleRPC/simpleRPC +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5696f34c429c4e87721ebdb191b21c2ea0f23791 diff --git a/tests/HostTests/Arch/Host/Hosted.cpp b/tests/HostTests/Arch/Host/Hosted.cpp index 24d48af805..c77f9b54cb 100644 --- a/tests/HostTests/Arch/Host/Hosted.cpp +++ b/tests/HostTests/Arch/Host/Hosted.cpp @@ -90,7 +90,7 @@ class HostedTest : public TestGroup return true; }); - // RCP Client + // RPC Client client.connect(WifiStation.getIP(), 4031); Hosted::Transport::TcpClientStream stream(client, 1024); From d8b8d7ca5bafbcaaed7e8f5d8e6b421471a04ff1 Mon Sep 17 00:00:00 2001 From: slaff Date: Mon, 4 Oct 2021 10:13:19 +0200 Subject: [PATCH 066/130] Removed deprecated items ... (#2379) Undeprecated HttpRequest::getQueryParameter. And removed a lot of old deprecated code. See the full list below: - Removed WebsocketClient::disconnect (). Use WebsocketClient::close() instead. - Removed TimerDelegateStdFunction. Use TimerDelegate instead. - Removed class URL. Use class Url instead. - Removed TemplateVariables Use TemplateStream::Variables instead. - Removed StreamTransformer::transformCallback. Create inherited class and override transform() method instead. - Removed StreamTransformer::StreamTransformer (IDataSourceStream *stream, const StreamTransformerCallback &callback, size_t resultSize=256, size_t blockSize=64). Create inherited class, override transform() method and Use alternative constructor instead. - Removed SslValidatorCallback. Use Ssl::ValidatorCallback instead. - Removed SslSessionId and SSLSessionId. Use Ssl::SessionId instead. - Removed SslKeyCertPair and SSLKeyCertPair. Use Ssl::KeyCertPair instead. - Removed SslCertificate. Use Ssl::Certificate instead. - Removed SeekOriginFlags. Use SeekOrigin instead. - Removed eSO_FileStart. Use SeekOrigin::Start instead. - Removed eSO_CurrentPos. Use SeekOrigin::Current instead. - Removed eSO_FileEnd. Use SeekOrigin::End instead. - Removed OtaUpgrade::BasicStream::errorToString. Use toString() global function. - Removed deprecated stuff in Mqtt. Removed MQTT_MAX_BUFFER_SIZE, MQTT_MSG_PUBREC, - Removed MqttClient::publishWithQoS (const String &topic, const String &message, int QoS, bool retained=false, MqttMessageDeliveredCallback onDelivery=nullptr). Use publish(const String& topic, const String& message, uint8_t flags = 0) instead. If you want to have a callback that should be triggered on successful delivery of messages then Use setEventHandler(MQTT_TYPE_PUBACK, youCallback) instead. - Removed MqttClient::setCallback (MqttStringSubscriptionCallback subscriptionCallback=nullptr). Use MqttClient::setEventHandler(MQTT_TYPE_PUBLISH, MqttDelegate handler) instead. - Removed MqttClient::setWill (const String &topic, const String &message, int QoS, bool retained=false). Use MqttClient::setWill(const String& topic, const String& message,uint8_t flags) instead. - Removed MqttMessageDeliveredCallback. Use MqttDelegate instead. - Removed MqttStringSubscriptionCallback. Use MqttDelegate instead. - Removed IDataSourceStream::length(). Use IDataSourceStream::available() instead. - Removed HttpServer::setDefaultResource (HttpResource *resource). Use paths.setDefault() instead. - Removed HttpServer::addPath (String path, const HttpPathDelegate &callback), HttpServer::addPath (const String &path, const HttpResourceDelegate &onRequestComplete) and HttpServer::addPath (const String &path, HttpResource *resource). Use paths.set() instead. - Removed HttpResponse::toString (const HttpResponse &res). Use HttpResponse::toString() method or toString(HttpResponse) function instead. - Removed HttpResponse::sendTemplate (IDataSourceStream *newTemplateInstance). Use sendNamedStream() instead. - Removed commandFunctionDelegate. Use CommandFunctionDelegate instead. - Removd DateTime::convertFromUnixTime (time_t timep, int8_t *psec, int8_t *pmin, int8_t *phour, int8_t *pday, int8_t *pwday, int8_t *pmonth, int16_t *pyear). Use fromUnixTime(time_t, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint16_t*) instead. - Removed DateTime::convertToUnixTime (uint8_t sec, uint8_t min, uint8_t hour, uint8_t day, uint8_t month, uint16_t year). Use 'toUnixTime()' instead. - Removed DateTime::fromUnixTime (time_t timep, int8_t *psec, int8_t *pmin, int8_t *phour, int8_t *pday, int8_t *pwday, int8_t *pmonth, int16_t *pyear). Use unsigned version instead fromUnixTime(time_t, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint16_t*). - Removed DateTime::parseHttpDate (const String &httpDate). Use fromHttpDate() instead. - Removed DNSServer. Use DnsServer instead. - Removed eFO_Append. Use File::Append instead. - Removed eFO_CreateIfNotExist. Use File::Create instead. - Removed eFO_CreateNewAlways. Use File::CreateNewAlways instead. - Removed eFO_ReadOnly. Use File::ReadOnly instead. - Removed eFO_ReadWrite. Use File::ReadWrite instead. - Removed eFO_Truncate. Use File::Truncate instead. - Removed eFO_WriteOnly. Use File::WriteOnly instead. - Removed eSO_CurrentPos. Use SeekOrigin::Current instead. - Removed eSO_FileEnd. Use SeekOrigin::End instead. - Removed eSO_FileStart. Use SeekOrigin::Start instead. - Removed fileList (). Use Directory object (or fileOpenDir / fileReadDir / fileCloseDir). - Removed FileStream::attach (const String &fileName, FileOpenFlags openFlags=File::ReadOnly). Use FileStream::open() instead. - Removed FTPServer. Use FtpServer instead. - Removed FtpServer::checkUser (const String &login, const String &pass). Use FtpServer::validateUser() instead - Removed Hardware_Timer. Use HardwareTimer class instead. - Removed HardwareSerial::setCallback (StreamDataReceivedDelegate dataReceivedDelegate). Use HardwareSerial::onDataReceived instead. - Removed HttpClient::request (const String &url). Use createRequest() instead. - Removed HttpConnection::getLastModifiedDate (). Use getResponse()->headers.getLastModifiedDate() instead. - Removed HttpConnection::getResponseCode (). Use getResponse()->code instead. - Removed HttpConnection::getResponseHeader (const String &headerName, const String &defaultValue=nullptr). Use getResponse()->headers[] instead. - Removed HttpConnection::getResponseHeaders (). Use getResponse()->headers instead. - Removed HttpConnection::getResponseString (). Use getResponse()->getBody() instead. - Removed HttpConnection::getServerDate (). Use getResponse()->headers.getServerDate() instead. - Removed httpGetErrorName (HttpError err). Use toString(HttpError) instead. - Removed HttpPartProducerDelegate. Use MultipartStream::Producer instead. - Removed HttpPartResult. Use MultipartStream::BodyPart instead. - Removed HttpRequest::getPath (). Use uri.Path instead. - Removed HttpRequest::operator= (const HttpRequest &rhs). Use clone() instead. - Removed HttpRequest::setPostParameters (const HttpParams ¶ms). Set postParams directly, i.e. request.postParams = params. - Removed HttpResponse::forbidden (). Use response.code = HTTP_STATUS_FORBIDDEN instead. - Removed HttpResponse::hasHeader (const String &name). Use headers.contains() instead. - Removed HttpResponse::notFound (). Use response.code = HTTP_STATUS_NOT_FOUND instead. - Removed HttpResponse::redirect (const String &location). Use headers[HTTP_HEADER_LOCATION] instead. Co-authored-by: mikee47 --- .../Arch/Esp32/Components/esp32/component.mk | 1 - .../Esp32/Components/esp32/sdk/config/common | 3 + Sming/Components/Network/component.mk | 4 - Sming/Components/Network/src/IpAddress.h | 3 - .../Network/src/Network/DnsServer.h | 3 - .../src/Network/Ftp/FtpServerConnection.h | 2 - .../Network/src/Network/FtpServer.h | 14 --- .../Network/src/Network/Http/HttpCommon.h | 11 -- .../Network/src/Network/Http/HttpConnection.h | 50 -------- .../Network/src/Network/Http/HttpRequest.h | 22 ---- .../Network/src/Network/Http/HttpResponse.h | 54 -------- .../Network/src/Network/HttpClient.h | 6 - .../Network/src/Network/HttpServer.h | 30 ----- .../Network/src/Network/MqttClient.h | 116 ------------------ Sming/Components/Network/src/Network/Url.h | 2 - .../Network/src/Network/WebsocketClient.h | 8 -- .../Components/Network/src/Platform/Station.h | 8 -- .../include/Data/Stream/RbootOutputStream.h | 3 - .../rboot/include/Network/RbootHttpUpdater.h | 15 --- .../ssl/include/Network/Ssl/Certificate.h | 5 - .../ssl/include/Network/Ssl/KeyCertPair.h | 10 -- .../ssl/include/Network/Ssl/SessionId.h | 10 -- .../ssl/include/Network/Ssl/Validator.h | 5 - Sming/Core/Data/Stream/DataSourceStream.h | 11 -- Sming/Core/Data/Stream/FileStream.h | 6 - Sming/Core/Data/Stream/MultipartStream.h | 10 -- Sming/Core/Data/Stream/TemplateStream.h | 5 - Sming/Core/Data/StreamTransformer.h | 22 +--- Sming/Core/DateTime.h | 67 ---------- Sming/Core/FileSystem.cpp | 15 --- Sming/Core/FileSystem.h | 22 ---- Sming/Core/HardwareSerial.h | 10 -- Sming/Core/HardwareTimer.h | 5 - Sming/Core/Timer.h | 3 - .../src/include/Ota/Network/HttpUpgrader.h | 9 -- .../OtaUpgrade/OtaUpgrade/BasicStream.cpp | 5 - .../OtaUpgrade/OtaUpgrade/BasicStream.h | 6 - .../tools/deployer/app/application.cpp | 2 +- .../CommandProcessing/CommandDelegate.h | 3 - Sming/component.mk | 7 -- .../Basic_WebSkeletonApp/app/DelayStream.cpp | 2 +- tests/HostTests/modules/TemplateStream.cpp | 4 +- 42 files changed, 8 insertions(+), 591 deletions(-) diff --git a/Sming/Arch/Esp32/Components/esp32/component.mk b/Sming/Arch/Esp32/Components/esp32/component.mk index b363b93cda..002f489da1 100644 --- a/Sming/Arch/Esp32/Components/esp32/component.mk +++ b/Sming/Arch/Esp32/Components/esp32/component.mk @@ -117,7 +117,6 @@ SDK_COMPONENTS += \ esp_wifi \ esp_eth \ lwip \ - mbedtls \ mbedcrypto \ esp_netif \ openssl diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/config/common b/Sming/Arch/Esp32/Components/esp32/sdk/config/common index eb0ee896e9..6bb6d89c55 100644 --- a/Sming/Arch/Esp32/Components/esp32/sdk/config/common +++ b/Sming/Arch/Esp32/Components/esp32/sdk/config/common @@ -37,5 +37,8 @@ CONFIG_VFS_SUPPORT_DIR=n CONFIG_VFS_SUPPORT_SELECT=n CONFIG_VFS_SUPPORT_TERMIOS=n +# Not used +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n + # Debugging CONFIG_ESP_SYSTEM_PANIC_GDBSTUB=y diff --git a/Sming/Components/Network/component.mk b/Sming/Components/Network/component.mk index e0b6847fb9..9ce420d2a7 100644 --- a/Sming/Components/Network/component.mk +++ b/Sming/Components/Network/component.mk @@ -47,10 +47,6 @@ COMPONENT_VARS += HTTP_SERVER_EXPOSE_VERSION HTTP_SERVER_EXPOSE_VERSION ?= 0 GLOBAL_CFLAGS += -DHTTP_SERVER_EXPOSE_VERSION=$(HTTP_SERVER_EXPOSE_VERSION) -# => MQTT -COMPONENT_VARS += MQTT_NO_COMPAT -MQTT_NO_COMPAT ?= 1 - # => LWIP COMPONENT_VARS += ENABLE_CUSTOM_LWIP ifeq ($(SMING_ARCH),Esp8266) diff --git a/Sming/Components/Network/src/IpAddress.h b/Sming/Components/Network/src/IpAddress.h index 0e5fe33c10..12efce480f 100644 --- a/Sming/Components/Network/src/IpAddress.h +++ b/Sming/Components/Network/src/IpAddress.h @@ -198,8 +198,5 @@ inline String toString(IpAddress address) return address.toString(); } -/** @deprecated Use `IpAddress` instead. */ -typedef IpAddress IPAddress SMING_DEPRECATED; - // Making this extern saves 100's of bytes; each usage otherwise incurs 4 bytes of BSS #define INADDR_NONE IpAddress() diff --git a/Sming/Components/Network/src/Network/DnsServer.h b/Sming/Components/Network/src/Network/DnsServer.h index 3f5eefc1cf..9c0075c67d 100644 --- a/Sming/Components/Network/src/Network/DnsServer.h +++ b/Sming/Components/Network/src/Network/DnsServer.h @@ -98,7 +98,4 @@ class DnsServer : public UdpConnection bool requestIncludesOnlyOneQuestion(); }; -/** @deprecated Use `DnsServer` */ -typedef DnsServer DNSServer SMING_DEPRECATED; - /** @} */ diff --git a/Sming/Components/Network/src/Network/Ftp/FtpServerConnection.h b/Sming/Components/Network/src/Network/Ftp/FtpServerConnection.h index 85292cfd46..a3dcd14a27 100644 --- a/Sming/Components/Network/src/Network/Ftp/FtpServerConnection.h +++ b/Sming/Components/Network/src/Network/Ftp/FtpServerConnection.h @@ -80,6 +80,4 @@ class FtpServerConnection : public TcpConnection FtpDataStream* dataConnection{nullptr}; }; -typedef FtpServerConnection FTPServerConnection SMING_DEPRECATED; // @deprecated Use `FtpServerConnection` instead - /** @} */ diff --git a/Sming/Components/Network/src/Network/FtpServer.h b/Sming/Components/Network/src/Network/FtpServer.h index 78f947b938..f5140e9c5e 100644 --- a/Sming/Components/Network/src/Network/FtpServer.h +++ b/Sming/Components/Network/src/Network/FtpServer.h @@ -72,15 +72,6 @@ class FtpServer : public CustomFtpServer void addUser(const String& login, const String& pass, IFS::UserRole userRole = IFS::UserRole::Admin); IFS::UserRole validateUser(const char* login, const char* pass) override; - /** - * @brief Legacy user validation - * @deprecated Use `validateUser()` instead - */ - bool checkUser(const String& login, const String& pass) SMING_DEPRECATED - { - return validateUser(login.c_str(), pass.c_str()) != IFS::UserRole::None; - } - protected: bool onCommand(String cmd, String data, FtpServerConnection& connection) override; @@ -92,8 +83,3 @@ class FtpServer : public CustomFtpServer using UserList = HashMap; UserList users; }; - -/** - * @deprecated Use `FtpServer` instead - */ -typedef FtpServer FTPServer SMING_DEPRECATED; diff --git a/Sming/Components/Network/src/Network/Http/HttpCommon.h b/Sming/Components/Network/src/Network/Http/HttpCommon.h index 8acb25fb19..6ee5181b36 100644 --- a/Sming/Components/Network/src/Network/Http/HttpCommon.h +++ b/Sming/Components/Network/src/Network/Http/HttpCommon.h @@ -98,17 +98,6 @@ using HttpFiles = ObjectMap; */ String toString(HttpError err); -/** - * @brief Return a string name of the given error - * @note This replaces the one in http_parser module which uses a load of RAM - * @deprecated Use `toString(HttpError)` - */ -inline String httpGetErrorName(HttpError err) SMING_DEPRECATED; -inline String httpGetErrorName(HttpError err) -{ - return toString(err); -} - /** * @brief Return a descriptive string for the given error */ diff --git a/Sming/Components/Network/src/Network/Http/HttpConnection.h b/Sming/Components/Network/src/Network/Http/HttpConnection.h index 99ab8e512d..7bcfa6135a 100644 --- a/Sming/Components/Network/src/Network/Http/HttpConnection.h +++ b/Sming/Components/Network/src/Network/Http/HttpConnection.h @@ -78,56 +78,6 @@ class HttpConnection : public TcpClient return &response; } - // Backported for compatibility reasons - - /** - * @deprecated Use `getResponse()->code` instead - */ - int getResponseCode() const SMING_DEPRECATED - { - return int(response.code); - } - - /** - * @deprecated Use `getResponse()->headers[]` instead - */ - String getResponseHeader(const String& headerName, const String& defaultValue = nullptr) const SMING_DEPRECATED - { - return response.headers[headerName] ?: defaultValue; - } - - /** - * @deprecated Use `getResponse()->headers` instead - */ - HttpHeaders& getResponseHeaders() SMING_DEPRECATED - { - return response.headers; - } - - /** - * @deprecated Use `getResponse()->headers.getLastModifiedDate()` instead - */ - DateTime getLastModifiedDate() const SMING_DEPRECATED - { - return response.headers.getLastModifiedDate(); - } - - /** - * @deprecated Use `getResponse()->headers.getServerDate()` instead - */ - DateTime getServerDate() const SMING_DEPRECATED - { - return response.headers.getServerDate(); - } - - /** - * @deprecated Use `getResponse()->getBody()` instead - */ - String getResponseString() SMING_DEPRECATED - { - return response.getBody(); - } - protected: /** @brief Called after all headers have been received and processed */ void resetHeaders(); diff --git a/Sming/Components/Network/src/Network/Http/HttpRequest.h b/Sming/Components/Network/src/Network/Http/HttpRequest.h index eac7b7831a..df4b4756c7 100644 --- a/Sming/Components/Network/src/Network/Http/HttpRequest.h +++ b/Sming/Components/Network/src/Network/Http/HttpRequest.h @@ -68,12 +68,6 @@ class HttpRequest return new HttpRequest(*this); } - /** @deprecated Please use `clone()` instead */ - HttpRequest& operator=(const HttpRequest& rhs) SMING_DEPRECATED - { - return *this; - } - ~HttpRequest() { reset(); @@ -103,15 +97,6 @@ class HttpRequest return this; } - /** - * @deprecated Set postParams directly, i.e. `request.postParams = params` - */ - HttpRequest* setPostParameters(const HttpParams& params) SMING_DEPRECATED - { - postParams = params; - return this; - } - HttpRequest* setPostParameter(const String& name, const String& value) { postParams[name] = value; @@ -153,13 +138,6 @@ class HttpRequest return static_cast(postParams)[name]; } - /** @deprecated Use `uri.Path` instead */ - String getPath() SMING_DEPRECATED - { - return uri.Path; - } - - /* @deprecated Use methods of `uri.Query` instead */ String getQueryParameter(const String& parameterName, const String& defaultValue = nullptr) const { return static_cast(uri.Query)[parameterName] ?: defaultValue; diff --git a/Sming/Components/Network/src/Network/Http/HttpResponse.h b/Sming/Components/Network/src/Network/Http/HttpResponse.h index b4579b810f..24cb890dc7 100644 --- a/Sming/Components/Network/src/Network/Http/HttpResponse.h +++ b/Sming/Components/Network/src/Network/Http/HttpResponse.h @@ -34,38 +34,6 @@ class HttpResponse bool sendString(String&& text) noexcept; - /** - * @deprecated Use `headers.contains()` instead - */ - bool hasHeader(const String& name) SMING_DEPRECATED - { - return headers.contains(name); - } - - /** - * @deprecated Use `headers[HTTP_HEADER_LOCATION]` instead - */ - void redirect(const String& location) SMING_DEPRECATED - { - headers[HTTP_HEADER_LOCATION] = location; - } - - /** - * @deprecated Use `response.code = HTTP_STATUS_FORBIDDEN` instead - */ - void forbidden() SMING_DEPRECATED - { - code = HTTP_STATUS_FORBIDDEN; - } - - /** - * @deprecated Use `response.code = HTTP_STATUS_NOT_FOUND` instead - */ - void notFound() SMING_DEPRECATED - { - code = HTTP_STATUS_NOT_FOUND; - } - HttpResponse* setContentType(const String& type) { headers[HTTP_HEADER_CONTENT_TYPE] = type; @@ -102,17 +70,6 @@ class HttpResponse */ bool sendFile(const String& fileName, bool allowGzipFileCheck = true); - /** - * @brief Parse and send template file - * @param newTemplateInstance - * @retval bool - * @deprecated Use `sendNamedStream()` instead - */ - bool sendTemplate(IDataSourceStream* newTemplateInstance) SMING_DEPRECATED - { - return sendNamedStream(newTemplateInstance); - } - /** * @brief Parse and send stream, using the name to determine the content type * @param newDataStream If not set already, the contentType will be obtained from the name of this stream @@ -185,17 +142,6 @@ class HttpResponse */ String toString() const; - /** - * @brief Tries to present a readable version of the response - * @param res - * @retval String - * @deprecated use `toString()` method or `toString(HttpResponse)` function - */ - static String toString(const HttpResponse& res) SMING_DEPRECATED - { - return res.toString(); - } - private: void setStream(IDataSourceStream* stream); diff --git a/Sming/Components/Network/src/Network/HttpClient.h b/Sming/Components/Network/src/Network/HttpClient.h index 80a40fa6f8..9d1e86687b 100644 --- a/Sming/Components/Network/src/Network/HttpClient.h +++ b/Sming/Components/Network/src/Network/HttpClient.h @@ -111,12 +111,6 @@ class HttpClient */ bool send(HttpRequest* request); - /** @deprecated Use `createRequest()` instead */ - HttpRequest* request(const String& url) SMING_DEPRECATED - { - return createRequest(url); - } - /** @brief Helper function to create a new request on a URL * @param url * @retval HttpRequest* diff --git a/Sming/Components/Network/src/Network/HttpServer.h b/Sming/Components/Network/src/Network/HttpServer.h index 9f4ec11a50..7ca2b0b26b 100644 --- a/Sming/Components/Network/src/Network/HttpServer.h +++ b/Sming/Components/Network/src/Network/HttpServer.h @@ -76,36 +76,6 @@ class HttpServer : public TcpServer bodyParsers[toString(mimeType)] = parser; } - /** @deprecated Use `paths.set()` instead */ - void addPath(String path, const HttpPathDelegate& callback) SMING_DEPRECATED - { - paths.set(path, callback); - } - - /** @deprecated Use `paths.set()` instead */ - void addPath(const String& path, const HttpResourceDelegate& onRequestComplete) SMING_DEPRECATED - { - paths.set(path, onRequestComplete); - } - - /** @deprecated Use `paths.set()` instead */ - void addPath(const String& path, HttpResource* resource) SMING_DEPRECATED - { - paths.set(path, resource); - } - - /** @deprecated Use `paths.setDefault()` instead */ - void setDefaultHandler(const HttpPathDelegate& callback) SMING_DEPRECATED - { - paths.setDefault(callback); - } - - /** @deprecated Use `paths.setDefault()` instead */ - void setDefaultResource(HttpResource* resource) SMING_DEPRECATED - { - paths.setDefault(resource); - } - public: /** @brief Maps paths to resources which deal with incoming requests */ HttpResourceTree paths; diff --git a/Sming/Components/Network/src/Network/MqttClient.h b/Sming/Components/Network/src/Network/MqttClient.h index e648e0ea7e..ea169e20ca 100644 --- a/Sming/Components/Network/src/Network/MqttClient.h +++ b/Sming/Components/Network/src/Network/MqttClient.h @@ -38,23 +38,11 @@ enum MqttClientState { eMCS_Ready = 0, eMCS_SendingData }; #define MQTT_FLAG_RETAINED 1 -#ifndef MQTT_NO_COMPAT -#define MQTT_MAX_BUFFER_SIZE MQTT_PAYLOAD_LENGTH ///< @deprecated -#define MQTT_MSG_PUBREC MQTT_TYPE_PUBREC ///< @deprecated -#endif - class MqttClient; using MqttDelegate = Delegate; using MqttRequestQueue = ObjectQueue; -#ifndef MQTT_NO_COMPAT -/** @deprecated Use MqttDelegate instead */ -using MqttStringSubscriptionCallback = Delegate; -/** @deprecated Use MqttDelegate instead */ -using MqttMessageDeliveredCallback = Delegate; -#endif - class MqttClient : protected TcpClient { public: @@ -193,57 +181,6 @@ class MqttClient : protected TcpClient using TcpClient::getRemoteIp; using TcpClient::getRemotePort; -#ifndef MQTT_NO_COMPAT - /** - * @deprecated: Use setWill(const String& topic, const String& message,uint8_t flags) instead - */ - bool setWill(const String& topic, const String& message, int QoS, bool retained = false) SMING_DEPRECATED - { - uint8_t flags = (uint8_t)(retained + (QoS << 1)); - return setWill(topic, message, flags); - } - - /** - * @removed - * bool publish(String& topic, String& message, bool retained = false) - * Use publish(const String& topic, const String& message, uint8_t flags = 0) instead. - */ - - /** - * @deprecated: Use publish(const String& topic, const String& message, uint8_t flags = 0) instead. - * If you want to have a callback that should be triggered on successful delivery of messages - * then use setEventHandler(MQTT_TYPE_PUBACK, youCallback) instead. - */ - bool publishWithQoS(const String& topic, const String& message, int QoS, bool retained = false, - MqttMessageDeliveredCallback onDelivery = nullptr) SMING_DEPRECATED - { - if(onDelivery) { - if(QoS == 1) { - setEventHandler(MQTT_TYPE_PUBACK, onPuback); - this->onDelivery = onDelivery; - } else if(QoS == 2) { - setEventHandler(MQTT_TYPE_PUBREC, onPuback); - this->onDelivery = onDelivery; - } else { - debug_w("No callback is set for QoS == 0"); - } - } - - uint8_t flags = (uint8_t)(retained + (QoS << 1)); - return publish(topic, message, flags); - } - - /** - * @brief Provide a function to be called when a message is received from the broker - * @deprecated: Use setEventHandler(MQTT_TYPE_PUBLISH, MqttDelegate handler) instead. - */ - void setCallback(MqttStringSubscriptionCallback subscriptionCallback = nullptr) SMING_DEPRECATED - { - this->subscriptionCallback = subscriptionCallback; - setEventHandler(MQTT_TYPE_PUBLISH, onPublish); - } -#endif - protected: void onReadyToSendData(TcpConnectionEvent sourceEvent) override; void onFinished(TcpClientState finishState) override; @@ -260,54 +197,6 @@ class MqttClient : protected TcpClient static int staticOnMessageEnd(void* user_data, mqtt_message_t* message); int onMessageEnd(mqtt_message_t* message); -#ifndef MQTT_NO_COMPAT - /** @deprecated This method is only for compatibility with the previous release and will be removed soon. */ - static int onPuback(MqttClient& client, mqtt_message_t* message) - { - if(!message) { - return 1; - } - - if(client.onDelivery) { - uint16_t msgId = 0; - if(message->common.type == MQTT_TYPE_PUBACK) { - msgId = message->puback.message_id; - } else if(message->common.type == MQTT_TYPE_PUBREC) { - msgId = message->pubrec.message_id; - } - - if(msgId) { - client.onDelivery(msgId, (int)message->common.type); - } - } - - return 0; - } - - /** @deprecated This method is only for compatibility with the previous release and will be removed soon. */ - static int onPublish(MqttClient& client, mqtt_message_t* message) - { - if(message == nullptr) { - return -1; - } - - if(message->common.length > MQTT_PAYLOAD_LENGTH) { - return -2; - } - - if(client.subscriptionCallback) { - String topic = String((const char*)message->publish.topic_name.data, message->publish.topic_name.length); - String content; - if(message->publish.content.data) { - content.concat((const char*)message->publish.content.data, message->publish.content.length); - } - client.subscriptionCallback(topic, content); - } - - return 0; - } -#endif - private: Url url; @@ -343,11 +232,6 @@ class MqttClient : protected TcpClient * | * --- set when connected ... */ - -#ifndef MQTT_NO_COMPAT - SMING_DEPRECATED MqttMessageDeliveredCallback onDelivery = nullptr; ///< @deprecated - SMING_DEPRECATED MqttStringSubscriptionCallback subscriptionCallback = nullptr; ///< @deprecated -#endif }; /** @} */ diff --git a/Sming/Components/Network/src/Network/Url.h b/Sming/Components/Network/src/Network/Url.h index 9da50d6091..dd21edcd55 100644 --- a/Sming/Components/Network/src/Network/Url.h +++ b/Sming/Components/Network/src/Network/Url.h @@ -177,8 +177,6 @@ class Url String Fragment; ///< Without '#' }; -typedef Url URL SMING_DEPRECATED; ///< @deprecated Use `Url` instead - inline String toString(const Url& url) { return url.toString(); diff --git a/Sming/Components/Network/src/Network/WebsocketClient.h b/Sming/Components/Network/src/Network/WebsocketClient.h index a62e0996af..3952942e0f 100644 --- a/Sming/Components/Network/src/Network/WebsocketClient.h +++ b/Sming/Components/Network/src/Network/WebsocketClient.h @@ -90,14 +90,6 @@ class WebsocketClient : protected WebsocketConnection sslInitHandler = handler; } - /** @brief Disconnects websocket client from server - * @deprecated Use `close()` instead - */ - void disconnect() SMING_DEPRECATED - { - close(); - } - protected: int verifyKey(HttpConnection& connection, HttpResponse& response); diff --git a/Sming/Components/Network/src/Platform/Station.h b/Sming/Components/Network/src/Platform/Station.h index 19e68d40da..5a3441a9e2 100644 --- a/Sming/Components/Network/src/Platform/Station.h +++ b/Sming/Components/Network/src/Platform/Station.h @@ -295,14 +295,6 @@ class StationClass */ virtual bool wpsConfigStart(WPSConfigDelegate callback = nullptr) = 0; - /** @brief Start WiFi station by WPS method - * @deprecated Use `wpsConfigStart()` - */ - bool beginWPSConfig() SMING_DEPRECATED - { - return wpsConfigStart(nullptr); - } - /** @brief Stop WiFi station WPS configuration */ virtual void wpsConfigStop() = 0; diff --git a/Sming/Components/rboot/include/Data/Stream/RbootOutputStream.h b/Sming/Components/rboot/include/Data/Stream/RbootOutputStream.h index 1873d01885..fca76c75a5 100644 --- a/Sming/Components/rboot/include/Data/Stream/RbootOutputStream.h +++ b/Sming/Components/rboot/include/Data/Stream/RbootOutputStream.h @@ -97,6 +97,3 @@ class RbootOutputStream : public ReadWriteStream protected: virtual bool init(); }; - -/** @deprecated Use `RbootOutputStream` */ -typedef RbootOutputStream rBootOutputStream SMING_DEPRECATED; diff --git a/Sming/Components/rboot/include/Network/RbootHttpUpdater.h b/Sming/Components/rboot/include/Network/RbootHttpUpdater.h index 2100626770..3816d914b7 100644 --- a/Sming/Components/rboot/include/Network/RbootHttpUpdater.h +++ b/Sming/Components/rboot/include/Network/RbootHttpUpdater.h @@ -143,15 +143,6 @@ class RbootHttpUpdater : protected HttpClient baseRequest = request; } - /** - * @brief Allow reading items - * @deprecated Access list directly using `getItems()` - */ - const Item& getItem(unsigned int index) const SMING_DEPRECATED - { - return items[index]; - } - /** * @brief Allow read access to item list */ @@ -175,9 +166,3 @@ class RbootHttpUpdater : protected HttpClient uint8_t currentItem{0}; rboot_write_status rbootWriteStatus{}; }; - -/** @deprecated Use `RbootHttpUpdater` */ -typedef RbootHttpUpdater rBootHttpUpdate SMING_DEPRECATED; - -/** @deprecated Use 'auto' in expressions or `RbootHttpUpdater::Item` */ -typedef RbootHttpUpdater::Item RbootHttpUpdaterItem SMING_DEPRECATED; diff --git a/Sming/Components/ssl/include/Network/Ssl/Certificate.h b/Sming/Components/ssl/include/Network/Ssl/Certificate.h index a02d4a94be..837d1c2e65 100644 --- a/Sming/Components/ssl/include/Network/Ssl/Certificate.h +++ b/Sming/Components/ssl/include/Network/Ssl/Certificate.h @@ -98,8 +98,3 @@ class Certificate String toString(Certificate::RDN rdn); } // namespace Ssl - -/** - * @deprecated Use `Ssl::Certificate` instead - */ -typedef Ssl::Certificate SslCertificate SMING_DEPRECATED; diff --git a/Sming/Components/ssl/include/Network/Ssl/KeyCertPair.h b/Sming/Components/ssl/include/Network/Ssl/KeyCertPair.h index 2082b4f517..b3fa85444c 100644 --- a/Sming/Components/ssl/include/Network/Ssl/KeyCertPair.h +++ b/Sming/Components/ssl/include/Network/Ssl/KeyCertPair.h @@ -99,13 +99,3 @@ class KeyCertPair }; } // namespace Ssl - -/** - * @deprecated Use Ssl::KeyCertPair instead - */ -typedef Ssl::KeyCertPair SslKeyCertPair SMING_DEPRECATED; - -/** - * @deprecated Use Ssl::KeyCertPair instead - */ -typedef Ssl::KeyCertPair SSLKeyCertPair SMING_DEPRECATED; diff --git a/Sming/Components/ssl/include/Network/Ssl/SessionId.h b/Sming/Components/ssl/include/Network/Ssl/SessionId.h index 05119f428b..9b39d33e9b 100644 --- a/Sming/Components/ssl/include/Network/Ssl/SessionId.h +++ b/Sming/Components/ssl/include/Network/Ssl/SessionId.h @@ -63,13 +63,3 @@ __forceinline String toString(const SessionId& id) } } // namespace Ssl - -/** - * @deprecated Use Ssl::SessionId instead - */ -typedef Ssl::SessionId SslSessionId SMING_DEPRECATED; - -/** - * @deprecated Use Ssl::SessionId instead - */ -typedef Ssl::SessionId SSLSessionId SMING_DEPRECATED; diff --git a/Sming/Components/ssl/include/Network/Ssl/Validator.h b/Sming/Components/ssl/include/Network/Ssl/Validator.h index b6faa9a80e..a1bef660d4 100644 --- a/Sming/Components/ssl/include/Network/Ssl/Validator.h +++ b/Sming/Components/ssl/include/Network/Ssl/Validator.h @@ -103,8 +103,3 @@ class CallbackValidator : public Validator }; } // namespace Ssl - -/** - * @deprecated Use `Ssl::ValidatorCallback` instead - */ -typedef Ssl::ValidatorCallback SslValidatorCallback SMING_DEPRECATED; diff --git a/Sming/Core/Data/Stream/DataSourceStream.h b/Sming/Core/Data/Stream/DataSourceStream.h index 8c77cd559d..f0915aae64 100644 --- a/Sming/Core/Data/Stream/DataSourceStream.h +++ b/Sming/Core/Data/Stream/DataSourceStream.h @@ -131,17 +131,6 @@ class IDataSourceStream : public Stream return 0; } - /** - * @brief Return the total length of the stream - * @retval int -1 is returned when the size cannot be determined - * - * @deprecated Use `available()` instead - */ - int length() SMING_DEPRECATED - { - return available(); - } - /* * @brief Flushes the stream */ diff --git a/Sming/Core/Data/Stream/FileStream.h b/Sming/Core/Data/Stream/FileStream.h index f42aaaac6d..1e03feeef3 100644 --- a/Sming/Core/Data/Stream/FileStream.h +++ b/Sming/Core/Data/Stream/FileStream.h @@ -39,10 +39,4 @@ class FileStream : public IFS::FileStream } using IFS::FileStream::attach; - - /** @deprecated Use `open()` instead */ - bool attach(const String& fileName, FileOpenFlags openFlags = File::ReadOnly) SMING_DEPRECATED - { - return open(fileName, openFlags); - } }; diff --git a/Sming/Core/Data/Stream/MultipartStream.h b/Sming/Core/Data/Stream/MultipartStream.h index 53a4a279a2..68fb88c87a 100644 --- a/Sming/Core/Data/Stream/MultipartStream.h +++ b/Sming/Core/Data/Stream/MultipartStream.h @@ -68,13 +68,3 @@ class MultipartStream : public MultiStream char boundary[16]{}; bool footerSent{false}; }; - -/** - * @deprecated Use `MultipartStream::BodyPart` instead - */ -typedef MultipartStream::BodyPart HttpPartResult SMING_DEPRECATED; - -/** - * @deprecated Use `MultipartStream::Producer` instead - */ -typedef MultipartStream::Producer HttpPartProducerDelegate SMING_DEPRECATED; diff --git a/Sming/Core/Data/Stream/TemplateStream.h b/Sming/Core/Data/Stream/TemplateStream.h index 824fb134d3..8133b4a1eb 100644 --- a/Sming/Core/Data/Stream/TemplateStream.h +++ b/Sming/Core/Data/Stream/TemplateStream.h @@ -166,8 +166,3 @@ class TemplateStream : public IDataSourceStream bool enableNextState : 1; bool doubleBraces : 1; }; - -/** - * @deprecated Use `TemplateStream::Variables` instead - */ -typedef TemplateStream::Variables TemplateVariables; diff --git a/Sming/Core/Data/StreamTransformer.h b/Sming/Core/Data/StreamTransformer.h index fe293ec88e..d83a04d5e7 100644 --- a/Sming/Core/Data/StreamTransformer.h +++ b/Sming/Core/Data/StreamTransformer.h @@ -35,18 +35,6 @@ class StreamTransformer : public IDataSourceStream { } - /** @brief Constructor with external callback function - * @deprecated Create inherited class, override `transform()` method and use alternative constructor instead - */ - StreamTransformer(IDataSourceStream* stream, const StreamTransformerCallback& callback, size_t resultSize = 256, - size_t blockSize = 64) SMING_DEPRECATED : transformCallback(callback), - sourceStream(stream), - result(new uint8_t[resultSize]), - resultSize(resultSize), - blockSize(blockSize) - { - } - ~StreamTransformer() { delete[] result; @@ -106,15 +94,7 @@ class StreamTransformer : public IDataSourceStream * @retval size_t number of output bytes written * @note Called with `in = nullptr` and `inLength = 0` at end of input stream */ - virtual size_t transform(const uint8_t* in, size_t inLength, uint8_t* out, size_t outLength) - { - return (transformCallback == nullptr) ? 0 : transformCallback(in, inLength, out, outLength); - } - - /** @brief Callback function to perform transformation - * @deprecated Create inherited class and override transform() method instead - */ - StreamTransformerCallback transformCallback = nullptr; + virtual size_t transform(const uint8_t* in, size_t inLength, uint8_t* out, size_t outLength) = 0; private: void fillTempStream(char* buffer, size_t bufSize); diff --git a/Sming/Core/DateTime.h b/Sming/Core/DateTime.h index 6a759def5f..2e301286df 100644 --- a/Sming/Core/DateTime.h +++ b/Sming/Core/DateTime.h @@ -132,18 +132,6 @@ class DateTime */ bool fromHttpDate(const String& httpDate); - /** @brief Parse a HTTP full date and set time and date - * @param httpDate HTTP full date in RFC 1123 format, e.g. Sun, 06 Nov 1994 08:49:37 GMT - * @retval bool True on success - * @note Also supports obsolete RFC 850 date format, e.g. Sunday, 06-Nov-94 08:49:37 GMT where 2 digit year represents range 1970-2069 - * @note GMT suffix is optional and is always assumed / ignored - * @deprecated Use `fromHttpDate()` instead - */ - bool parseHttpDate(const String& httpDate) SMING_DEPRECATED - { - return fromHttpDate(httpDate); - } - /** @brief Check if time date object is initialised * @retval True if object has no value. False if initialised. */ @@ -206,43 +194,7 @@ class DateTime static void fromUnixTime(time_t timep, uint8_t* psec, uint8_t* pmin, uint8_t* phour, uint8_t* pday, uint8_t* pwday, uint8_t* pmonth, uint16_t* pyear); - /** @deprecated Use unsigned version instead - * `fromUnixTime(time_t, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint16_t*)` */ - static void fromUnixTime(time_t timep, int8_t* psec, int8_t* pmin, int8_t* phour, int8_t* pday, int8_t* pwday, - int8_t* pmonth, int16_t* pyear) SMING_DEPRECATED - { - fromUnixTime(timep, reinterpret_cast(psec), reinterpret_cast(pmin), - reinterpret_cast(phour), reinterpret_cast(pday), - reinterpret_cast(pwday), reinterpret_cast(pmonth), - reinterpret_cast(pyear)); - } - // functions to convert to and from time components (hrs, secs, days, years etc) to time_t - /** @brief Convert from Unix time to individual time components - * @param timep Unix time date value to convert - * @param psec Pointer to integer to hold resulting seconds - * @param pmin Pointer to integer to hold resulting minutes - * @param phour Pointer to integer to hold resulting hour - * @param pday Pointer to integer to hold resulting day of month - * @param pwday Pointer to integer to hold resulting day of week - * @param pmonth Pointer to integer to hold resulting month - * @param pyear Pointer to integer to hold resulting year - * @note This is a more compact version of the C library localtime function - * @note Pass the Unix timedate value and pointers to existing integers. The integers are updated with the converted values - * @note This static function may be used without instantiating a DateTime object, e.g. DateTime::convertFromUnixTime(...); - * @note 32-bit Unix time has year 2036 issue. - * @note Unix time does not account for leap seconds. To convert Unix time to UTC requires reference to a leap second table. - * @note All of the return values are optional, specify nullptr if not required - * @deprecated Use `fromUnixTime(time_t, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint16_t*)` instead - */ - static void convertFromUnixTime(time_t timep, int8_t* psec, int8_t* pmin, int8_t* phour, int8_t* pday, - int8_t* pwday, int8_t* pmonth, int16_t* pyear) SMING_DEPRECATED - { - fromUnixTime(timep, reinterpret_cast(psec), reinterpret_cast(pmin), - reinterpret_cast(phour), reinterpret_cast(pday), - reinterpret_cast(pwday), reinterpret_cast(pmonth), - reinterpret_cast(pyear)); - } /** @brief Convert from individual time components to Unix time * @param sec Seconds @@ -258,25 +210,6 @@ class DateTime */ static time_t toUnixTime(uint8_t sec, uint8_t min, uint8_t hour, uint8_t day, uint8_t month, uint16_t year); - /** @brief Convert from individual time components to Unix time - * @param sec Seconds - * @param min Minutes - * @param hour Hours - * @param day Days - * @param month Month (0-11, Jan=0, Feb=1, ...Dec=11) - * @param year Year (1901-2036), either full 4 digit year or 2 digits for 1970-2036 - * @note Seconds, minutes, hours and days may be any value, e.g. to calculate the value for 300 days since 1970 (epoch), set day=300 - * @note This static function may be used without instantiating a DateTime object, e.g. time_t unixTime = DateTime::convertToUnixTime(...); - * @note 32-bit Unix time is valid between 1901-12-13 and 03:14:07 2038-01-19 - * @note Unix time does not account for leap seconds. To convert Unix time to UTC requires reference to a leap second table. - * @deprecated Use 'toUnixTime()' instead - */ - static time_t convertToUnixTime(uint8_t sec, uint8_t min, uint8_t hour, uint8_t day, uint8_t month, - uint16_t year) SMING_DEPRECATED - { - return toUnixTime(sec, min, hour, day, month, year); - } - /** @brief Create string formatted with time and date placeholders * @param formatString String including date and time formatting * @retval String Formatted string diff --git a/Sming/Core/FileSystem.cpp b/Sming/Core/FileSystem.cpp index 7b10808d0e..92f1e08ea6 100644 --- a/Sming/Core/FileSystem.cpp +++ b/Sming/Core/FileSystem.cpp @@ -58,21 +58,6 @@ bool fwfs_mount(Storage::Partition partition) return fileMountFileSystem(fs); } -Vector fileList() -{ - Vector result; - - DirHandle dir; - if(fileOpenDir(nullptr, dir) == FS_OK) { - FileNameStat stat; - while(fileReadDir(dir, stat) >= 0) { - result.add(stat.name.buffer); - } - fileCloseDir(dir); - } - return result; -} - IFS::IFileSystem::Type fileSystemType() { if(SmingInternal::activeFileSystem == nullptr) { diff --git a/Sming/Core/FileSystem.h b/Sming/Core/FileSystem.h index ff4c72f4af..3042df2958 100644 --- a/Sming/Core/FileSystem.h +++ b/Sming/Core/FileSystem.h @@ -19,10 +19,8 @@ #include #include #include -#include "WVector.h" ///< @deprecated see fileList() using file_t = IFS::FileHandle; -typedef SeekOrigin SeekOriginFlags; ///< @deprecated Use `SeekOrigin` instead using FileHandle = IFS::FileHandle; using DirHandle = IFS::DirHandle; using FileOpenFlag = IFS::OpenFlag; @@ -33,10 +31,6 @@ using FileStat = IFS::Stat; using FileNameStat = IFS::NameStat; constexpr int FS_OK = IFS::FS_OK; -constexpr SeekOrigin eSO_FileStart{SeekOrigin::Start}; ///< @deprecated use SeekOrigin::Start -constexpr SeekOrigin eSO_CurrentPos{SeekOrigin::Current}; ///< @deprecated use SeekOrigin::Current -constexpr SeekOrigin eSO_FileEnd{SeekOrigin::End}; ///< @deprecated use SeekOrigin::End - namespace SmingInternal { /** @brief Global file system instance @@ -71,15 +65,6 @@ class Directory : public IFS::Directory } }; -// Various file flag combinations -constexpr FileOpenFlags eFO_ReadOnly{File::ReadOnly}; ///< @deprecated use File::ReadOnly -constexpr FileOpenFlags eFO_WriteOnly{File::WriteOnly}; ///< @deprecated use File::WriteOnly -constexpr FileOpenFlags eFO_ReadWrite{File::ReadWrite}; ///< @deprecated use File::ReadWrite -constexpr FileOpenFlags eFO_CreateIfNotExist{File::Create}; ///< @deprecated use File::Create -constexpr FileOpenFlags eFO_Append{File::Append}; ///< @deprecated use File::Append -constexpr FileOpenFlags eFO_Truncate{File::Truncate}; ///< @deprecated use File::Truncate -constexpr FileOpenFlags eFO_CreateNewAlways{File::CreateNewAlways}; ///< @deprecated use File::CreateNewAlways - /* * Boilerplate check for file function wrappers to catch undefined filesystem. */ @@ -325,13 +310,6 @@ inline int fileRename(const String& oldName, const String& newName) return fileRename(oldName.c_str(), newName.c_str()); } -/** @brief Get list of files on file system - * @retval Vector Vector of strings. - Each string element contains the name of a file on the file system - @deprecated use `Directory` object (or fileOpenDir / fileReadDir / fileCloseDir) - */ -Vector fileList() SMING_DEPRECATED; - /** @brief Read content of a file * @param fileName Name of file to read from * @retval String String variable in to which to read the file content diff --git a/Sming/Core/HardwareSerial.h b/Sming/Core/HardwareSerial.h index c2fe0cc17c..b1ad1bbbe1 100644 --- a/Sming/Core/HardwareSerial.h +++ b/Sming/Core/HardwareSerial.h @@ -339,16 +339,6 @@ class HardwareSerial : public ReadWriteStream */ void commandProcessing(bool reqEnable); - /** @brief Set handler for received data - * @param dataReceivedDelegate Function to handle received data - * @retval bool Returns true if the callback was set correctly - * @deprecated Use `onDataReceived` instead - */ - bool setCallback(StreamDataReceivedDelegate dataReceivedDelegate) SMING_DEPRECATED - { - return onDataReceived(dataReceivedDelegate); - } - /** @brief Set handler for received data * @param dataReceivedDelegate Function to handle received data * @retval bool Returns true if the callback was set correctly diff --git a/Sming/Core/HardwareTimer.h b/Sming/Core/HardwareTimer.h index bf3677e0a2..364f21ea50 100644 --- a/Sming/Core/HardwareTimer.h +++ b/Sming/Core/HardwareTimer.h @@ -153,9 +153,4 @@ using HardwareTimer1 = CallbackTimer>; */ using HardwareTimer = HardwareTimer1<>; -/** - * @deprecated Use HardwareTimer class instead - */ -typedef HardwareTimer Hardware_Timer SMING_DEPRECATED; - /** @} */ diff --git a/Sming/Core/Timer.h b/Sming/Core/Timer.h index e22c9581e4..192314dc84 100644 --- a/Sming/Core/Timer.h +++ b/Sming/Core/Timer.h @@ -20,9 +20,6 @@ * @{ */ -/** @deprecated Use `TimerDelegate` */ -typedef std::function TimerDelegateStdFunction SMING_DEPRECATED; - /** * @brief Class template implementing an extended OS Timer with 64-bit microsecond times and delegate callback support */ diff --git a/Sming/Libraries/OtaNetwork/src/include/Ota/Network/HttpUpgrader.h b/Sming/Libraries/OtaNetwork/src/include/Ota/Network/HttpUpgrader.h index 40816b6491..9a93aa114f 100644 --- a/Sming/Libraries/OtaNetwork/src/include/Ota/Network/HttpUpgrader.h +++ b/Sming/Libraries/OtaNetwork/src/include/Ota/Network/HttpUpgrader.h @@ -119,15 +119,6 @@ class HttpUpgrader : protected HttpClient baseRequest = request; } - /** - * @brief Allow reading items - * @deprecated Access list directly using `getItems()` - */ - const Item& getItem(unsigned int index) const SMING_DEPRECATED - { - return items[index]; - } - /** * @brief Allow read access to item list */ diff --git a/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp b/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp index 451936f54f..3ee7133304 100644 --- a/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp +++ b/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp @@ -201,11 +201,6 @@ size_t BasicStream::write(const uint8_t* data, size_t size) return origSize - size; } -String BasicStream::errorToString(Error code) -{ - return toString(code); -} - } // namespace OtaUpgrade String toString(OtaUpgrade::BasicStream::Error code) diff --git a/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.h b/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.h index aaa96251bc..c365cd7d0e 100644 --- a/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.h +++ b/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.h @@ -66,12 +66,6 @@ class BasicStream : public ReadWriteStream Error errorCode = Error::None; ///< Error code. Only relevant if `hasError()` returns `true`. - /** @brief Convert error code to string. - * @see #errorCode - * @deprecated Use `toString()` global function - */ - static String errorToString(Error code) SMING_DEPRECATED; - /** @brief Process chunk of upgrade file. * @param data Pointer to chunk of data. * @param size Size of chunk pointed to by \a data in bytes. diff --git a/Sming/Libraries/OtaUpgradeMqtt/tools/deployer/app/application.cpp b/Sming/Libraries/OtaUpgradeMqtt/tools/deployer/app/application.cpp index f3a513af69..ab13bf96dc 100644 --- a/Sming/Libraries/OtaUpgradeMqtt/tools/deployer/app/application.cpp +++ b/Sming/Libraries/OtaUpgradeMqtt/tools/deployer/app/application.cpp @@ -89,7 +89,7 @@ bool pack(const String& inputFileName, const String& outputFileName, size_t patc } HostFileStream output; - if(!output.open(outputFileName, eFO_CreateNewAlways | eFO_WriteOnly)) { + if(!output.open(outputFileName, File::CreateNewAlways | File::WriteOnly)) { fileError(output, outputFileName, F("open output")); return false; } diff --git a/Sming/Services/CommandProcessing/CommandDelegate.h b/Sming/Services/CommandProcessing/CommandDelegate.h index 2acab00e7f..f315813333 100644 --- a/Sming/Services/CommandProcessing/CommandDelegate.h +++ b/Sming/Services/CommandProcessing/CommandDelegate.h @@ -22,9 +22,6 @@ */ using CommandFunctionDelegate = Delegate; -/** @deprecated Use `CommandFunctionDelegate` instead */ -typedef CommandFunctionDelegate commandFunctionDelegate SMING_DEPRECATED; - /** @brief Command delegate class */ class CommandDelegate { diff --git a/Sming/component.mk b/Sming/component.mk index 1ea550f99a..14ebc96e4c 100644 --- a/Sming/component.mk +++ b/Sming/component.mk @@ -38,13 +38,6 @@ COMPONENT_SRCDIRS += Services/CommandProcessing endif GLOBAL_CFLAGS += -DENABLE_CMD_EXECUTOR=$(ENABLE_CMD_EXECUTOR) -# => MQTT -# Flags for compatability with old versions (most of them should disappear with the next major release) -COMPONENT_VARS += MQTT_NO_COMPAT -ifeq ($(MQTT_NO_COMPAT),1) - GLOBAL_CFLAGS += -DMQTT_NO_COMPAT=1 -endif - # RELINK_VARS += DISABLE_NETWORK DISABLE_NETWORK ?= 0 diff --git a/samples/Basic_WebSkeletonApp/app/DelayStream.cpp b/samples/Basic_WebSkeletonApp/app/DelayStream.cpp index e75105eca4..eb69a6b81c 100644 --- a/samples/Basic_WebSkeletonApp/app/DelayStream.cpp +++ b/samples/Basic_WebSkeletonApp/app/DelayStream.cpp @@ -11,7 +11,7 @@ void DelayStream::sendFile() fn = filename; } - if(FileStream::open(fn, eFO_ReadOnly)) { + if(FileStream::open(fn, File::ReadOnly)) { debug_i("opened '%s', %u bytes", fn.c_str(), FileStream::available()); response->setContentType(getMimeType()); } else { diff --git a/tests/HostTests/modules/TemplateStream.cpp b/tests/HostTests/modules/TemplateStream.cpp index f98d145ccc..008930f978 100644 --- a/tests/HostTests/modules/TemplateStream.cpp +++ b/tests/HostTests/modules/TemplateStream.cpp @@ -97,14 +97,14 @@ class TemplateStreamTest : public TestGroup #ifdef ARCH_HOST { - HostFileStream fs("test-src1.out", eFO_CreateNewAlways | eFO_WriteOnly); + HostFileStream fs("test-src1.out", File::CreateNewAlways | File::WriteOnly); int res = fs.copyFrom(&tmpl); debug_e("copyfrom(src) = %d", res); tmpl.gotoSection(0); } { - HostFileStream fs("test-src2.out", eFO_CreateNewAlways | eFO_WriteOnly); + HostFileStream fs("test-src2.out", File::CreateNewAlways | File::WriteOnly); int res = fs.copyFrom(&tmpl); debug_e("copyfrom(src) = %d", res); tmpl.gotoSection(0); From cb406e6f5f38f316ccca83162bf0164df06891a8 Mon Sep 17 00:00:00 2001 From: slaff Date: Wed, 6 Oct 2021 09:53:35 +0200 Subject: [PATCH 067/130] clang-format-8 is optional and should not prevent the installation. (#2381) Co-authored-by: mikee47 --- Tools/install.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Tools/install.sh b/Tools/install.sh index e998967777..76fdc2eeff 100755 --- a/Tools/install.sh +++ b/Tools/install.sh @@ -108,7 +108,6 @@ else debian) sudo apt-get -y update || echo "Update failed... Try to install anyway..." $PKG_INSTALL \ - clang-format-8 \ cmake \ curl \ git \ @@ -120,6 +119,8 @@ else python3-pip \ python3-setuptools \ wget + + $PKG_INSTALL clang-format-8 || printf "\nWARNING: Failed to install optional clang-format-8.\n\n" ;; fedora) @@ -148,7 +149,9 @@ fi set -e -sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-8 100 +if [ -f "/usr/bin/clang-format-8" ]; then + sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-8 100 +fi python3 -m pip install --upgrade pip -r $SMING_HOME/../Tools/requirements.txt From 0bb596cca1d0c0f592d8b0e06be6c57a8c421a49 Mon Sep 17 00:00:00 2001 From: slaff Date: Mon, 11 Oct 2021 08:47:41 +0200 Subject: [PATCH 068/130] Fix for SPI debug. (#2382) --- Sming/Core/SPISettings.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sming/Core/SPISettings.h b/Sming/Core/SPISettings.h index ff9947a779..d8753ffde1 100644 --- a/Sming/Core/SPISettings.h +++ b/Sming/Core/SPISettings.h @@ -14,6 +14,9 @@ #pragma once #include "Digital.h" +#ifdef SPI_DEBUG +#include +#endif /** @ingroup base_spi * @{ From 333af2eacbcbc24868f5c947474ef1913d5b0c26 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 11 Oct 2021 07:48:51 +0100 Subject: [PATCH 069/130] Bugfix: WDT::alive() calls wrong function (#2383) --- Sming/Platform/WDT.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Sming/Platform/WDT.cpp b/Sming/Platform/WDT.cpp index 3c5c0cae7f..d9731bd423 100644 --- a/Sming/Platform/WDT.cpp +++ b/Sming/Platform/WDT.cpp @@ -18,15 +18,16 @@ WDTClass WDT; void WDTClass::enable(bool enableWatchDog) { enabled = enableWatchDog; - if(System.isReady()) + if(System.isReady()) { internalApplyEnabled(); - else + } else { System.onReady(this); + } } void WDTClass::alive() { - system_soft_wdt_restart(); + system_soft_wdt_feed(); } void WDTClass::onSystemReady() @@ -36,8 +37,9 @@ void WDTClass::onSystemReady() void WDTClass::internalApplyEnabled() { - if(enabled) + if(enabled) { system_soft_wdt_restart(); - else + } else { system_soft_wdt_stop(); + } } From 14366beed939fb0cf14589f2d3ce3deb207d73e5 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 11 Oct 2021 07:49:43 +0100 Subject: [PATCH 070/130] Bugfix: String::replace not using memmove (#2384) Can fail when regions overlap --- Sming/Wiring/WString.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sming/Wiring/WString.cpp b/Sming/Wiring/WString.cpp index d32e20afe2..2c6838eebe 100644 --- a/Sming/Wiring/WString.cpp +++ b/Sming/Wiring/WString.cpp @@ -860,14 +860,14 @@ bool String::replace(const char* find_buf, size_t find_len, const char* replace_ char* writeTo = buf; while((foundAt = (char*)memmem(readFrom, end - readFrom, find_buf, find_len)) != nullptr) { size_t n = foundAt - readFrom; - memcpy(writeTo, readFrom, n); + memmove(writeTo, readFrom, n); writeTo += n; memcpy(writeTo, replace_buf, replace_len); writeTo += replace_len; readFrom = foundAt + find_len; len += diff; } - memcpy(writeTo, readFrom, end - readFrom); + memmove(writeTo, readFrom, end - readFrom); setlen(len); } else { size_t size = len; // compute size needed for result From 63ac63a48f0a9b0330b3a5140042b15c8b6029e4 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 11 Oct 2021 07:50:36 +0100 Subject: [PATCH 071/130] Fix hwconfig option handling, add 1m, 2m hwconfig options and spiffs-2m.hw (#2385) Fix allows inherited configurations to re-apply options. For example, 2m - 4m - 2m. Otherwise the second 2m is ignored and uses 4m instead. --- Sming/Arch/Host/standard.hw | 7 +------ .../Components/Storage/Tools/hwconfig/config.py | 6 +++--- Sming/options.json | 16 ++++++++++++++++ Sming/spiffs-2m.hw | 12 ++++++++++++ 4 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 Sming/spiffs-2m.hw diff --git a/Sming/Arch/Host/standard.hw b/Sming/Arch/Host/standard.hw index 287e348737..7244be0578 100644 --- a/Sming/Arch/Host/standard.hw +++ b/Sming/Arch/Host/standard.hw @@ -3,12 +3,7 @@ "arch": "Host", "bootloader_size": "0x2000", "partition_table_offset": "0x2000", - "devices": { - "spiFlash": { - "type": "flash", - "size": "4M" - } - }, + "options": ["4m"], "partitions": { "rom0": { "address": "0x008000", diff --git a/Sming/Components/Storage/Tools/hwconfig/config.py b/Sming/Components/Storage/Tools/hwconfig/config.py index cb273d7eb3..ddbf659888 100644 --- a/Sming/Components/Storage/Tools/hwconfig/config.py +++ b/Sming/Components/Storage/Tools/hwconfig/config.py @@ -122,11 +122,11 @@ def load(self, name): self.parse_dict(data) def parse_options(self, options): - """Apply any specified options, each option is applied only once + """Apply any specified options + + Each option can be applied more than once to ensure overrides work as expected """ for option in options: - if option in self.options: - continue self.options.append(option) data = self.option_library.get(option) if data is None: diff --git a/Sming/options.json b/Sming/options.json index a713d4bc65..d321da5dce 100644 --- a/Sming/options.json +++ b/Sming/options.json @@ -1,4 +1,20 @@ { + "1m": { + "description": "Set Flash size to 1MByte", + "devices": { + "spiFlash": { + "size": "1M" + } + } + }, + "2m": { + "description": "Set Flash size to 2MByte", + "devices": { + "spiFlash": { + "size": "2M" + } + } + }, "4m": { "description": "Set Flash size to 4MByte", "devices": { diff --git a/Sming/spiffs-2m.hw b/Sming/spiffs-2m.hw new file mode 100644 index 0000000000..eeed997118 --- /dev/null +++ b/Sming/spiffs-2m.hw @@ -0,0 +1,12 @@ +{ + "name": "Single SPIFFS partition (2M flash)", + "base_config": "spiffs", + "options": [ + "2m" + ], + "partitions": { + "spiffs0": { + "address": "0x100000" + } + } +} \ No newline at end of file From b0fcb8498a4ec9e4d35bb9f0e45d00e4e57475aa Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 11 Oct 2021 08:41:02 +0100 Subject: [PATCH 072/130] Move Network-related classes into Network Component (#2386) --- .../Network/src}/Data/Stream/Base64OutputStream.cpp | 0 .../Network/src}/Data/Stream/Base64OutputStream.h | 4 ++-- .../Network/src}/Data/Stream/ChunkedStream.cpp | 0 .../Network/src}/Data/Stream/ChunkedStream.h | 2 +- .../Network/src}/Data/Stream/MultipartStream.h | 4 ++-- .../Network/src}/Data/Stream/QuotedPrintableOutputStream.cpp | 0 .../Network/src}/Data/Stream/QuotedPrintableOutputStream.h | 2 +- .../Network/src/Data/Stream/UrlencodedOutputStream.h | 2 +- 8 files changed, 7 insertions(+), 7 deletions(-) rename Sming/{Core => Components/Network/src}/Data/Stream/Base64OutputStream.cpp (100%) rename Sming/{Core => Components/Network/src}/Data/Stream/Base64OutputStream.h (94%) rename Sming/{Core => Components/Network/src}/Data/Stream/ChunkedStream.cpp (100%) rename Sming/{Core => Components/Network/src}/Data/Stream/ChunkedStream.h (95%) rename Sming/{Core => Components/Network/src}/Data/Stream/MultipartStream.h (94%) rename Sming/{Core => Components/Network/src}/Data/Stream/QuotedPrintableOutputStream.cpp (100%) rename Sming/{Core => Components/Network/src}/Data/Stream/QuotedPrintableOutputStream.h (96%) diff --git a/Sming/Core/Data/Stream/Base64OutputStream.cpp b/Sming/Components/Network/src/Data/Stream/Base64OutputStream.cpp similarity index 100% rename from Sming/Core/Data/Stream/Base64OutputStream.cpp rename to Sming/Components/Network/src/Data/Stream/Base64OutputStream.cpp diff --git a/Sming/Core/Data/Stream/Base64OutputStream.h b/Sming/Components/Network/src/Data/Stream/Base64OutputStream.h similarity index 94% rename from Sming/Core/Data/Stream/Base64OutputStream.h rename to Sming/Components/Network/src/Data/Stream/Base64OutputStream.h index a81af415a7..865f680571 100644 --- a/Sming/Core/Data/Stream/Base64OutputStream.h +++ b/Sming/Components/Network/src/Data/Stream/Base64OutputStream.h @@ -12,8 +12,8 @@ #pragma once -#include "../StreamTransformer.h" -#include "libb64/cencode.h" +#include +#include /** * @brief Read-only stream to emit base64-encoded content from source stream diff --git a/Sming/Core/Data/Stream/ChunkedStream.cpp b/Sming/Components/Network/src/Data/Stream/ChunkedStream.cpp similarity index 100% rename from Sming/Core/Data/Stream/ChunkedStream.cpp rename to Sming/Components/Network/src/Data/Stream/ChunkedStream.cpp diff --git a/Sming/Core/Data/Stream/ChunkedStream.h b/Sming/Components/Network/src/Data/Stream/ChunkedStream.h similarity index 95% rename from Sming/Core/Data/Stream/ChunkedStream.h rename to Sming/Components/Network/src/Data/Stream/ChunkedStream.h index 18488796d9..70b9ea6039 100644 --- a/Sming/Core/Data/Stream/ChunkedStream.h +++ b/Sming/Components/Network/src/Data/Stream/ChunkedStream.h @@ -12,7 +12,7 @@ #pragma once -#include "../StreamTransformer.h" +#include /** * @brief Read-only stream to obtain data using HTTP chunked encoding diff --git a/Sming/Core/Data/Stream/MultipartStream.h b/Sming/Components/Network/src/Data/Stream/MultipartStream.h similarity index 94% rename from Sming/Core/Data/Stream/MultipartStream.h rename to Sming/Components/Network/src/Data/Stream/MultipartStream.h index 68fb88c87a..026e9f09a0 100644 --- a/Sming/Core/Data/Stream/MultipartStream.h +++ b/Sming/Components/Network/src/Data/Stream/MultipartStream.h @@ -12,8 +12,8 @@ #pragma once -#include "MultiStream.h" -#include "Network/Http/HttpHeaders.h" +#include +#include /** * @brief Read-only stream for creating HTTP multi-part content diff --git a/Sming/Core/Data/Stream/QuotedPrintableOutputStream.cpp b/Sming/Components/Network/src/Data/Stream/QuotedPrintableOutputStream.cpp similarity index 100% rename from Sming/Core/Data/Stream/QuotedPrintableOutputStream.cpp rename to Sming/Components/Network/src/Data/Stream/QuotedPrintableOutputStream.cpp diff --git a/Sming/Core/Data/Stream/QuotedPrintableOutputStream.h b/Sming/Components/Network/src/Data/Stream/QuotedPrintableOutputStream.h similarity index 96% rename from Sming/Core/Data/Stream/QuotedPrintableOutputStream.h rename to Sming/Components/Network/src/Data/Stream/QuotedPrintableOutputStream.h index bdd19da8c3..c78a2730d6 100644 --- a/Sming/Core/Data/Stream/QuotedPrintableOutputStream.h +++ b/Sming/Components/Network/src/Data/Stream/QuotedPrintableOutputStream.h @@ -12,7 +12,7 @@ #pragma once -#include "../StreamTransformer.h" +#include /** * @brief Read-only stream that transforms bytes of data into quoted printable data stream diff --git a/Sming/Components/Network/src/Data/Stream/UrlencodedOutputStream.h b/Sming/Components/Network/src/Data/Stream/UrlencodedOutputStream.h index bd32391a7b..66615fa0a9 100644 --- a/Sming/Components/Network/src/Data/Stream/UrlencodedOutputStream.h +++ b/Sming/Components/Network/src/Data/Stream/UrlencodedOutputStream.h @@ -13,7 +13,7 @@ #pragma once #include -#include "Network/Http/HttpParams.h" +#include /** * @brief Represents key-value pairs as urlencoded string content From aeab9d4360099f9578301fd4de21684a1d77b4e9 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 11 Oct 2021 08:42:04 +0100 Subject: [PATCH 073/130] Change uart settings values and use enums (#2387) --- Sming/Arch/Esp32/Components/driver/uart.cpp | 23 ++- .../arch_driver/src/include/driver/uart.h | 133 ++++++++++++------ 2 files changed, 102 insertions(+), 54 deletions(-) diff --git a/Sming/Arch/Esp32/Components/driver/uart.cpp b/Sming/Arch/Esp32/Components/driver/uart.cpp index 34752c9d76..f62ece0f5c 100644 --- a/Sming/Arch/Esp32/Components/driver/uart.cpp +++ b/Sming/Arch/Esp32/Components/driver/uart.cpp @@ -2,23 +2,22 @@ uart.cpp - esp32 UART HAL */ -#include - -#include -#include -#include -#include -#include - -// These conflict with enumerated types defined in IDF - values are same though +// #define typeof(x) std::remove_volatile::type +#define typeof(x) decltype(x) +// These conflict with enumerated types defined in IDF +#define UART_PARITY_NONE IDF_UART_PARITY_NONE +#define UART_PARITY_EVEN IDF_UART_PARITY_EVEN +#define UART_PARITY_ODD IDF_UART_PARITY_ODD +#include #undef UART_PARITY_NONE #undef UART_PARITY_EVEN #undef UART_PARITY_ODD -// #define typeof(x) std::remove_volatile::type -#define typeof(x) decltype(x) -#include +#include +#include #include +#include +#include namespace { diff --git a/Sming/Components/arch_driver/src/include/driver/uart.h b/Sming/Components/arch_driver/src/include/driver/uart.h index 9df0d09753..3d8877560f 100644 --- a/Sming/Components/arch_driver/src/include/driver/uart.h +++ b/Sming/Components/arch_driver/src/include/driver/uart.h @@ -53,48 +53,97 @@ extern "C" { #define UART_PIN_DEFAULT (255) ///< Use default pin assignments #define UART_PIN_NO_CHANGE (-1) ///< Use default pin assignments -// Options for `config` argument of uart_init -#define UART_NB_BIT_MASK 0B00001100 -#define UART_NB_BIT_5 0B00000000 -#define UART_NB_BIT_6 0B00000100 -#define UART_NB_BIT_7 0B00001000 -#define UART_NB_BIT_8 0B00001100 - -#define UART_PARITY_MASK 0B00000011 -#define UART_PARITY_NONE 0B00000000 -#define UART_PARITY_EVEN 0B00000010 -#define UART_PARITY_ODD 0B00000011 - -#define UART_NB_STOP_BIT_MASK 0B00110000 -#define UART_NB_STOP_BIT_0 0B00000000 -#define UART_NB_STOP_BIT_1 0B00010000 -#define UART_NB_STOP_BIT_15 0B00100000 -#define UART_NB_STOP_BIT_2 0B00110000 - -#define UART_5N1 (UART_NB_BIT_5 | UART_PARITY_NONE | UART_NB_STOP_BIT_1) -#define UART_6N1 (UART_NB_BIT_6 | UART_PARITY_NONE | UART_NB_STOP_BIT_1) -#define UART_7N1 (UART_NB_BIT_7 | UART_PARITY_NONE | UART_NB_STOP_BIT_1) -#define UART_8N1 (UART_NB_BIT_8 | UART_PARITY_NONE | UART_NB_STOP_BIT_1) -#define UART_5N2 (UART_NB_BIT_5 | UART_PARITY_NONE | UART_NB_STOP_BIT_2) -#define UART_6N2 (UART_NB_BIT_6 | UART_PARITY_NONE | UART_NB_STOP_BIT_2) -#define UART_7N2 (UART_NB_BIT_7 | UART_PARITY_NONE | UART_NB_STOP_BIT_2) -#define UART_8N2 (UART_NB_BIT_8 | UART_PARITY_NONE | UART_NB_STOP_BIT_2) -#define UART_5E1 (UART_NB_BIT_5 | UART_PARITY_EVEN | UART_NB_STOP_BIT_1) -#define UART_6E1 (UART_NB_BIT_6 | UART_PARITY_EVEN | UART_NB_STOP_BIT_1) -#define UART_7E1 (UART_NB_BIT_7 | UART_PARITY_EVEN | UART_NB_STOP_BIT_1) -#define UART_8E1 (UART_NB_BIT_8 | UART_PARITY_EVEN | UART_NB_STOP_BIT_1) -#define UART_5E2 (UART_NB_BIT_5 | UART_PARITY_EVEN | UART_NB_STOP_BIT_2) -#define UART_6E2 (UART_NB_BIT_6 | UART_PARITY_EVEN | UART_NB_STOP_BIT_2) -#define UART_7E2 (UART_NB_BIT_7 | UART_PARITY_EVEN | UART_NB_STOP_BIT_2) -#define UART_8E2 (UART_NB_BIT_8 | UART_PARITY_EVEN | UART_NB_STOP_BIT_2) -#define UART_5O1 (UART_NB_BIT_5 | UART_PARITY_ODD | UART_NB_STOP_BIT_1) -#define UART_6O1 (UART_NB_BIT_6 | UART_PARITY_ODD | UART_NB_STOP_BIT_1) -#define UART_7O1 (UART_NB_BIT_7 | UART_PARITY_ODD | UART_NB_STOP_BIT_1) -#define UART_8O1 (UART_NB_BIT_8 | UART_PARITY_ODD | UART_NB_STOP_BIT_1) -#define UART_5O2 (UART_NB_BIT_5 | UART_PARITY_ODD | UART_NB_STOP_BIT_2) -#define UART_6O2 (UART_NB_BIT_6 | UART_PARITY_ODD | UART_NB_STOP_BIT_2) -#define UART_7O2 (UART_NB_BIT_7 | UART_PARITY_ODD | UART_NB_STOP_BIT_2) -#define UART_8O2 (UART_NB_BIT_8 | UART_PARITY_ODD | UART_NB_STOP_BIT_2) +/** + * @brief Number of UART data bits + */ +enum smg_uart_bits_t { + UART_NB_BIT_5 = 0, + UART_NB_BIT_6 = 1, + UART_NB_BIT_7 = 2, + UART_NB_BIT_8 = 3, +}; + +/** + * @brief UART parity setting + * + * Actually combines two bits: + * bit 1 is set for parity enable, clear if disabled + * bit 0 is set for even parity, clear for odd parity + */ +enum smg_uart_parity_t { + UART_PARITY_NONE = 0, + UART_PARITY_EVEN = 2, + UART_PARITY_ODD = 3, +}; + +/** + * @brief Number of UART stop bits + */ +enum smg_uart_stop_bits_t { + UART_NB_STOP_BIT_0 = 0, + UART_NB_STOP_BIT_1 = 1, + UART_NB_STOP_BIT_15 = 2, + UART_NB_STOP_BIT_2 = 3, +}; + +enum smg_uart_format_settings_t { + UART_PARITY_MASK = 0B00000011, + UART_PARITY_SHIFT = 0, + UART_NB_BIT_MASK = 0B00001100, + UART_NB_BIT_SHIFT = 2, + UART_NB_STOP_BIT_MASK = 0B00110000, + UART_NB_STOP_BIT_SHIFT = 4, +}; + +static inline constexpr uint8_t SMG_UART_FORMAT(smg_uart_bits_t databits, smg_uart_stop_bits_t stopbits, + smg_uart_parity_t parity) +{ + return (databits << UART_NB_BIT_SHIFT) | (stopbits << UART_NB_STOP_BIT_SHIFT) | (parity << UART_PARITY_SHIFT); +} + +/** + * @brief Structure for easier decomposing of `config` value + * + * Used by drivers to read config values + */ +union smg_uart_config_format_t { + struct { + smg_uart_parity_t parity : 2; + smg_uart_bits_t bits : 2; + smg_uart_stop_bits_t stop_bits : 2; + }; + uint8_t val; +}; + +/** + * @brief Options for `config` argument of uart_init + */ +enum smg_uart_format_t { + UART_5N1 = SMG_UART_FORMAT(UART_NB_BIT_5, UART_NB_STOP_BIT_1, UART_PARITY_NONE), + UART_6N1 = SMG_UART_FORMAT(UART_NB_BIT_6, UART_NB_STOP_BIT_1, UART_PARITY_NONE), + UART_7N1 = SMG_UART_FORMAT(UART_NB_BIT_7, UART_NB_STOP_BIT_1, UART_PARITY_NONE), + UART_8N1 = SMG_UART_FORMAT(UART_NB_BIT_8, UART_NB_STOP_BIT_1, UART_PARITY_NONE), + UART_5N2 = SMG_UART_FORMAT(UART_NB_BIT_5, UART_NB_STOP_BIT_2, UART_PARITY_NONE), + UART_6N2 = SMG_UART_FORMAT(UART_NB_BIT_6, UART_NB_STOP_BIT_2, UART_PARITY_NONE), + UART_7N2 = SMG_UART_FORMAT(UART_NB_BIT_7, UART_NB_STOP_BIT_2, UART_PARITY_NONE), + UART_8N2 = SMG_UART_FORMAT(UART_NB_BIT_8, UART_NB_STOP_BIT_2, UART_PARITY_NONE), + UART_5E1 = SMG_UART_FORMAT(UART_NB_BIT_5, UART_NB_STOP_BIT_1, UART_PARITY_EVEN), + UART_6E1 = SMG_UART_FORMAT(UART_NB_BIT_6, UART_NB_STOP_BIT_1, UART_PARITY_EVEN), + UART_7E1 = SMG_UART_FORMAT(UART_NB_BIT_7, UART_NB_STOP_BIT_1, UART_PARITY_EVEN), + UART_8E1 = SMG_UART_FORMAT(UART_NB_BIT_8, UART_NB_STOP_BIT_1, UART_PARITY_EVEN), + UART_5E2 = SMG_UART_FORMAT(UART_NB_BIT_5, UART_NB_STOP_BIT_2, UART_PARITY_EVEN), + UART_6E2 = SMG_UART_FORMAT(UART_NB_BIT_6, UART_NB_STOP_BIT_2, UART_PARITY_EVEN), + UART_7E2 = SMG_UART_FORMAT(UART_NB_BIT_7, UART_NB_STOP_BIT_2, UART_PARITY_EVEN), + UART_8E2 = SMG_UART_FORMAT(UART_NB_BIT_8, UART_NB_STOP_BIT_2, UART_PARITY_EVEN), + UART_5O1 = SMG_UART_FORMAT(UART_NB_BIT_5, UART_NB_STOP_BIT_1, UART_PARITY_ODD), + UART_6O1 = SMG_UART_FORMAT(UART_NB_BIT_6, UART_NB_STOP_BIT_1, UART_PARITY_ODD), + UART_7O1 = SMG_UART_FORMAT(UART_NB_BIT_7, UART_NB_STOP_BIT_1, UART_PARITY_ODD), + UART_8O1 = SMG_UART_FORMAT(UART_NB_BIT_8, UART_NB_STOP_BIT_1, UART_PARITY_ODD), + UART_5O2 = SMG_UART_FORMAT(UART_NB_BIT_5, UART_NB_STOP_BIT_2, UART_PARITY_ODD), + UART_6O2 = SMG_UART_FORMAT(UART_NB_BIT_6, UART_NB_STOP_BIT_2, UART_PARITY_ODD), + UART_7O2 = SMG_UART_FORMAT(UART_NB_BIT_7, UART_NB_STOP_BIT_2, UART_PARITY_ODD), + UART_8O2 = SMG_UART_FORMAT(UART_NB_BIT_8, UART_NB_STOP_BIT_2, UART_PARITY_ODD), +}; // Status values enum smg_uart_status_t { From db49926912dd668e5624ef5361b78b6753094e38 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 11 Oct 2021 08:46:26 +0100 Subject: [PATCH 074/130] Rationalise compiler flags and esp32 improvements (#2388) Fix build inconsistencies and move common settings from arch. into main Sming to reduce code duplication. - Rationalise compiler flags, add COMPONENT_CPPFLAGS - Add SMING_C_STD to debug vars - Get rid of obsolete Esp32 build settings (these related to pre-CMake builds) - Add `Undef`, `UndefWrap`, `Wrap`, `DefSym` linker function - Split Esp32 linker settings out. Use one file per esp32 component to simplify tracking settings and changes with IDF. - Simplify meminfo, rename as `fwMeminfo.txt` and remove old/saved - Wrap esp32 printf functions so they don't get pulled in from C library. Saves about 60K flash (and a little RAM). --- Sming/Arch/Esp32/Components/driver/uart.cpp | 5 - .../Arch/Esp32/Components/esp32/component.mk | 39 ++--- .../Esp32/Components/esp32/{sdk => }/misc.mk | 0 .../Esp32/Components/esp32/sdk/app_update.mk | 7 + Sming/Arch/Esp32/Components/esp32/sdk/cxx.mk | 57 +++++++ .../Arch/Esp32/Components/esp32/sdk/esp32.mk | 5 + .../Esp32/Components/esp32/sdk/esp_rom.mk | 6 + .../Esp32/Components/esp32/sdk/esp_system.mk | 18 ++ .../Esp32/Components/esp32/sdk/freertos.mk | 7 + Sming/Arch/Esp32/Components/esp32/sdk/heap.mk | 16 ++ .../Arch/Esp32/Components/esp32/sdk/newlib.mk | 13 ++ .../Esp32/Components/esp32/sdk/pthread.mk | 11 ++ .../esp32/src/include/esp_systemapi.h | 7 +- Sming/Arch/Esp32/Components/libc/component.mk | 19 ++- .../Esp32/Components/libc/src/replacements.c | 61 ++++++- .../Esp32/Components/spi_flash/flashmem.cpp | 2 +- .../spi_flash/include/esp_spi_flash.h | 7 +- .../spi_flash/include/iram_precache.h | 20 --- Sming/Arch/Esp32/app.mk | 20 +-- Sming/Arch/Esp32/build.mk | 160 ++---------------- Sming/Arch/Esp8266/app.mk | 21 +-- Sming/Arch/Esp8266/build.mk | 7 +- Sming/Arch/Host/app.mk | 4 +- Sming/Components/Storage/component.mk | 1 - Sming/Components/malloc_count/component.mk | 2 +- Sming/System/include/m_printf.h | 5 +- Sming/System/m_printf.cpp | 4 + Sming/build.mk | 36 +++- Sming/building.rst | 5 +- Sming/component-wrapper.mk | 2 + Sming/project.mk | 6 +- 31 files changed, 306 insertions(+), 267 deletions(-) rename Sming/Arch/Esp32/Components/esp32/{sdk => }/misc.mk (100%) create mode 100644 Sming/Arch/Esp32/Components/esp32/sdk/app_update.mk create mode 100644 Sming/Arch/Esp32/Components/esp32/sdk/cxx.mk create mode 100644 Sming/Arch/Esp32/Components/esp32/sdk/esp32.mk create mode 100644 Sming/Arch/Esp32/Components/esp32/sdk/esp_rom.mk create mode 100644 Sming/Arch/Esp32/Components/esp32/sdk/esp_system.mk create mode 100644 Sming/Arch/Esp32/Components/esp32/sdk/freertos.mk create mode 100644 Sming/Arch/Esp32/Components/esp32/sdk/heap.mk create mode 100644 Sming/Arch/Esp32/Components/esp32/sdk/newlib.mk create mode 100644 Sming/Arch/Esp32/Components/esp32/sdk/pthread.mk diff --git a/Sming/Arch/Esp32/Components/driver/uart.cpp b/Sming/Arch/Esp32/Components/driver/uart.cpp index f62ece0f5c..a25e220ff0 100644 --- a/Sming/Arch/Esp32/Components/driver/uart.cpp +++ b/Sming/Arch/Esp32/Components/driver/uart.cpp @@ -131,11 +131,6 @@ void notify(smg_uart_t* uart, smg_uart_notify_code_t code) } } -__forceinline bool uart_isr_enabled(uint8_t nr) -{ - return bitRead(isrMask, nr); -} - /** @brief Determine if the given uart is a real uart or a virtual one */ __forceinline bool is_physical(int uart_nr) diff --git a/Sming/Arch/Esp32/Components/esp32/component.mk b/Sming/Arch/Esp32/Components/esp32/component.mk index 002f489da1..29893fb9ad 100644 --- a/Sming/Arch/Esp32/Components/esp32/component.mk +++ b/Sming/Arch/Esp32/Components/esp32/component.mk @@ -24,12 +24,15 @@ SDK_LIBDIRS := \ $(ESP_VARIANT)/ld \ esp_rom/$(ESP_VARIANT)/ld +ESP32_COMPONENT_PATH := $(COMPONENT_PATH) +SDK_DEFAULT_PATH := $(ESP32_COMPONENT_PATH)/sdk + LIBDIRS += \ $(SDK_COMPONENT_LIBDIR) \ $(SDK_BUILD_BASE)/esp-idf/mbedtls/mbedtls/library \ $(SDK_BUILD_BASE)/esp-idf/$(ESP_VARIANT) \ $(SDK_BUILD_BASE)/esp-idf/$(ESP_VARIANT)/ld \ - $(COMPONENT_PATH)/ld \ + $(ESP32_COMPONENT_PATH)/ld \ $(addprefix $(SDK_COMPONENTS_PATH)/,$(SDK_LIBDIRS)) SDK_INCDIRS := \ @@ -151,11 +154,6 @@ ifeq ($(ESP_VARIANT),esp32) SDK_ESP_WIFI_LIBS += rtc endif -SDK_NEWLIB_LIBS := \ - c \ - m \ - stdc++ - ifdef IDF_TARGET_ARCH_RISCV SDK_TARGET_ARCH_LIBS := hal else @@ -163,9 +161,7 @@ SDK_TARGET_ARCH_LIBS := hal xt_hal endif EXTRA_LIBS := \ - gcc \ $(SDK_COMPONENTS) \ - $(SDK_NEWLIB_LIBS) \ $(SDK_TARGET_ARCH_LIBS) ifneq ($(DISABLE_WIFI),1) @@ -196,23 +192,18 @@ LDFLAGS_esp32s3 := \ $(call LinkerScript,rom.newlib-data) \ $(call LinkerScript,rom.spiflash) +SDK_WRAP_SYMBOLS := +SDK_UNDEF_SYMBOLS := + +$(foreach c,$(wildcard $(SDK_DEFAULT_PATH)/*.mk),$(eval include $c)) + EXTRA_LDFLAGS := \ - -u esp_app_desc \ - -u __cxa_guard_dummy -u __cxx_fatal_exception \ -T $(ESP_VARIANT)_out.ld \ - -u ld_include_panic_highint_hdl \ $(call LinkerScript,project) \ $(call LinkerScript,peripherals) \ $(call LinkerScript,rom) \ $(call LinkerScript,rom.api) \ $(call LinkerScript,rom.libgcc) \ - -u newlib_include_locks_impl \ - -u newlib_include_heap_impl \ - -u newlib_include_syscalls_impl \ - -u pthread_include_pthread_impl \ - -u pthread_include_pthread_cond_impl \ - -u pthread_include_pthread_local_storage_impl \ - -Wl,--undefined=uxTopUsedPriority \ $(call Wrap,\ esp_event_loop_create_default \ esp_event_handler_register \ @@ -221,10 +212,12 @@ EXTRA_LDFLAGS := \ esp_event_handler_instance_unregister \ esp_event_post \ esp_event_isr_post) \ - $(LDFLAGS_$(ESP_VARIANT)) + $(LDFLAGS_$(ESP_VARIANT)) \ + $(call Undef,$(SDK_UNDEF_SYMBOLS)) \ + $(call Wrap,$(SDK_WRAP_SYMBOLS)) + -SDK_DEFAULT_PATH := $(COMPONENT_PATH)/sdk -SDK_PROJECT_PATH := $(COMPONENT_PATH)/project/$(ESP_VARIANT)/$(BUILD_TYPE) +SDK_PROJECT_PATH := $(ESP32_COMPONENT_PATH)/project/$(ESP_VARIANT)/$(BUILD_TYPE) SDK_CONFIG_DEFAULTS := $(SDK_PROJECT_PATH)/sdkconfig.defaults SDKCONFIG_MAKEFILE := $(SDK_PROJECT_PATH)/sdkconfig @@ -253,7 +246,7 @@ CUSTOM_TARGETS += checksdk .PHONY: checksdk checksdk: $(SDK_PROJECT_PATH) $(SDKCONFIG_H) $(SDKCONFIG_MAKEFILE) $(Q) $(NINJA) -C $(SDK_BUILD_BASE) bootloader app - $(Q) $(MAKE) --no-print-directory -C $(SDK_DEFAULT_PATH) -f misc.mk copylibs + $(Q) $(MAKE) --no-print-directory -C $(ESP32_COMPONENT_PATH) -f misc.mk copylibs $(SDKCONFIG_H) $(SDKCONFIG_MAKEFILE) $(SDK_COMPONENT_LIBS): $(SDK_PROJECT_PATH) $(SDK_CONFIG_DEFAULTS) | $(SDK_BUILD_BASE) $(SDK_COMPONENT_LIBDIR) $(Q) $(SDK_BUILD) reconfigure @@ -312,4 +305,4 @@ sdk: ##Pass options to IDF builder, e.g. `make sdk -- --help` or `make sdk menuc .PHONY: checkdirs checkdirs: | checksdk - + diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/misc.mk b/Sming/Arch/Esp32/Components/esp32/misc.mk similarity index 100% rename from Sming/Arch/Esp32/Components/esp32/sdk/misc.mk rename to Sming/Arch/Esp32/Components/esp32/misc.mk diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/app_update.mk b/Sming/Arch/Esp32/Components/esp32/sdk/app_update.mk new file mode 100644 index 0000000000..c70fd37ebe --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/sdk/app_update.mk @@ -0,0 +1,7 @@ +# +# app_update +# + +# esp_app_desc structure is added as an undefined symbol because otherwise the +# linker will ignore this structure as it has no other files depending on it. +SDK_UNDEF_SYMBOLS += esp_app_desc diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/cxx.mk b/Sming/Arch/Esp32/Components/esp32/sdk/cxx.mk new file mode 100644 index 0000000000..cd150b4cb6 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/sdk/cxx.mk @@ -0,0 +1,57 @@ +# +# cxx +# + +ifndef CONFIG_COMPILER_CXX_EXCEPTIONS +# If exceptions are disabled, ensure our fatal exception +# hooks are preferentially linked over libstdc++ which +# has full exception support +SDK_WRAP_SYMBOLS += \ + _Unwind_SetEnableExceptionFdeSorting \ + __register_frame_info_bases \ + __register_frame_info \ + __register_frame \ + __register_frame_info_table_bases \ + __register_frame_info_table \ + __register_frame_table \ + __deregister_frame_info_bases \ + __deregister_frame_info \ + _Unwind_Find_FDE \ + _Unwind_GetGR \ + _Unwind_GetCFA \ + _Unwind_GetIP \ + _Unwind_GetIPInfo \ + _Unwind_GetRegionStart \ + _Unwind_GetDataRelBase \ + _Unwind_GetTextRelBase \ + _Unwind_SetIP \ + _Unwind_SetGR \ + _Unwind_GetLanguageSpecificData \ + _Unwind_FindEnclosingFunction \ + _Unwind_Resume \ + _Unwind_RaiseException \ + _Unwind_DeleteException \ + _Unwind_ForcedUnwind \ + _Unwind_Resume_or_Rethrow \ + _Unwind_Backtrace \ + __cxa_call_unexpected \ + __gxx_personality_v0 + +SDK_UNDEF_SYMBOLS += __cxx_fatal_exception +endif + +SDK_UNDEF_SYMBOLS += __cxa_guard_dummy + +# Force libpthread to appear later than libstdc++ in link line since libstdc++ depends on libpthread. +# Furthermore, force libcxx to appear later than libgcc because some libgcc unwind code is wrapped, if C++ +# exceptions are disabled. libcxx (this component) provides the unwind code wrappers. +# This is to prevent linking of libgcc's unwind code which considerably increases the binary size. + +# idf_component_get_property(pthread pthread COMPONENT_LIB) +# idf_component_get_property(cxx cxx COMPONENT_LIB) +# add_library(stdcpp_pthread INTERFACE) +# target_link_libraries(stdcpp_pthread INTERFACE stdc++ $) +# target_link_libraries(${COMPONENT_LIB} PUBLIC stdcpp_pthread) +# add_library(libgcc_cxx INTERFACE) +# target_link_libraries(libgcc_cxx INTERFACE gcc $) +# target_link_libraries(${COMPONENT_LIB} PUBLIC libgcc_cxx) diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/esp32.mk b/Sming/Arch/Esp32/Components/esp32/sdk/esp32.mk new file mode 100644 index 0000000000..84ac656dd0 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/sdk/esp32.mk @@ -0,0 +1,5 @@ +# +# esp32 +# + +SDK_UNDEF_SYMBOLS += call_user_start_cpu0 diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/esp_rom.mk b/Sming/Arch/Esp32/Components/esp32/sdk/esp_rom.mk new file mode 100644 index 0000000000..ed4f2b64c5 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/sdk/esp_rom.mk @@ -0,0 +1,6 @@ +# +# esp_rom +# +ifdef CONFIG_IDF_TARGET_ARCH_XTENSA +SDK_WRAP_SYMBOLS += longjmp +endif diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/esp_system.mk b/Sming/Arch/Esp32/Components/esp32/sdk/esp_system.mk new file mode 100644 index 0000000000..1d7f1f8726 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/sdk/esp_system.mk @@ -0,0 +1,18 @@ +# +# esp_system +# + +# After system initialization, `start_app` (and its other cores variant) is called. +# This is provided by the user or from another component. Since we can't establish +# dependency on what we don't know, force linker to not drop the symbol regardless +# of link line order. +SDK_UNDEF_SYMBOLS += start_app + +ifndef CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE +SDK_UNDEF_SYMBOLS += start_app_other_cores +endif + +# ld_include_panic_highint_hdl is added as an undefined symbol because otherwise the +# linker will ignore panic_highint_hdl.S as it has no other files depending on any +# symbols in it. +SDK_UNDEF_SYMBOLS += ld_include_panic_highint_hdl diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/freertos.mk b/Sming/Arch/Esp32/Components/esp32/sdk/freertos.mk new file mode 100644 index 0000000000..8d658211a5 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/sdk/freertos.mk @@ -0,0 +1,7 @@ +# +# freertos +# + +ifdef CONFIG_FREERTOS_DEBUG_OCDAWARE +EXTRA_LDFLAGS += -Wl,--undefined=uxTopUsedPriority +endif diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/heap.mk b/Sming/Arch/Esp32/Components/esp32/sdk/heap.mk new file mode 100644 index 0000000000..b16d826602 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/sdk/heap.mk @@ -0,0 +1,16 @@ +# +# heap +# + +ifdef CONFIG_HEAP_TRACING +SDK_WRAP_SYMBOLS += \ + calloc \ + malloc \ + free \ + realloc \ + heap_caps_malloc \ + heap_caps_free \ + heap_caps_realloc \ + heap_caps_malloc_default \ + heap_caps_realloc_default +endif diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/newlib.mk b/Sming/Arch/Esp32/Components/esp32/sdk/newlib.mk new file mode 100644 index 0000000000..75613e5733 --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/sdk/newlib.mk @@ -0,0 +1,13 @@ +# +# newlib +# + +# Forces the linker to include heap, syscall, pthread and retargetable locks from this component, +# instead of the implementations provided by newlib. +SDK_UNDEF_SYMBOLS += \ + newlib_include_heap_impl \ + newlib_include_syscalls_impl \ + newlib_include_pthread_impl + +# Must link before standard C library +LIBS := newlib $(LIBS) diff --git a/Sming/Arch/Esp32/Components/esp32/sdk/pthread.mk b/Sming/Arch/Esp32/Components/esp32/sdk/pthread.mk new file mode 100644 index 0000000000..c24e62cb1e --- /dev/null +++ b/Sming/Arch/Esp32/Components/esp32/sdk/pthread.mk @@ -0,0 +1,11 @@ +# +# pthread +# +SDK_UNDEF_SYMBOLS += \ + pthread_include_pthread_impl \ + pthread_include_pthread_cond_impl \ + pthread_include_pthread_local_storage_impl + +ifdef CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP +SDK_WRAP_SYMBOLS += vPortCleanUpTCB +endif diff --git a/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h b/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h index 89482e7071..9b81061be4 100644 --- a/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h +++ b/Sming/Arch/Esp32/Components/esp32/src/include/esp_systemapi.h @@ -10,11 +10,6 @@ #pragma once -// ==== taken from ESP8266/ets_sys.h -#define ETS_GPIO_INUM 4 - -// ==== - // Default types #include #include @@ -38,7 +33,7 @@ #include #include -#define __ESP32_EX__ // System definition ESP8266 SOC +#define __ESP32_EX__ // System definition ESP32 SOC #define LOCAL static diff --git a/Sming/Arch/Esp32/Components/libc/component.mk b/Sming/Arch/Esp32/Components/libc/component.mk index 3980983c81..065d909a37 100644 --- a/Sming/Arch/Esp32/Components/libc/component.mk +++ b/Sming/Arch/Esp32/Components/libc/component.mk @@ -3,4 +3,21 @@ COMPONENT_INCDIRS := src/include COMPONENT_DOXYGEN_INPUT := src/include/sys -EXTRA_LDFLAGS := $(call Wrap,_write_r _read_r) +LIBC_WRAPSYMS := \ + _write_r \ + _read_r \ + putchar \ + puts \ + vprintf \ + printf \ + vsnprintf \ + vsprintf \ + sprintf + +EXTRA_LDFLAGS := $(call Wrap,$(LIBC_WRAPSYMS)) + +EXTRA_LIBS := \ + c \ + m \ + gcc \ + stdc++ diff --git a/Sming/Arch/Esp32/Components/libc/src/replacements.c b/Sming/Arch/Esp32/Components/libc/src/replacements.c index 04d972852b..768f09ee11 100644 --- a/Sming/Arch/Esp32/Components/libc/src/replacements.c +++ b/Sming/Arch/Esp32/Components/libc/src/replacements.c @@ -6,14 +6,17 @@ #include #include -ssize_t __wrap__write_r(struct _reent* r, int fd, const void* data, size_t size) +#define BUFMAX 16384 +#define WRAP(x) __wrap_##x + +ssize_t WRAP(_write_r)(struct _reent* r, int fd, const void* data, size_t size) { (void)r; (void)fd; // Ignore, direct everything return m_nputs(data, size); } -ssize_t __wrap__read_r(struct _reent* r, int fd, void* dst, size_t size) +ssize_t WRAP(_read_r)(struct _reent* r, int fd, void* dst, size_t size) { (void)r; (void)fd; @@ -22,3 +25,57 @@ ssize_t __wrap__read_r(struct _reent* r, int fd, void* dst, size_t size) errno = ENOSYS; return -1; } + +size_t WRAP(putc)(char c) +{ + return m_putc(c); +} + +size_t WRAP(puts)(const char* str) +{ + return m_puts(str); +} + +int WRAP(vprintf)(const char* format, va_list arg) +{ + return m_vprintf(format, arg); +} + +int WRAP(printf)(char const* fmt, ...) +{ + va_list args; + va_start(args, fmt); + int n = m_vprintf(fmt, args); + va_end(args); + return n; +} + +int WRAP(vsnprintf)(char* buf, size_t maxLen, const char* fmt, va_list args) +{ + return m_vsnprintf(buf, maxLen, fmt, args); +} + +int WRAP(snprintf)(char* buf, int length, const char* fmt, ...) +{ + va_list args; + + va_start(args, fmt); + int n = m_vsnprintf(buf, length, fmt, args); + va_end(args); + + return n; +} + +int WRAP(vsprintf)(char* buf, const char* fmt, va_list args) +{ + return m_vsnprintf(buf, BUFMAX, fmt, args); +} + +int WRAP(sprintf)(char* buf, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + int n = m_vsnprintf(buf, BUFMAX, fmt, args); + va_end(args); + return n; +} diff --git a/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp b/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp index 76f01d16c0..79a2f02f94 100644 --- a/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp +++ b/Sming/Arch/Esp32/Components/spi_flash/flashmem.cpp @@ -39,7 +39,7 @@ uint32_t flashmem_read(void* to, uint32_t fromaddr, uint32_t size) bool flashmem_erase_sector(uint32_t sector_id) { - debug_e("flashmem_erase_sector(0x%08x)", sector_id); + debug_d("flashmem_erase_sector(0x%08x)", sector_id); return spi_flash_erase_sector(sector_id) == SPI_FLASH_RESULT_OK; } diff --git a/Sming/Arch/Esp32/Components/spi_flash/include/esp_spi_flash.h b/Sming/Arch/Esp32/Components/spi_flash/include/esp_spi_flash.h index f4b34017ef..331a9ddc5d 100644 --- a/Sming/Arch/Esp32/Components/spi_flash/include/esp_spi_flash.h +++ b/Sming/Arch/Esp32/Components/spi_flash/include/esp_spi_flash.h @@ -151,10 +151,13 @@ uint32_t flashmem_find_sector(uint32_t address, uint32_t* pstart, uint32_t* pend */ uint32_t flashmem_get_sector_of_address(uint32_t addr); -/** @} */ - +/* + * @brief Get unique 32-bit flash identification code + */ uint32_t spi_flash_get_id(void); +/** @} */ + #ifdef __cplusplus } #endif diff --git a/Sming/Arch/Esp32/Components/spi_flash/include/iram_precache.h b/Sming/Arch/Esp32/Components/spi_flash/include/iram_precache.h index ecebfa0ca2..e48a605ba0 100644 --- a/Sming/Arch/Esp32/Components/spi_flash/include/iram_precache.h +++ b/Sming/Arch/Esp32/Components/spi_flash/include/iram_precache.h @@ -2,8 +2,6 @@ * Dummy stub. ESP32 has tons of IRAM. */ -#include - #pragma once /** @@ -16,22 +14,4 @@ #define IRAM_PRECACHE_START(tag) #define IRAM_PRECACHE_END(tag) -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Pre-load flash data into the flash instruction cache - * @param addr First location to cache, specify NULL to use current location. - * @param bytes Number of bytes to cache - * @note All pages containing the requested region will be read to pull them into cache RAM. - */ -inline void iram_precache(void* addr, uint32_t bytes) -{ -} - /** @} */ - -#ifdef __cplusplus -} -#endif diff --git a/Sming/Arch/Esp32/app.mk b/Sming/Arch/Esp32/app.mk index db6ca31cbf..a2ffcaa001 100644 --- a/Sming/Arch/Esp32/app.mk +++ b/Sming/Arch/Esp32/app.mk @@ -16,24 +16,8 @@ application: $(TARGET_BIN) $(TARGET_OUT): $(COMPONENTS_AR) $(info $(notdir $(PROJECT_DIR)): Linking $@) $(Q) $(LD) $(addprefix -L,$(LIBDIRS)) $(LDFLAGS) -Wl,--start-group $(COMPONENTS_AR) $(addprefix -l,$(LIBS)) -Wl,--end-group -o $@ - - $(Q) $(MEMANALYZER) $@ > $(FW_MEMINFO_NEW) - - $(Q) if [ -f "$(FW_MEMINFO_NEW)" -a -f "$(FW_MEMINFO_OLD)" ]; then \ - $(AWK) -F "|" ' \ - FILENAME == "$(FW_MEMINFO_OLD)" { \ - arr[$$1]=$$5 \ - } \ - FILENAME == "$(FW_MEMINFO_NEW)" { \ - if (arr[$$1] != $$5) { \ - printf "%s%s%+d%s", substr($$0, 1, length($$0) - 1)," (",$$5 - arr[$$1],")\n" \ - } else { \ - print $$0 \ - } \ - }' $(FW_MEMINFO_OLD) $(FW_MEMINFO_NEW); \ - elif [ -f "$(FW_MEMINFO_NEW)" ]; then \ - cat $(FW_MEMINFO_NEW); \ - fi + $(Q) $(MEMANALYZER) $@ > $(FW_MEMINFO) + $(Q) cat $(FW_MEMINFO) CHIP_REV_MIN := $(CONFIG_$(call ToUpper,$(ESP_VARIANT))_REV_MIN) ifeq ($(CHIP_REV_MIN),) diff --git a/Sming/Arch/Esp32/build.mk b/Sming/Arch/Esp32/build.mk index 3356671ea9..0092030400 100644 --- a/Sming/Arch/Esp32/build.mk +++ b/Sming/Arch/Esp32/build.mk @@ -131,174 +131,40 @@ endif IDF_VER := $(shell echo "$(IDF_VER_T)" | cut -c 1-31) # [ Sming specific flags ] -DEBUG_VARS += IDF_PATH IDF_VER +DEBUG_VARS += IDF_PATH IDF_VER -# Set default LDFLAGS -EXTRA_LDFLAGS ?= -LDFLAGS ?= -nostdlib \ - -u call_user_start_cpu0 \ - $(EXTRA_LDFLAGS) \ - -Wl,--gc-sections \ - -Wl,-static \ - -Wl,--start-group \ - $(COMPONENT_LDFLAGS) \ - -lgcc \ - -lstdc++ \ - -lgcov \ - -Wl,--end-group \ - -Wl,-EL - -SMING_C_STD := gnu99 +# IDF uses 'asm' keyword +SMING_C_STD := gnu11 -# Set default CPPFLAGS, CFLAGS, CXXFLAGS -# These are exported so that components can use them when compiling. -# If you need your component to add CFLAGS/etc for it's own source compilation only, set CFLAGS += in your component's Makefile. -# If you need your component to add CFLAGS/etc globally for all source -# files, set CFLAGS += in your component's Makefile.projbuild -# If you need to set CFLAGS/CPPFLAGS/CXXFLAGS at project level, set them in application Makefile -# before including project.mk. Default flags will be added before the ones provided in application Makefile. - -# This variable stores the common C/C++ flags -# CPPFLAGS used by C preprocessor -# If any flags are defined in application Makefile, add them at the end. +# Common C/C++ flags CPPFLAGS += \ -DESP_PLATFORM \ - -D IDF_VER=\"$(IDF_VER)\" \ + -DIDF_VER=\"$(IDF_VER)\" \ -MMD \ -MP \ - $(EXTRA_CPPFLAGS) - -# Sming specific CPPFLAGS -CPPFLAGS += \ + $(EXTRA_CPPFLAGS) \ -DARCH_ESP32 \ -D__ets__ \ - -DICACHE_FLASH \ - -DUSE_OPTIMIZE_PRINTF \ - -DESP32 + -D_GNU_SOURCE \ + -DCONFIG_NONE_OS PROJECT_VER ?= export IDF_VER export PROJECT_NAME export PROJECT_VER -# Warnings-related flags relevant both for C and C++ -COMMON_WARNING_FLAGS := -Wall -Werror=all \ - -Wno-error=unused-function \ - -Wno-error=unused-but-set-variable \ - -Wno-error=unused-variable \ - -Wno-error=deprecated-declarations \ - -Wno-error=extra \ - -Wno-unused-parameter -Wno-error=sign-compare \ - -Wno-error=ignored-qualifiers \ - -Wno-error=missing-field-initializers \ - -Wno-error=implicit-fallthrough - -# Sming warning.... -COMMON_WARNING_FLAGS += -Wno-error=undef - -ifdef CONFIG_COMPILER_DISABLE_GCC8_WARNINGS -COMMON_WARNING_FLAGS += -Wno-parentheses \ - -Wno-sizeof-pointer-memaccess \ - -Wno-clobbered \ - -Wno-format-overflow \ - -Wno-stringop-truncation \ - -Wno-misleading-indentation \ - -Wno-cast-function-type \ - -Wno-implicit-fallthrough \ - -Wno-unused-const-variable \ - -Wno-switch-unreachable \ - -Wno-format-truncation \ - -Wno-memset-elt-size \ - -Wno-int-in-bool-context -endif - -ifdef CONFIG_COMPILER_WARN_WRITE_STRINGS -COMMON_WARNING_FLAGS += -Wwrite-strings -endif #CONFIG_COMPILER_WARN_WRITE_STRINGS - # Flags which control code generation and dependency generation, both for C and C++ -COMMON_FLAGS := \ - -Wno-frame-address \ - -ffunction-sections -fdata-sections \ - -fstrict-volatile-bitfields \ - -nostdlib +CPPFLAGS += -Wno-frame-address -ifdef IDF_TARGET_ARCH_RISCV -COMMON_FLAGS += -DIDF_TARGET_ARCH_RISCV=1 -else -COMMON_FLAGS += \ +ifndef IDF_TARGET_ARCH_RISCV +CPPFLAGS += \ -mlongcalls \ -mtext-section-literals endif -ifndef IS_BOOTLOADER_BUILD -# stack protection (only one option can be selected in menuconfig) -ifdef CONFIG_COMPILER_STACK_CHECK_MODE_NORM -COMMON_FLAGS += -fstack-protector -endif -ifdef CONFIG_COMPILER_STACK_CHECK_MODE_STRONG -COMMON_FLAGS += -fstack-protector-strong -endif -ifdef CONFIG_COMPILER_STACK_CHECK_MODE_ALL -COMMON_FLAGS += -fstack-protector-all -endif -endif - -# Optimization flags are set based on menuconfig choice -ifdef CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE -OPTIMIZATION_FLAGS = -Os -freorder-blocks -else -OPTIMIZATION_FLAGS = -Og +ifeq ($(SMING_RELEASE),1) +CPPFLAGS += -freorder-blocks endif -ifdef CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE -CPPFLAGS += -DNDEBUG -endif - -# IDF uses some GNU extension from libc -CPPFLAGS += -D_GNU_SOURCE - -CPPFLAGS += -DCONFIG_NONE_OS - -# Enable generation of debugging symbols -# (we generate even in Release mode, as this has no impact on final binary size.) -DEBUG_FLAGS ?= -ggdb - -# List of flags to pass to C compiler -# If any flags are defined in application Makefile, add them at the end. -CFLAGS := $(strip \ - $(OPTIMIZATION_FLAGS) $(DEBUG_FLAGS) \ - $(COMMON_FLAGS) \ - $(COMMON_WARNING_FLAGS) -Wno-old-style-declaration \ - $(CPPFLAGS) \ - $(CFLAGS) \ - $(EXTRA_CFLAGS)) - -# List of flags to pass to C++ compiler -# If any flags are defined in application Makefile, add them at the end. -CXXFLAGS := $(strip \ - -std=gnu++11 \ - $(OPTIMIZATION_FLAGS) $(DEBUG_FLAGS) \ - $(COMMON_FLAGS) \ - $(COMMON_WARNING_FLAGS) \ - $(CPPFLAGS) \ - $(CXXFLAGS) \ - $(EXTRA_CXXFLAGS)) - -ifdef CONFIG_COMPILER_CXX_EXCEPTIONS -CXXFLAGS += -fexceptions -else -CXXFLAGS += -fno-exceptions -endif - -ifdef CONFIG_COMPILER_CXX_RTTI -CXXFLAGS += -frtti -else -CXXFLAGS += -fno-rtti -LDFLAGS += -fno-rtti -endif - -ARFLAGS := cru - # => Tools MEMANALYZER = $(PYTHON) $(ARCH_TOOLS)/memanalyzer.py $(OBJDUMP)$(TOOL_EXT) diff --git a/Sming/Arch/Esp8266/app.mk b/Sming/Arch/Esp8266/app.mk index 96ad91ec04..0ce2887fb5 100644 --- a/Sming/Arch/Esp8266/app.mk +++ b/Sming/Arch/Esp8266/app.mk @@ -45,25 +45,8 @@ endef $(TARGET_OUT_0): $(COMPONENTS_AR) $(LIBMAIN_DST) $(call LinkTarget,$(RBOOT_LD_0)) - - $(Q) $(MEMANALYZER) $@ > $(FW_MEMINFO_NEW) - - $(Q) if [ -f "$(FW_MEMINFO_NEW)" -a -f "$(FW_MEMINFO_OLD)" ]; then \ - $(AWK) -F "|" ' \ - FILENAME == "$(FW_MEMINFO_OLD)" { \ - arr[$$1]=$$5 \ - } \ - FILENAME == "$(FW_MEMINFO_NEW)" { \ - if (arr[$$1] != $$5) { \ - printf "%s%s%+d%s", substr($$0, 1, length($$0) - 1)," (",$$5 - arr[$$1],")\n" \ - } else { \ - print $$0 \ - } \ - }' $(FW_MEMINFO_OLD) $(FW_MEMINFO_NEW); \ - elif [ -f "$(FW_MEMINFO_NEW)" ]; then \ - cat $(FW_MEMINFO_NEW); \ - fi - + $(Q) $(MEMANALYZER) $@ > $(FW_MEMINFO) + $(Q) cat $(FW_MEMINFO) $(TARGET_OUT_1): $(COMPONENTS_AR) $(LIBMAIN_DST) $(call LinkTarget,$(RBOOT_LD_1)) diff --git a/Sming/Arch/Esp8266/build.mk b/Sming/Arch/Esp8266/build.mk index d3497ba66d..232658a06e 100644 --- a/Sming/Arch/Esp8266/build.mk +++ b/Sming/Arch/Esp8266/build.mk @@ -9,12 +9,7 @@ override ESP_VARIANT := endif CPPFLAGS += -DARCH_ESP8266 -CXXFLAGS += -fno-rtti -fno-exceptions -fno-threadsafe-statics - -# Required to access peripheral registers using structs -# e.g. `uint32_t value: 8` sitting at a byte or word boundary will be 'optimised' to -# an 8-bit fetch/store instruction which will not work; it must be a full 32-bit access. -CXXFLAGS += -fstrict-volatile-bitfields +CXXFLAGS += -fno-threadsafe-statics ## ESP_HOME sets the path where ESP tools and SDK are located. DEBUG_VARS += ESP_HOME diff --git a/Sming/Arch/Host/app.mk b/Sming/Arch/Host/app.mk index 0e5c61c07e..9a7bcac527 100644 --- a/Sming/Arch/Host/app.mk +++ b/Sming/Arch/Host/app.mk @@ -26,8 +26,8 @@ $(TARGET_OUT_0): $(COMPONENTS_AR) $(info $(notdir $(PROJECT_DIR)): Linking $@) $(Q) $(LD) $(addprefix -L,$(LIBDIRS)) $(LDFLAGS) -Wl,--start-group $(COMPONENTS_AR) $(addprefix -l,$(LIBS)) -Wl,--end-group -o $@ $(Q) $(call WriteFirmwareConfigFile,$@) - $(Q) $(MEMANALYZER) $@ > $(FW_MEMINFO_NEW) - $(Q) cat $(FW_MEMINFO_NEW) + $(Q) $(MEMANALYZER) $@ > $(FW_MEMINFO) + $(Q) cat $(FW_MEMINFO) ##@Tools .PHONY: valgrind diff --git a/Sming/Components/Storage/component.mk b/Sming/Components/Storage/component.mk index cc10fb3a10..5bd2caffbc 100644 --- a/Sming/Components/Storage/component.mk +++ b/Sming/Components/Storage/component.mk @@ -70,7 +70,6 @@ $(error Hardware configuration error) else ifneq ($(SMING_ARCH),$(SMING_ARCH_HW)) $(error Hardware configuration is for arch $(SMING_ARCH_HW), does not match SMING_ARCH ($(SMING_ARCH))) endif -COMPONENT_CXXFLAGS := -DPARTITION_TABLE_OFFSET=$(PARTITION_TABLE_OFFSET) COMPONENT_CPPFLAGS := -DPARTITION_TABLE_OFFSET=$(PARTITION_TABLE_OFFSET) # Function to evaluate expression against config diff --git a/Sming/Components/malloc_count/component.mk b/Sming/Components/malloc_count/component.mk index 4306eb9552..e0e6f5365c 100644 --- a/Sming/Components/malloc_count/component.mk +++ b/Sming/Components/malloc_count/component.mk @@ -26,6 +26,6 @@ MC_WRAP_FUNCS += \ strdup endif -EXTRA_LDFLAGS := $(call Wrap,$(MC_WRAP_FUNCS)) +EXTRA_LDFLAGS := $(call UndefWrap,$(MC_WRAP_FUNCS)) endif diff --git a/Sming/System/include/m_printf.h b/Sming/System/include/m_printf.h index bc3efc4991..ed36499842 100644 --- a/Sming/System/include/m_printf.h +++ b/Sming/System/include/m_printf.h @@ -56,10 +56,7 @@ size_t m_putc(char c); */ size_t m_nputs(const char* str, size_t length); -static inline size_t m_puts(const char* str) -{ - return m_nputs(str, strlen(str)); -} +size_t m_puts(const char* str); #ifdef __cplusplus } diff --git a/Sming/System/m_printf.cpp b/Sming/System/m_printf.cpp index 4f0c865955..46b29172ac 100644 --- a/Sming/System/m_printf.cpp +++ b/Sming/System/m_printf.cpp @@ -231,6 +231,10 @@ size_t m_nputs(const char* str, size_t length) return _puts_callback ? _puts_callback(str, length) : 0; } +size_t m_puts(const char* str) +{ + return m_nputs(str, strlen(str)); +} void m_printHex(const char* tag, const void* data, size_t len, int addr, size_t bytesPerLine) { diff --git a/Sming/build.mk b/Sming/build.mk index 4c06fdabd9..c38b928f9c 100644 --- a/Sming/build.mk +++ b/Sming/build.mk @@ -148,6 +148,11 @@ CPPFLAGS = \ -fdata-sections \ -ffunction-sections +# Required to access peripheral registers using structs +# e.g. `uint32_t value: 8` sitting at a byte or word boundary will be 'optimised' to +# an 8-bit fetch/store instruction which will not work; it must be a full 32-bit access. +CPPFLAGS += -fstrict-volatile-bitfields + CPPFLAGS += \ -Wall \ -Wpointer-arith \ @@ -183,7 +188,10 @@ else CPPFLAGS += -Os -g endif -CXXFLAGS += -felide-constructors +CXXFLAGS += \ + -felide-constructors \ + -fno-rtti \ + -fno-exceptions ifneq ($(STRICT),1) CXXFLAGS += -Wno-reorder @@ -196,8 +204,9 @@ DEBUG_VARS += GCC_VERSION GCC_VERSION := $(shell $(CC) -dumpversion) # Use c11 by default. Every architecture can override it -SMING_C_STD ?= c11 -CFLAGS += -std=$(SMING_C_STD) +DEBUG_VARS += SMING_C_STD +SMING_C_STD ?= c11 +CFLAGS += -std=$(SMING_C_STD) # Select C++17 if supported, defaulting to C++11 otherwise DEBUG_VARS += SMING_CXX_STD @@ -240,8 +249,27 @@ CLIB_PREFIX := clib- ToUpper = $(shell echo "$1" | tr 'a-z' 'A-Z') ToLower = $(shell echo "$1" | tr 'A-Z' 'a-z') -# Use with LDFLAGS to undefine and wrap a list of functions +# Use with LDFLAGS to define a symbol alias +# $1 -> List of alias=name pairs +define DefSym +$(foreach n,$1,-Wl,--defsym=$n) +endef + +# Use with LDFLAGS to undefine a list of symbols +# $1 -> List of symbols +define Undef +$(foreach n,$1,-u $n) +endef + +# Use with LDFLAGS to wrap a list of symbols +# $1 -> List of symbols define Wrap +$(foreach n,$1,-Wl,-wrap,$n) +endef + +# Use with LDFLAGS to undefine and wrap a list of symbols +# $1 -> List of symbols +define UndefWrap $(foreach n,$1,-u $n -Wl,-wrap,$n) endef diff --git a/Sming/building.rst b/Sming/building.rst index 89fe66115c..ab103ddb23 100644 --- a/Sming/building.rst +++ b/Sming/building.rst @@ -39,7 +39,7 @@ These are the main variables you need to be aware of: - **Esp8266** The default if not specified. :envvar:`ESP_HOME` must also be provided to locate SDK & tools. - - **Esp32** {todo} + - **Esp32** Supports ESP32 architecture. - **Host** builds a version of the library for native host debugging on Linux or Windows @@ -634,6 +634,9 @@ never overwritten. Will be visible **ONLY** to C++ code within the component. +.. envvar:: COMPONENT_CPPFLAGS + + Will be visible to both C and C++ code within the component. .. important:: diff --git a/Sming/component-wrapper.mk b/Sming/component-wrapper.mk index ae7d2c4cfd..06079ebab3 100644 --- a/Sming/component-wrapper.mk +++ b/Sming/component-wrapper.mk @@ -27,6 +27,7 @@ CUSTOM_BUILD := COMPONENT_TARGETS := EXTRA_OBJ := COMPONENT_CFLAGS := +COMPONENT_CPPFLAGS := COMPONENT_CXXFLAGS := COMPONENT_VARS := COMPONENT_RELINK_VARS := @@ -106,6 +107,7 @@ endif ifeq (,$(CUSTOM_BUILD)) CFLAGS += $(COMPONENT_CFLAGS) +CPPFLAGS += $(COMPONENT_CPPFLAGS) CXXFLAGS += $(COMPONENT_CXXFLAGS) # GCC 10 escapes ':' in path names which breaks GNU make for Windows so filter them diff --git a/Sming/project.mk b/Sming/project.mk index 37a393d241..384410acea 100644 --- a/Sming/project.mk +++ b/Sming/project.mk @@ -109,10 +109,8 @@ DEBUG_VARS += TARGET_OUT_0 CACHE_VARS += APP_NAME APP_NAME ?= app -# Firmware memory layout info files -FW_MEMINFO_NEW := $(FW_BASE)/fwMeminfo.new -FW_MEMINFO_OLD := $(FW_BASE)/fwMeminfo.old -FW_MEMINFO_SAVED := out/fwMeminfo +# Firmware memory layout info file +FW_MEMINFO := $(FW_BASE)/fwMeminfo.txt # List of Components we're going to parse, with duplicate libraries removed COMPONENTS := Sming From 0e698664cb88476e192dd5b1cc71cf7e1770d19f Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 11 Oct 2021 15:57:49 +0100 Subject: [PATCH 075/130] Fix bugs in si2c twi_status() timeout and host uart/timer. (#2389) Tallies with esp8266 arduino core code --- Sming/Arch/Host/Components/driver/hw_timer.cpp | 2 +- Sming/Arch/Host/Components/driver/uart_server.cpp | 2 +- Sming/Core/si2c.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Sming/Arch/Host/Components/driver/hw_timer.cpp b/Sming/Arch/Host/Components/driver/hw_timer.cpp index 6e8abc7172..0724df0a06 100644 --- a/Sming/Arch/Host/Components/driver/hw_timer.cpp +++ b/Sming/Arch/Host/Components/driver/hw_timer.cpp @@ -44,7 +44,7 @@ class CTimerThread : public CThread void attach_interrupt(hw_timer_source_type_t source_type, hw_timer_callback_t callback, void* arg) { stop(); - source_type = source_type; + this->source_type = source_type; this->callback.func = callback; this->callback.arg = arg; } diff --git a/Sming/Arch/Host/Components/driver/uart_server.cpp b/Sming/Arch/Host/Components/driver/uart_server.cpp index 2b0e5ad0a5..7c51ca1d35 100644 --- a/Sming/Arch/Host/Components/driver/uart_server.cpp +++ b/Sming/Arch/Host/Components/driver/uart_server.cpp @@ -209,7 +209,7 @@ void CUart::onNotify(smg_uart_t* uart, smg_uart_notify_code_t code) break; case UART_NOTIFY_AFTER_WRITE: { - if(uart != nullptr) { + if(this->uart != nullptr) { // Kick the thread to send now txsem.post(); } else { diff --git a/Sming/Core/si2c.cpp b/Sming/Core/si2c.cpp index 521120d151..924cf6a051 100644 --- a/Sming/Core/si2c.cpp +++ b/Sming/Core/si2c.cpp @@ -249,7 +249,7 @@ uint8_t twi_status() int clockCount = 20; - while(SDA_READ() == 0 && clockCount > 0) { //if SDA low, read the bits slaves have to sent to a max + while(SDA_READ() == 0 && clockCount-- > 0) { //if SDA low, read the bits slaves have to sent to a max twi_read_bit(); if(SCL_READ() == 0) { return I2C_SCL_HELD_LOW_AFTER_READ; //I2C bus error. SCL held low beyond slave clock stretch time From 5866301717c29068c912be37a837be144f9d91d5 Mon Sep 17 00:00:00 2001 From: slaff Date: Wed, 13 Oct 2021 09:55:13 +0200 Subject: [PATCH 076/130] Adding also support for creating Eclipse project meta files. (#2390) Run `make ide-eclipse` in the root folder of your Sming application if you are missing the .project and .cproject files. Co-authored-by: mikee47 --- Sming/project.mk | 7 +- Tools/ide/common/sming.py | 120 ++++++++++ Tools/ide/eclipse/setup.py | 31 +++ Tools/ide/eclipse/templates/.cproject | 151 ++++++++++++ Tools/ide/eclipse/templates/.project | 28 +++ Tools/ide/vscode/setup.py | 119 ++++++++++ .../template/intellisense/configuration.json | 0 .../template/intellisense/properties.json | 0 Tools/{ => ide}/vscode/template/launch.json | 0 Tools/{ => ide}/vscode/template/tasks.json | 0 .../{ => ide}/vscode/template/workspace.json | 0 Tools/vscode/setup.py | 223 ------------------ docs/source/tools/eclipse.rst | 43 ++++ docs/source/tools/index.rst | 1 + 14 files changed, 499 insertions(+), 224 deletions(-) create mode 100644 Tools/ide/common/sming.py create mode 100644 Tools/ide/eclipse/setup.py create mode 100644 Tools/ide/eclipse/templates/.cproject create mode 100644 Tools/ide/eclipse/templates/.project create mode 100644 Tools/ide/vscode/setup.py rename Tools/{ => ide}/vscode/template/intellisense/configuration.json (100%) rename Tools/{ => ide}/vscode/template/intellisense/properties.json (100%) rename Tools/{ => ide}/vscode/template/launch.json (100%) rename Tools/{ => ide}/vscode/template/tasks.json (100%) rename Tools/{ => ide}/vscode/template/workspace.json (100%) delete mode 100644 Tools/vscode/setup.py create mode 100644 docs/source/tools/eclipse.rst diff --git a/Sming/project.mk b/Sming/project.mk index 384410acea..2620d3f569 100644 --- a/Sming/project.mk +++ b/Sming/project.mk @@ -534,7 +534,12 @@ export HOST_PARAMETERS .PHONY: ide-vscode-update ide-vscode-update: $(Q) SMING_HOME=$(SMING_HOME) OUT_BASE=$(OUT_BASE) \ - $(PYTHON) $(SMING_TOOLS)/vscode/setup.py + $(PYTHON) $(SMING_TOOLS)/ide/vscode/setup.py + +.PHONY: ide-eclipse +ide-eclipse: + $(Q) SMING_HOME=$(SMING_HOME) OUT_BASE=$(OUT_BASE) \ + $(PYTHON) $(SMING_TOOLS)/ide/eclipse/setup.py ##@Testing diff --git a/Tools/ide/common/sming.py b/Tools/ide/common/sming.py new file mode 100644 index 0000000000..b9979e473b --- /dev/null +++ b/Tools/ide/common/sming.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +# +# Sming hardware configuration tool +# + +import os, sys, json, shutil, configparser, string + +class Env(dict): + + """Cache build environment variables.""" + + def __init__(self): + self.update(os.environ) + self.updateFromConfig('config.mk') + self.updateFromConfig('debug.mk') + + def updateFromConfig(self, filename): + path = self['OUT_BASE'] + '/' + filename + if not os.path.exists(path): + return + parser = configparser.ConfigParser() + parser.optionxform = str # preserve case + with open(path) as f: + data = "[config]\n" + f.read() + parser.read_string(data) + self.update(parser['config']) + + def replace(self, path, name, prefix): + value = self.get(name) + if value is not None: + s = fix_path(path) + value = fix_path(value) + if value != '' and s.startswith(value): + return '${%s%s}%s' % (prefix, name, s[len(value):]) + return path + + def resolve(self, path): + """Convert any embedded environment variables into real paths.""" + tmp = str(path) + while True: + tmp = tmp.replace('(', '{') + tmp = tmp.replace(')', '}') + new_path = string.Template(tmp).safe_substitute(self) + if new_path == tmp: + return new_path + tmp = new_path + + def subst_path(self, path, prefix=''): + path = self.replace(path, 'SMING_HOME', prefix) + path = self.replace(path, 'ESP_HOME', prefix) + path = self.replace(path, 'IDF_PATH', prefix) + path = self.replace(path, 'IDF_TOOLS_PATH', prefix) + return path + + def isWsl(self): + return self.get('WSL_ROOT', '') != '' + + +env = Env() + + +def fix_path(path): + """Fix path so it conforms to makefile specs.""" + if path[1:3] == ':/': + return '/' + path[0] + path[2:] + return path + +def check_path(path): + """Fix path so it conforms to vscode specs.""" + if sys.platform == 'win32': + if path[:1] == '/': + return path[1:2] + ':' + path[2:] + return path + +def find_tool(name): + if sys.platform == 'win32': + if os.path.splitext(name)[1] != '.exe': + name += '.exe' + if os.path.isabs(name): + path = name + else: + path = shutil.which(name) + if path is None: + sys.stderr.write("Warning! '%s' not found in path\n" % name) + path = name + if not os.path.exists(path): + sys.stderr.write("Warning! '%s' doesn't exist\n" % path) + return env.subst_path(path, 'env:') + +def load_json(filename, must_exist=True): + print(filename) + if must_exist or os.path.exists(filename): + import rjsmin + s = open(filename, 'r').read() + return json.loads(rjsmin.jsmin(s)) + return None + +def save_json(data, filename): + folder = os.path.dirname(filename) + if folder != '': + os.makedirs(folder, exist_ok=True) + with open(filename, 'w') as f: + json.dump(data, f, indent=4) + +def load_template(name, folder = None): + if folder is None: + folder = os.path.dirname(__file__) + filename = os.path.join(folder, 'template', name) + return load_json(filename) + +def find_object(data, name): + for o in data: + if o['name'] == name: + return o + return None + +def get_property(data, name, default): + if not name in data: + data[name] = default + return data[name] diff --git a/Tools/ide/eclipse/setup.py b/Tools/ide/eclipse/setup.py new file mode 100644 index 0000000000..1bcf3cc53b --- /dev/null +++ b/Tools/ide/eclipse/setup.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# Sming hardware configuration tool +# + +import os, sys +from shutil import copyfile +from pathlib import Path + +appPath = os.path.dirname(os.path.realpath(__file__)) +libPath = os.path.realpath(os.path.join(appPath, '..', 'common')) +sys.path.append(libPath) + +from sming import env + +def update_project_files(): + projectName = os.path.basename(os.getcwd()) + content = Path(appPath + '/templates/.project').read_text() + content = content % { 'PROJECT_NAME': projectName } + with open(os.getcwd() + '/.project', 'w') as f: + f.write(content) + copyfile(appPath + '/templates/.cproject', os.getcwd() + '/.cproject') + +def main(): + if not env['SMING_HOME'] or not env['SMING_ARCH']: + sys.exit(1) + update_project_files() + + +if __name__ == '__main__': + main() diff --git a/Tools/ide/eclipse/templates/.cproject b/Tools/ide/eclipse/templates/.cproject new file mode 100644 index 0000000000..e1450b6031 --- /dev/null +++ b/Tools/ide/eclipse/templates/.cproject @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + make + -f ${ProjDirPath}/Makefile + all + true + true + true + + + make + -f ${ProjDirPath}/Makefile + clean + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flash + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flashonefile + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flashinit + true + true + true + + + make + -f ${ProjDirPath}/Makefile + flashboot + true + true + true + + + make + -f ${ProjDirPath}/Makefile + rebuild + true + true + true + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/ide/eclipse/templates/.project b/Tools/ide/eclipse/templates/.project new file mode 100644 index 0000000000..65650d9b9c --- /dev/null +++ b/Tools/ide/eclipse/templates/.project @@ -0,0 +1,28 @@ + + + %(PROJECT_NAME)s + + + SmingFramework + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/Tools/ide/vscode/setup.py b/Tools/ide/vscode/setup.py new file mode 100644 index 0000000000..8c11f54b21 --- /dev/null +++ b/Tools/ide/vscode/setup.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +# +# Sming hardware configuration tool +# + +import os, sys + +appPath = os.path.dirname(os.path.realpath(__file__)) +libPath = appPath + '/../common/' +print(libPath) +sys.path.append(libPath) + +from sming import check_path, env, find_object, find_tool, get_property, load_json, load_template, save_json + +def update_intellisense(): + dirs = [] + for d in env['COMPONENTS_EXTRA_INCDIR'].split(): + d = check_path(d) + if os.path.exists(d): + dirs += [check_path(env.subst_path(d))] + + propertiesFile = '.vscode/c_cpp_properties.json' + if os.path.exists(propertiesFile): + properties = load_json(propertiesFile) + else: + properties = load_template('intellisense/properties.json', appPath) + + env.update(get_property(properties, 'env', {})) + configurations = get_property(properties, 'configurations', []) + + config = find_object(configurations, env['SMING_ARCH']) + + if config is None: + config = load_template('intellisense/configuration.json', appPath) + config['name'] = env['SMING_ARCH'] + config['defines'].append('ARCH_%s=1' % env['SMING_ARCH'].upper()) + configurations.append(config) + + config['compilerPath'] = find_tool(env['CXX']) + config['includePath'] = dirs + + save_json(properties, propertiesFile) + + +def update_tasks(): + filename = '.vscode/tasks.json' + launch = load_json(filename, False) + if launch is None: + template = load_template('tasks.json', appPath) + launch = template.copy() + # TODO: Make any changes as required + save_json(launch, filename) + +def update_launch(): + filename = '.vscode/launch.json' + launch = load_json(filename, False) + template = load_template('launch.json', appPath) + if launch is None: + launch = template.copy() + configurations = get_property(launch, 'configurations', []) + config_name = "%s GDB" % env['SMING_ARCH'] + config = find_object(configurations, config_name) + template_config = find_object(template['configurations'], config_name) + if template_config is None: + print("Warning: Template launch configuration '%s' not found" % config_name) + elif not config is None: + configurations.remove(config) + config = template_config.copy() + configurations.append(config) + + if config is None: + return + + config['miDebuggerPath'] = find_tool(env['GDB']) + dbgargs = "-x ${env:SMING_HOME}/Arch/%s/Components/gdbstub/gdbcmds" % env['SMING_ARCH'] + if env['SMING_ARCH'] == 'Esp8266': + if not env.isWsl(): + dbgargs += " -b %s" % env['COM_SPEED_GDB'] + config['miDebuggerServerAddress'] = env['COM_PORT_GDB'] + elif env['SMING_ARCH'] == 'Host': + args = [] + args += env['CLI_TARGET_OPTIONS'].split() + args += ["--"] + args += env['HOST_PARAMETERS'].split() + config['args'] = args + config['miDebuggerArgs'] = dbgargs + config['program'] = "${workspaceFolder}/" + env.resolve('${TARGET_OUT_0}') + + save_json(launch, filename) + +def update_workspace(): + filename = 'sming.code-workspace' + ws = load_json(filename, False) + template = load_template('workspace.json', appPath) + if ws is None: + ws = template.copy() + schemas = ws['settings']['json.schemas'] = [] + # ws['settings']['json.schemas'] + for schema in template['settings']['json.schemas']: + schema['url'] = env.resolve(schema['url']) + schemas += [schema] + save_json(ws, filename) + +def main(): + if not env['SMING_HOME'] or not env['SMING_ARCH']: + sys.exit(1) + + # So we can find rjsmin.py + sys.path.append(os.path.join(env['SMING_HOME'], '../Tools/Python')) + + update_intellisense() + update_tasks() + update_launch() + update_workspace() + + + +if __name__ == '__main__': + main() diff --git a/Tools/vscode/template/intellisense/configuration.json b/Tools/ide/vscode/template/intellisense/configuration.json similarity index 100% rename from Tools/vscode/template/intellisense/configuration.json rename to Tools/ide/vscode/template/intellisense/configuration.json diff --git a/Tools/vscode/template/intellisense/properties.json b/Tools/ide/vscode/template/intellisense/properties.json similarity index 100% rename from Tools/vscode/template/intellisense/properties.json rename to Tools/ide/vscode/template/intellisense/properties.json diff --git a/Tools/vscode/template/launch.json b/Tools/ide/vscode/template/launch.json similarity index 100% rename from Tools/vscode/template/launch.json rename to Tools/ide/vscode/template/launch.json diff --git a/Tools/vscode/template/tasks.json b/Tools/ide/vscode/template/tasks.json similarity index 100% rename from Tools/vscode/template/tasks.json rename to Tools/ide/vscode/template/tasks.json diff --git a/Tools/vscode/template/workspace.json b/Tools/ide/vscode/template/workspace.json similarity index 100% rename from Tools/vscode/template/workspace.json rename to Tools/ide/vscode/template/workspace.json diff --git a/Tools/vscode/setup.py b/Tools/vscode/setup.py deleted file mode 100644 index a183d6f18d..0000000000 --- a/Tools/vscode/setup.py +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/env python3 -# -# Sming hardware configuration tool -# - -import os, sys, json, shutil, configparser, string - -class Env(dict): - """ Cache build environment variables""" - - def __init__(self): - self.update(os.environ) - self.updateFromConfig('config.mk') - self.updateFromConfig('debug.mk') - - def updateFromConfig(self, filename): - path = self['OUT_BASE'] + '/' + filename - if not os.path.exists(path): - return - parser = configparser.ConfigParser() - parser.optionxform = str # preserve case - with open(path) as f: - data = "[config]\n" + f.read() - parser.read_string(data) - self.update(parser['config']) - - def replace(self, path, name, prefix): - value = self.get(name) - if value is not None: - s = fix_path(path) - value = fix_path(value) - if value != '' and s.startswith(value): - return '${%s%s}%s' % (prefix, name, s[len(value):]) - return path - - def resolve(self, path): - """Convert any embedded environment variables into real paths - """ - tmp = str(path) - while True: - tmp = tmp.replace('(', '{') - tmp = tmp.replace(')', '}') - new_path = string.Template(tmp).safe_substitute(self) - if new_path == tmp: - return new_path - tmp = new_path - - def subst_path(self, path, prefix=''): - path = self.replace(path, 'SMING_HOME', prefix) - path = self.replace(path, 'ESP_HOME', prefix) - path = self.replace(path, 'IDF_PATH', prefix) - path = self.replace(path, 'IDF_TOOLS_PATH', prefix) - return path - - def isWsl(self): - return self.get('WSL_ROOT', '') != '' - - -env = Env() - - -def fix_path(path): - """Fix path so it conforms to makefile specs""" - if path[1:3] == ':/': - return '/' + path[0] + path[2:] - return path - -def check_path(path): - """Fix path so it conforms to vscode specs""" - if sys.platform == 'win32': - if path[:1] == '/': - return path[1:2] + ':' + path[2:] - return path - -def find_tool(name): - if sys.platform == 'win32': - if os.path.splitext(name)[1] != '.exe': - name += '.exe' - if os.path.isabs(name): - path = name - else: - path = shutil.which(name) - if path is None: - sys.stderr.write("Warning! '%s' not found in path\n" % name) - path = name - if not os.path.exists(path): - sys.stderr.write("Warning! '%s' doesn't exist\n" % path) - return env.subst_path(path, 'env:') - -def load_json(filename, must_exist=True): - if must_exist or os.path.exists(filename): - import rjsmin - s = open(filename, 'r').read() - return json.loads(rjsmin.jsmin(s)) - return None - -def save_json(data, filename): - dir = os.path.dirname(filename) - if dir != '': - os.makedirs(dir, exist_ok=True) - with open(filename, 'w') as f: - json.dump(data, f, indent=4) - -def load_template(name): - dir = os.path.dirname(__file__) - filename = os.path.join(dir, 'template', name) - return load_json(filename) - -def find_object(data, name): - for o in data: - if o['name'] == name: - return o - return None - -def get_property(data, name, default): - if not name in data: - data[name] = default - return data[name] - -def update_intellisense(): - dirs = [] - for d in env['COMPONENTS_EXTRA_INCDIR'].split(): - if os.path.exists(d): - dirs += [check_path(env.subst_path(d))] - - propertiesFile = '.vscode/c_cpp_properties.json' - if os.path.exists(propertiesFile): - properties = load_json(propertiesFile) - else: - properties = load_template('intellisense/properties.json') - - env.update(get_property(properties, 'env', {})) - configurations = get_property(properties, 'configurations', []) - - config = find_object(configurations, env['SMING_ARCH']) - - if config is None: - config = load_template('intellisense/configuration.json') - config['name'] = env['SMING_ARCH'] - config['defines'].append('ARCH_%s=1' % env['SMING_ARCH'].upper()) - configurations.append(config) - - config['compilerPath'] = find_tool(env['CXX']) - config['includePath'] = dirs - - save_json(properties, propertiesFile) - - -def update_tasks(): - filename = '.vscode/tasks.json' - launch = load_json(filename, False) - if launch is None: - template = load_template('tasks.json') - launch = template.copy() - # TODO: Make any changes as required - save_json(launch, filename) - -def update_launch(): - filename = '.vscode/launch.json' - launch = load_json(filename, False) - template = load_template('launch.json') - if launch is None: - launch = template.copy() - configurations = get_property(launch, 'configurations', []) - config_name = "%s GDB" % env['SMING_ARCH'] - config = find_object(configurations, config_name) - template_config = find_object(template['configurations'], config_name) - if template_config is None: - print("Warning: Template launch configuration '%s' not found" % config_name) - elif not config is None: - configurations.remove(config) - config = template_config.copy() - configurations.append(config) - - if config is None: - return - - config['miDebuggerPath'] = find_tool(env['GDB']) - dbgargs = "-x ${env:SMING_HOME}/Arch/%s/Components/gdbstub/gdbcmds" % env['SMING_ARCH'] - if env['SMING_ARCH'] == 'Esp8266': - if not env.isWsl(): - dbgargs += " -b %s" % env['COM_SPEED_GDB'] - config['miDebuggerServerAddress'] = env['COM_PORT_GDB'] - elif env['SMING_ARCH'] == 'Host': - args = [] - args += env['CLI_TARGET_OPTIONS'].split() - args += ["--"] - args += env['HOST_PARAMETERS'].split() - config['args'] = args - config['miDebuggerArgs'] = dbgargs - config['program'] = "${workspaceFolder}/" + env.resolve('${TARGET_OUT_0}') - - save_json(launch, filename) - -def update_workspace(): - filename = 'sming.code-workspace' - ws = load_json(filename, False) - template = load_template('workspace.json') - if ws is None: - ws = template.copy() - schemas = ws['settings']['json.schemas'] = [] - # ws['settings']['json.schemas'] - for schema in template['settings']['json.schemas']: - schema['url'] = env.resolve(schema['url']) - schemas += [schema] - save_json(ws, filename) - -def main(): - if not env['SMING_HOME'] or not env['SMING_ARCH']: - sys.exit(1) - - # So we can find rjsmin.py - sys.path.append(os.path.join(env['SMING_HOME'], '../Tools/Python')) - - update_intellisense() - update_tasks() - update_launch() - update_workspace() - - - -if __name__ == '__main__': - main() diff --git a/docs/source/tools/eclipse.rst b/docs/source/tools/eclipse.rst new file mode 100644 index 0000000000..5a37101aa3 --- /dev/null +++ b/docs/source/tools/eclipse.rst @@ -0,0 +1,43 @@ +Using with Eclipse CDT +====================== + +.. highlight:: bash + +The Eclipse CDT Project provides a fully functional C and C++ Integrated Development Environment based on the Eclipse platform. +Eclipse is a free (as in "free beer") and Open Source code editor for Windows, Linux and Mac. + +For easier integration make sure you have both :envvar:`ESP_HOME` and +:envvar:`SMING_HOME` exported in your working environment. + + +Software involved +----------------- + +- `Eclipse CDT `__ + +Installation +------------ + +- Install Eclipse CDT using your operating system packaging tools. + +Configuration +------------- + +First you should import the Sming project. +This can be done by going to menu ``File`` -> ``Import`` and from there choosing +``General`` -> ``Existing project into Workspace``. When asked for the location of the project +point Eclipse to the location of your SMING_HOME folder. + + +Once you have imported the Sming project you can import some of the samples. +Run again ``File`` -> ``Import`` and from there choose ``General`` -> ``Existing project into Workspace``. +Then select the ``Basic_Blink`` directory under ``samples``. Once the project is imported and the Eclipse CDT indexes +are build you will be able to program like a pro and have code completion and debug your code. +For the latter see :sample:`LiveDebug`. + +If your application is not containing `.project` and `.cproject` files inside its root folder then +you can create such files. First you need to go to the root folder of your project and then type the command below:: + + make ide-eclipse + +This will create the minimum required meta files for an Eclipse CDT project which you can import into your Workspace. \ No newline at end of file diff --git a/docs/source/tools/index.rst b/docs/source/tools/index.rst index 34ce3ebee5..fef10c8fc0 100644 --- a/docs/source/tools/index.rst +++ b/docs/source/tools/index.rst @@ -7,4 +7,5 @@ Details of Sming support for Integrated Development Environments (IDEs). :maxdepth: 1 clion + eclipse vscode From 5ed63aa9e2e193c490f75b46a13c0dd120a9c5c1 Mon Sep 17 00:00:00 2001 From: slaff Date: Thu, 14 Oct 2021 11:46:51 +0200 Subject: [PATCH 077/130] Fixes compilation of Ota library for Esp32. (#2391) --- Sming/Arch/Esp32/Components/esp32/component.mk | 1 + Sming/Arch/Esp32/Tools/ci/build.run.cmd | 3 +++ Sming/Arch/Esp32/Tools/ci/build.run.sh | 3 +++ 3 files changed, 7 insertions(+) diff --git a/Sming/Arch/Esp32/Components/esp32/component.mk b/Sming/Arch/Esp32/Components/esp32/component.mk index 29893fb9ad..131665d0bf 100644 --- a/Sming/Arch/Esp32/Components/esp32/component.mk +++ b/Sming/Arch/Esp32/Components/esp32/component.mk @@ -36,6 +36,7 @@ LIBDIRS += \ $(addprefix $(SDK_COMPONENTS_PATH)/,$(SDK_LIBDIRS)) SDK_INCDIRS := \ + app_update/include \ bootloader_support/include \ bootloader_support/include_bootloader \ driver/$(ESP_VARIANT)/include \ diff --git a/Sming/Arch/Esp32/Tools/ci/build.run.cmd b/Sming/Arch/Esp32/Tools/ci/build.run.cmd index ad6fc47873..a11ad25554 100644 --- a/Sming/Arch/Esp32/Tools/ci/build.run.cmd +++ b/Sming/Arch/Esp32/Tools/ci/build.run.cmd @@ -14,6 +14,9 @@ REM esp32c3 REM esp32s3 %MAKE_PARALLEL% ESP_VARIANT=esp32s3 %ESP32_PROJECTS% || goto :error +REM make sure that the Ota Library sample compiles for ESP32 +%MAKE_PARALLEL% -C %SMING_HOME%\Libraries\OtaUpgradeMqtt\samples\Upgrade + goto :EOF diff --git a/Sming/Arch/Esp32/Tools/ci/build.run.sh b/Sming/Arch/Esp32/Tools/ci/build.run.sh index 2bd9758347..e9f18d091f 100644 --- a/Sming/Arch/Esp32/Tools/ci/build.run.sh +++ b/Sming/Arch/Esp32/Tools/ci/build.run.sh @@ -13,3 +13,6 @@ $MAKE_PARALLEL ESP_VARIANT=esp32c3 $ESP32_PROJECTS # esp32s3 $MAKE_PARALLEL ESP_VARIANT=esp32s3 $ESP32_PROJECTS + +# make sure that the Ota Library sample compiles for ESP32 +$MAKE_PARALLEL -C "$SMING_HOME/Libraries/OtaUpgradeMqtt/samples/Upgrade" \ No newline at end of file From 25066b1af6141d7fd14791c8d2e0c2d1dfa37ca3 Mon Sep 17 00:00:00 2001 From: Mike Date: Thu, 14 Oct 2021 11:26:20 +0100 Subject: [PATCH 078/130] Fix some codacy advisories (#2393) Co-authored-by: Slavey Karadzhov --- Sming/Arch/Esp32/Tools/ci/build.run.sh | 2 + Sming/Arch/Esp32/Tools/ci/build.setup.sh | 2 + Sming/Arch/Esp32/Tools/decode-stacktrace.py | 1 - Sming/Arch/Esp32/Tools/install.sh | 2 + Sming/Arch/Esp8266/Tools/ci/build.run.sh | 2 + Sming/Arch/Esp8266/Tools/ci/build.setup.sh | 2 + Sming/Arch/Esp8266/Tools/decode-stacktrace.py | 1 - Sming/Arch/Esp8266/Tools/install.sh | 18 ++-- Sming/Arch/Host/Tools/ci/build.run.sh | 4 +- Sming/Arch/Host/Tools/ci/build.setup.sh | 6 +- Sming/Arch/Host/Tools/ci/coverity-scan.sh | 6 +- Sming/Arch/Host/Tools/install.sh | 2 + Sming/Arch/Host/Tools/setup-network-linux.sh | 4 +- .../Network/src/Network/TcpConnection.h | 2 +- .../Network/src/Network/TcpServer.cpp | 2 +- .../Storage/Tools/hwconfig/common.py | 14 +-- .../Storage/Tools/hwconfig/config.py | 15 ++- .../Storage/Tools/hwconfig/editor.py | 96 ++++++++----------- .../Storage/Tools/hwconfig/partition.py | 27 +++--- .../Storage/Tools/hwconfig/storage.py | 3 + Sming/Components/ssl/Tools/make_certs.sh | 10 +- Sming/Core/Data/Stream/ReadWriteStream.cpp | 3 +- .../CapacitiveSensor/CapacitiveSensor.cpp | 2 +- .../OtaUpgrade/OtaUpgrade/BasicStream.cpp | 5 +- Sming/Libraries/Spiffs/src/FileMeta.cpp | 2 + .../CommandProcessing/CommandExecutor.h | 3 + Tools/ci/build.sh | 18 ++-- Tools/ci/deploy.sh | 8 +- Tools/export.sh | 5 +- Tools/install.sh | 10 +- docs/Tools/install.sh | 0 31 files changed, 140 insertions(+), 137 deletions(-) mode change 100644 => 100755 Sming/Arch/Esp32/Tools/ci/build.run.sh mode change 100644 => 100755 Sming/Arch/Esp32/Tools/install.sh mode change 100644 => 100755 Sming/Arch/Esp8266/Tools/install.sh mode change 100644 => 100755 docs/Tools/install.sh diff --git a/Sming/Arch/Esp32/Tools/ci/build.run.sh b/Sming/Arch/Esp32/Tools/ci/build.run.sh old mode 100644 new mode 100755 index e9f18d091f..f597df2601 --- a/Sming/Arch/Esp32/Tools/ci/build.run.sh +++ b/Sming/Arch/Esp32/Tools/ci/build.run.sh @@ -1,3 +1,5 @@ +#!/bin/bash +# # Esp32 build.run.sh $MAKE_PARALLEL Basic_Blink Basic_WiFi HttpServer_ConfigNetwork DEBUG_VERBOSE_LEVEL=3 STRICT=1 diff --git a/Sming/Arch/Esp32/Tools/ci/build.setup.sh b/Sming/Arch/Esp32/Tools/ci/build.setup.sh index 4aaa83f008..ae000d1ac3 100755 --- a/Sming/Arch/Esp32/Tools/ci/build.setup.sh +++ b/Sming/Arch/Esp32/Tools/ci/build.setup.sh @@ -1 +1,3 @@ +#!/bin/bash +# # Esp32 build.setup.sh diff --git a/Sming/Arch/Esp32/Tools/decode-stacktrace.py b/Sming/Arch/Esp32/Tools/decode-stacktrace.py index 925958e2db..22dcf517f9 100644 --- a/Sming/Arch/Esp32/Tools/decode-stacktrace.py +++ b/Sming/Arch/Esp32/Tools/decode-stacktrace.py @@ -49,4 +49,3 @@ def extractAddresses(data): pipe.stdin.write(line.encode('ascii')) pipe.stdin.flush() - diff --git a/Sming/Arch/Esp32/Tools/install.sh b/Sming/Arch/Esp32/Tools/install.sh old mode 100644 new mode 100755 index d087b4bfa0..bb26adf36b --- a/Sming/Arch/Esp32/Tools/install.sh +++ b/Sming/Arch/Esp32/Tools/install.sh @@ -1,3 +1,5 @@ +#!/bin/bash +# # Esp32 install.sh if [ -n "$IDF_PATH" ] && [ -n "$IDF_TOOLS_PATH" ]; then diff --git a/Sming/Arch/Esp8266/Tools/ci/build.run.sh b/Sming/Arch/Esp8266/Tools/ci/build.run.sh index 9ffe3edc5f..2947e32c4c 100755 --- a/Sming/Arch/Esp8266/Tools/ci/build.run.sh +++ b/Sming/Arch/Esp8266/Tools/ci/build.run.sh @@ -1,3 +1,5 @@ +#!/bin/bash +# # Esp8266 build.run.sh $MAKE_PARALLEL samples diff --git a/Sming/Arch/Esp8266/Tools/ci/build.setup.sh b/Sming/Arch/Esp8266/Tools/ci/build.setup.sh index 941d5df444..fee356b4ac 100755 --- a/Sming/Arch/Esp8266/Tools/ci/build.setup.sh +++ b/Sming/Arch/Esp8266/Tools/ci/build.setup.sh @@ -1,3 +1,5 @@ +#!/bin/bash +# # Esp8266 build.setup.sh unset SPIFFY diff --git a/Sming/Arch/Esp8266/Tools/decode-stacktrace.py b/Sming/Arch/Esp8266/Tools/decode-stacktrace.py index 91d61460a9..d464c7d410 100644 --- a/Sming/Arch/Esp8266/Tools/decode-stacktrace.py +++ b/Sming/Arch/Esp8266/Tools/decode-stacktrace.py @@ -49,4 +49,3 @@ def extractAddresses(data): pipe.stdin.write(line.encode('ascii')) pipe.stdin.flush() - diff --git a/Sming/Arch/Esp8266/Tools/install.sh b/Sming/Arch/Esp8266/Tools/install.sh old mode 100644 new mode 100755 index 039b999a6d..5b3c5a08df --- a/Sming/Arch/Esp8266/Tools/install.sh +++ b/Sming/Arch/Esp8266/Tools/install.sh @@ -1,3 +1,5 @@ +#!/bin/bash +# # Esp8266 install.sh # Old toolchain @@ -6,11 +8,11 @@ if [ -n "$UDK_ROOT" ]; then printf "\n\n** Skipping Esp8266 tools installation: '$UDK_ROOT' exists\n\n" else TOOLCHAIN=esp-open-sdk-linux-x86_64.tar.gz - $WGET $SMINGTOOLS/$TOOLCHAIN -O $DOWNLOADS/$TOOLCHAIN - mkdir -p $UDK_ROOT - tar -zxf $DOWNLOADS/$TOOLCHAIN -C $UDK_ROOT --totals - mv $UDK_ROOT/esp-open-sdk/* $UDK_ROOT - rmdir $UDK_ROOT/esp-open-sdk + $WGET "$SMINGTOOLS/$TOOLCHAIN" -O "$DOWNLOADS/$TOOLCHAIN" + mkdir -p "$UDK_ROOT" + tar -zxf "$DOWNLOADS/$TOOLCHAIN" -C "$UDK_ROOT" --totals + mv "$UDK_ROOT/esp-open-sdk/"* "$UDK_ROOT" + rmdir "$UDK_ROOT/esp-open-sdk" fi fi @@ -20,8 +22,8 @@ if [ -n "$EQT_ROOT" ]; then printf "\n\n** Skipping Esp8266 tools installation: '$EQT_ROOT' exists\n\n" else TOOLCHAIN=x86_64-linux-gnu.xtensa-lx106-elf-e6a192b.201211.tar.gz - $WGET $SMINGTOOLS/$TOOLCHAIN -O $DOWNLOADS/$TOOLCHAIN - mkdir -p $EQT_ROOT - tar -zxf $DOWNLOADS/$TOOLCHAIN -C $EQT_ROOT --totals + $WGET "$SMINGTOOLS/$TOOLCHAIN" -O "$DOWNLOADS/$TOOLCHAIN" + mkdir -p "$EQT_ROOT" + tar -zxf "$DOWNLOADS/$TOOLCHAIN" -C "$EQT_ROOT" --totals fi fi diff --git a/Sming/Arch/Host/Tools/ci/build.run.sh b/Sming/Arch/Host/Tools/ci/build.run.sh index ada35bb8fb..dad176eb4a 100755 --- a/Sming/Arch/Host/Tools/ci/build.run.sh +++ b/Sming/Arch/Host/Tools/ci/build.run.sh @@ -1,3 +1,5 @@ +#!/bin/bash +# # Host build.run.sh SOURCE="${BASH_SOURCE[0]}" @@ -9,7 +11,7 @@ done DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" if [[ $CHECK_SCA -eq 1 ]]; then - $DIR/coverity-scan.sh + "$DIR/coverity-scan.sh" else $MAKE_PARALLEL Basic_Blink Basic_DateTime Basic_Delegates Basic_Interrupts Basic_ProgMem Basic_Serial Basic_Servo Basic_Ssl LiveDebug DEBUG_VERBOSE_LEVEL=3 fi diff --git a/Sming/Arch/Host/Tools/ci/build.setup.sh b/Sming/Arch/Host/Tools/ci/build.setup.sh index a2370b039b..f3b23005fb 100755 --- a/Sming/Arch/Host/Tools/ci/build.setup.sh +++ b/Sming/Arch/Host/Tools/ci/build.setup.sh @@ -1,3 +1,5 @@ +#!/bin/bash +# # Host build.setup.sh # Check coding style @@ -13,7 +15,7 @@ fi # Make deployment keys, etc. available set +x if [ -n "$SMING_SECRET" ]; then - openssl aes-256-cbc -d -a -iter 100 -in $CI_BUILD_DIR/Tools/ci/secrets.sh.enc -out /tmp/secrets.sh -pass pass:$SMING_SECRET + openssl aes-256-cbc -d -a -iter 100 -in "$CI_BUILD_DIR/Tools/ci/secrets.sh.enc" -out /tmp/secrets.sh -pass "pass:$SMING_SECRET" source /tmp/secrets.sh unset SMING_SECRET fi @@ -25,4 +27,4 @@ sudo ip a a dev tap0 192.168.13.1/24 sudo ip link set tap0 up # Build documentation -make -C $SMING_HOME docs +make -C "$SMING_HOME" docs diff --git a/Sming/Arch/Host/Tools/ci/coverity-scan.sh b/Sming/Arch/Host/Tools/ci/coverity-scan.sh index 984660af48..cd1a02f1e8 100755 --- a/Sming/Arch/Host/Tools/ci/coverity-scan.sh +++ b/Sming/Arch/Host/Tools/ci/coverity-scan.sh @@ -35,11 +35,11 @@ if [ "$AUTH_RES" = "Access denied" ]; then echo -e "\033[33;1mCoverity Scan API access denied. Check COVERITY_SCAN_PROJECT_NAME and COVERITY_SCAN_TOKEN.\033[0m" exit 1 else - AUTH=$(echo $AUTH_RES | ruby -e "require 'rubygems'; require 'json'; puts JSON[STDIN.read]['upload_permitted']") + AUTH=$(echo "$AUTH_RES" | ruby -e "require 'rubygems'; require 'json'; puts JSON[STDIN.read]['upload_permitted']") if [ "$AUTH" = "true" ]; then echo -e "\033[33;1mCoverity Scan analysis authorized per quota.\033[0m" else - WHEN=$(echo $AUTH_RES | ruby -e "require 'rubygems'; require 'json'; puts JSON[STDIN.read]['next_upload_permitted_at']") + WHEN=$(echo "$AUTH_RES" | ruby -e "require 'rubygems'; require 'json'; puts JSON[STDIN.read]['next_upload_permitted_at']") echo -e "\033[33;1mOops!Coverity Scan analysis engine NOT authorized until $WHEN.\033[0m" permit=false fi @@ -50,7 +50,7 @@ if [ ! -d $TOOL_BASE ]; then # Download Coverity Scan Analysis Tool if [ ! -e $TOOL_ARCHIVE ]; then echo -e "\033[33;1mDownloading Coverity Scan Analysis Tool...\033[0m" - wget -nv -O $TOOL_ARCHIVE $TOOL_URL --post-data "project=$COVERITY_SCAN_PROJECT_NAME&token=$COVERITY_SCAN_TOKEN" + wget -nv -O "$TOOL_ARCHIVE" "$TOOL_URL" --post-data "project=$COVERITY_SCAN_PROJECT_NAME&token=$COVERITY_SCAN_TOKEN" fi # Extract Coverity Scan Analysis Tool diff --git a/Sming/Arch/Host/Tools/install.sh b/Sming/Arch/Host/Tools/install.sh index 4b21178d6b..6417bdbc4a 100755 --- a/Sming/Arch/Host/Tools/install.sh +++ b/Sming/Arch/Host/Tools/install.sh @@ -1,3 +1,5 @@ +#!/bin/bash +# # Host install.sh # Required by deployment script diff --git a/Sming/Arch/Host/Tools/setup-network-linux.sh b/Sming/Arch/Host/Tools/setup-network-linux.sh index 7f5b7c2df8..e654790ff8 100755 --- a/Sming/Arch/Host/Tools/setup-network-linux.sh +++ b/Sming/Arch/Host/Tools/setup-network-linux.sh @@ -24,8 +24,8 @@ sudo sysctl net.ipv4.ip_forward=1 sudo sysctl net.ipv6.conf.default.forwarding=1 sudo sysctl net.ipv6.conf.all.forwarding=1 -sudo iptables -t nat -A POSTROUTING -o $INTERNET_IF -j MASQUERADE +sudo iptables -t nat -A POSTROUTING -o "$INTERNET_IF" -j MASQUERADE sudo iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -sudo iptables -A FORWARD -i tap0 -o $INTERNET_IF -j ACCEPT +sudo iptables -A FORWARD -i tap0 -o "$INTERNET_IF" -j ACCEPT exit 0 \ No newline at end of file diff --git a/Sming/Components/Network/src/Network/TcpConnection.h b/Sming/Components/Network/src/Network/TcpConnection.h index a930567559..58d707eab8 100644 --- a/Sming/Components/Network/src/Network/TcpConnection.h +++ b/Sming/Components/Network/src/Network/TcpConnection.h @@ -207,7 +207,7 @@ class TcpConnection : public IpConnection static err_t staticOnPoll(void* arg, tcp_pcb* tcp); static void closeTcpConnection(tcp_pcb* tpcb); - inline void checkSelfFree() + void checkSelfFree() { if(tcp == nullptr && autoSelfDestruct) { delete this; diff --git a/Sming/Components/Network/src/Network/TcpServer.cpp b/Sming/Components/Network/src/Network/TcpServer.cpp index 8e761c08b6..926a750c0e 100644 --- a/Sming/Components/Network/src/Network/TcpServer.cpp +++ b/Sming/Components/Network/src/Network/TcpServer.cpp @@ -96,7 +96,7 @@ err_t TcpServer::onAccept(tcp_pcb* clientTcp, err_t err) connections.add(client); debug_d("Opening connection. Total connections: %d", connections.count()); - onClient((TcpClient*)client); + onClient(reinterpret_cast(client)); return ERR_OK; } diff --git a/Sming/Components/Storage/Tools/hwconfig/common.py b/Sming/Components/Storage/Tools/hwconfig/common.py index 8b644def8d..af10921d7c 100644 --- a/Sming/Components/Storage/Tools/hwconfig/common.py +++ b/Sming/Components/Storage/Tools/hwconfig/common.py @@ -11,27 +11,28 @@ quiet = False def status(msg): - """ Print status message to stderr """ + """Print status message to stderr.""" if not quiet: critical(msg) def critical(msg): - """ Print critical message to stderr """ + """Print critical message to stderr.""" sys.stderr.write(msg) sys.stderr.write('\n') def fixpath(path): - """ Paths in Windows can get a little weird """ + """Paths in Windows can get a little weird """ if len(path) > 2 and path[1] != ':' and platform.system() == 'Windows' and path[2] == '/': return path[1] + ':' + path[2:] return path def parse_int(v, keywords=None): - """Generic parser for integer fields - int(x,0) with provision for - k/m/K/M suffixes and 'keyword' value lookup. + """Generic parser for integer fields. + + int(x,0) with provision for k/m/K/M suffixes and 'keyword' value lookup. """ if not isinstance(v, str): return v @@ -50,8 +51,7 @@ def parse_int(v, keywords=None): def stringnum(s): - """Return number if s contains only digits, otherwise return the string - """ + """Return number if s contains only digits, otherwise return the string.""" return int(s) if s.isdigit() else s diff --git a/Sming/Components/Storage/Tools/hwconfig/config.py b/Sming/Components/Storage/Tools/hwconfig/config.py index ddbf659888..2056c49461 100644 --- a/Sming/Components/Storage/Tools/hwconfig/config.py +++ b/Sming/Components/Storage/Tools/hwconfig/config.py @@ -93,8 +93,7 @@ def __str__(self): @classmethod def from_name(cls, name): - """Create configuration given its name and resolve options - """ + """Create configuration given its name and resolve options.""" config = Config() config.load(name) options = os.environ.get('HWCONFIG_OPTS', '').replace(' ', '') @@ -105,26 +104,26 @@ def from_name(cls, name): return config @classmethod - def from_json(cls, json, options = []): + def from_json(cls, json, options = None): config = Config() config.parse_dict(copy.deepcopy(json)) - config.parse_options(options) + if options is not None: + config.parse_options(options) config.resolve_expressions() config.partitions.sort() return config def load(self, name): - """Load a configuration recursively - """ + """Load a configuration recursively.""" filename = find_config(name) self.depends.append(filename) data = json_load(filename) self.parse_dict(data) def parse_options(self, options): - """Apply any specified options + """Apply any specified options. - Each option can be applied more than once to ensure overrides work as expected + Each option can be applied more than once to ensure overrides work as expected. """ for option in options: self.options.append(option) diff --git a/Sming/Components/Storage/Tools/hwconfig/editor.py b/Sming/Components/Storage/Tools/hwconfig/editor.py index 76ccead9d5..fa8d16f0f4 100644 --- a/Sming/Components/Storage/Tools/hwconfig/editor.py +++ b/Sming/Components/Storage/Tools/hwconfig/editor.py @@ -32,15 +32,13 @@ } def read_property(obj, name): - """Read an object property, preferring string representation - """ + """Read an object property, preferring string representation.""" value = getattr(obj, name + '_str', None) return getattr(obj, name, None) if value is None else value() def get_dict_value(dict, key, default): - """Read dictionary value, creating one if it doesn't exist - """ + """Read dictionary value, creating one if it doesn't exist.""" if not key in dict: dict[key] = default return dict[key] @@ -67,13 +65,6 @@ def __init__(self): # Don't need this information until user requests it threading.Thread(target=self._resolvePathVars).start() - def getRelativePath(self, path): - try: - res = os.path.relpath(path) - return res.replace('\\', '/') - except Exception: - return path - def resolve_path(self, path): tmp = str(path) while True: @@ -112,16 +103,14 @@ def checkProfilePath(filename): def get_id(obj): - """Get string identifier for a device or partition object - """ + """Get string identifier for a device or partition object.""" if isinstance(obj, partition.Entry): if obj.is_unused(): return obj.device.name + '/' + str(obj.address) return obj.name def resolve_id(config, id): - """Get corresponding device or partition object given the ID value provided from get_id - """ + """Get corresponding device or partition object given the ID value provided from get_id.""" elem = id.split('/') if len(elem) == 2: dev = config.devices.find_by_name(elem[0]) @@ -138,8 +127,7 @@ def resolve_device(config, id): return obj.device def resolve_key(obj, key): - """Resolve dotted key, e.g. 'build.target' refers to 'target' property of 'build' object - """ + """Resolve dotted key, e.g. 'build.target' refers to 'target' property of 'build' object.""" keys = key.split('.') while len(keys) > 1: k = keys.pop(0) @@ -150,9 +138,33 @@ def resolve_key(obj, key): return obj, keys[0] -class Field: - """Manages widget(s) and associated variable +def verify_config(json_config): + """Raises an exception if any problems are found in the configuration. + + On success, returns a consistently-ordered JSON configuration. """ + cfg = Config.from_json(json_config) + cfg.verify(False) + res = OrderedDict() + for key in config.schema['Config']['properties'].keys(): + if key in json_config: + value = json_config[key] + if key == 'devices': + names = list(dev.name for dev in cfg.devices) + elif key == 'partitions': + names = list(p.name for p in cfg.map()) + else: + res[key] = value + continue + output = res[key] = OrderedDict() + for n in names: + if n in value: + output[n] = value[n] + return res + + +class Field: + """Manages widget(s) and associated variable.""" def __init__(self, name, schema, var, widget): self.name = name self.schema = schema @@ -219,13 +231,12 @@ def setPath(self, path): except Exception: None values = list(values) - values.sort(key=lambda v: len(v)) + values.sort(key=len) self.widget.configure(values=values) return path def addScale(self, min, max, on_change): - """Add scale controls for address/size fields - """ + """Add scale controls for address/size fields.""" scale = self.scale = tk.Scale( self.widget.master, orient = tk.HORIZONTAL, @@ -305,8 +316,7 @@ def is_visible(self): class EditState(dict): - """Manage details of Config/Device/Partition editing using dictionary of Field objects - """ + """Manage details of Config/Device/Partition editing using dictionary of Field objects.""" def __init__(self, editor, objectType, dictName, obj): super().__init__(self) self.editor = editor @@ -645,7 +655,7 @@ def apply(self): if len(json_dict) == 0: del json_config[self.dictName] - self.editor.json = self.editor.verify_config(json_config) + self.editor.json = verify_config(json_config) if new_name is not None: self.name = new_name if self.objectType != 'Config': @@ -675,7 +685,7 @@ def delete(self): self.editor.reload() def get_property(self, name): - """Get field property from schema""" + """Get field property from schema.""" prop = self.schema['properties'].get(name, None) if prop is None: prop = virtual_fields[name] @@ -1205,7 +1215,7 @@ def configure(event): def apply(*args): try: json_config = json_loads(self.jsonEditor.get('1.0', 'end')) - self.json = self.verify_config(json_config) + self.json = verify_config(json_config) self.updateWindowTitle() self.reload() except Exception as err: @@ -1241,13 +1251,11 @@ def user_error(self, err): messagebox.showerror(type(err).__name__, err) def getBaseConfig(self): - """Load the base configuration - """ + """Load the base configuration.""" return Config.from_json(self.json_base_config) - + def getOptionBaseConfig(self): - """Load the base configuration with currently selected options applied - """ + """Load the base configuration with currently selected options applied.""" return Config.from_json(self.json_base_config, self.json.get('options', [])) def loadConfig(self, filename): @@ -1278,29 +1286,6 @@ def updateWindowTitle(self): name = '"' + name + '"' self.main.title(self.config.arch + ' ' + name + ' - ' + app_name) - def verify_config(self, json_config): - """Raises an exception if any problems are found in the configuration. - On success, returns a consistently-ordered JSON configuration. - """ - cfg = Config.from_json(json_config) - cfg.verify(False) - res = OrderedDict() - for key in config.schema['Config']['properties'].keys(): - if key in json_config: - value = json_config[key] - if key == 'devices': - names = list(dev.name for dev in cfg.devices) - elif key == 'partitions': - names = list(p.name for p in cfg.map()) - else: - res[key] = value - continue - output = res[key] = OrderedDict() - for n in names: - if n in value: - output[n] = value[n] - return res - def reset(self): self.tree.clear() self.map.clear() @@ -1312,6 +1297,7 @@ def reset(self): self.updateWindowTitle() class Used: + """Stores space used for a file referenced by partition.""" def __init__(self): self.text = '' self.size = 0 diff --git a/Sming/Components/Storage/Tools/hwconfig/partition.py b/Sming/Components/Storage/Tools/hwconfig/partition.py index bd275e430a..01a680b4fa 100644 --- a/Sming/Components/Storage/Tools/hwconfig/partition.py +++ b/Sming/Components/Storage/Tools/hwconfig/partition.py @@ -129,6 +129,7 @@ def parse_subtype(ptype, value): class Table(list): def __init__(self, devices): + """Create table of partitions against list of registered devices.""" super().__init__(self) self.devices = devices @@ -180,8 +181,7 @@ def buildVars(self): return dict def __getitem__(self, item): - """Allow partition table access by name or index - """ + """Allow partition table access by name or index.""" if isinstance(item, str): p = self.find_by_name(item) if p is None: @@ -190,8 +190,7 @@ def __getitem__(self, item): return super().__getitem__(item) def find_by_type(self, ptype, subtype): - """Return a partition by type & subtype, returns None if not found - """ + """Return a partition by type & subtype, returns None if not found.""" # convert ptype & subtypes names (if supplied this way) to integer values try: ptype = TYPES[ptype] @@ -228,8 +227,7 @@ def find_by_address(self, device, addr): return None def verify(self, config, secure): - """Verify partition layout - """ + """Verify partition layout.""" # verify each partition individually for p in self: p.verify(config.arch, secure) @@ -276,8 +274,7 @@ def verify(self, config, secure): last = p def parse_binary(self, b, devices): - """Construct partition table object from binary image - """ + """Construct partition table object from binary image.""" dev = None md5 = hashlib.md5() for o in range(0, len(b), PARTITION_ENTRY_SIZE): @@ -309,8 +306,7 @@ def parse_binary(self, b, devices): raise InputError("Partition table is missing an end-of-table marker") def to_binary(self, devices): - """Create binary image of partition table - """ + """Create binary image of partition table.""" dev_count = 0 dev = None result = b"" @@ -318,7 +314,7 @@ def to_binary(self, devices): if e.device != dev: if dev_count == 1: result += MD5_PARTITION_BEGIN + hashlib.md5(result).digest() - # esp32 bootloader will see this as end of partition table + # esp32 bootloader will see this as end of partition table result += struct.pack(Entry.STRUCT_FORMAT, b"\xff\xff", 0xff, 0xff, @@ -365,8 +361,7 @@ def __init__(self, device=None, name="", address=None, size=None, ptype=None, su self.unused_after = 0 def parse_dict(self, data, devices): - """Construct a partition object from JSON definition - """ + """Construct a partition object from JSON definition.""" try: # Sort out type information first v = data.pop('type', None) @@ -426,7 +421,7 @@ def buildVars(self): dict.pop('build', None) for k, v in dict.items(): k = "PARTITION_%s_%s" % (self.name, k.upper()) - res[k] = int(v) if type(v) is bool else v + res[k] = int(v) if isinstance(v, bool) else v return res @@ -558,8 +553,8 @@ def to_binary(self): class Map(Table): - """Contiguous map of flash memory - """ + + """Contiguous map of flash memory.""" def __init__(self, config): def add(table, device, name, address, size, subtype): entry = Entry(device, name, address, size, INTERNAL_TYPE, subtype) diff --git a/Sming/Components/Storage/Tools/hwconfig/storage.py b/Sming/Components/Storage/Tools/hwconfig/storage.py index c72c0fddda..410e51486b 100644 --- a/Sming/Components/Storage/Tools/hwconfig/storage.py +++ b/Sming/Components/Storage/Tools/hwconfig/storage.py @@ -49,6 +49,7 @@ def buildVars(self): return dict def __getitem__(self, item): + """Access entries by name or index.""" if isinstance(item, str): d = self.find_by_name(item) if d is None: @@ -67,6 +68,8 @@ def verify(self): dev.verify() class Device(object): + + """Initialise a storage device.""" def __init__(self, name, stype = 0, size = 0): self.name = name self.type = parse_type(stype) diff --git a/Sming/Components/ssl/Tools/make_certs.sh b/Sming/Components/ssl/Tools/make_certs.sh index be1adde987..b5d8d060dc 100755 --- a/Sming/Components/ssl/Tools/make_certs.sh +++ b/Sming/Components/ssl/Tools/make_certs.sh @@ -148,9 +148,9 @@ openssl x509 -req -in x509_512.req -out x509_bad_after.pem \ -CA ca_x509.pem -CAkey ca_key.pem # some cleanup -rm *.req -rm *.srl -rm *.conf +rm ./*.req +rm ./*.srl +rm ./*.conf # need this for the client tests openssl x509 -in ca_x509.pem -outform DER -out ca_x509.cer @@ -177,6 +177,6 @@ cat ca_x509.pem >> x509_device.pem # set default key/cert for use in the server xxd -i x509_1024.cer | sed -e \ - "s/x509_1024_cer/default_certificate/" > $SSL_INCLUDE_DIR/cert.h + "s/x509_1024_cer/default_certificate/" > "$SSL_INCLUDE_DIR/cert.h" xxd -i key_1024 | sed -e \ - "s/key_1024/default_private_key/" > $SSL_INCLUDE_DIR/private_key.h + "s/key_1024/default_private_key/" > "$SSL_INCLUDE_DIR/private_key.h" diff --git a/Sming/Core/Data/Stream/ReadWriteStream.cpp b/Sming/Core/Data/Stream/ReadWriteStream.cpp index af972a11a8..d4e36bf76d 100644 --- a/Sming/Core/Data/Stream/ReadWriteStream.cpp +++ b/Sming/Core/Data/Stream/ReadWriteStream.cpp @@ -21,9 +21,8 @@ size_t ReadWriteStream::copyFrom(IDataSourceStream* source, size_t size) auto bufSize = std::min(size, maxBufferSize); char buffer[bufSize]; size_t total = 0; - size_t count; while(!source->isFinished()) { - count = source->readMemoryBlock(buffer, bufSize); + size_t count = source->readMemoryBlock(buffer, bufSize); if(count == 0) { continue; } diff --git a/Sming/Libraries/CapacitiveSensor/CapacitiveSensor.cpp b/Sming/Libraries/CapacitiveSensor/CapacitiveSensor.cpp index 6a3b27155d..889c8798de 100644 --- a/Sming/Libraries/CapacitiveSensor/CapacitiveSensor.cpp +++ b/Sming/Libraries/CapacitiveSensor/CapacitiveSensor.cpp @@ -22,7 +22,7 @@ // Constructor ///////////////////////////////////////////////////////////////// // Function that handles the creation and setup of instances -CapacitiveSensor::CapacitiveSensor(uint8_t sendPin, uint8_t receivePin) +CapacitiveSensor::CapacitiveSensor(uint8_t sendPin, uint8_t receivePin): total(0) { // initialize this instance's variables // Serial.begin(9600); // for debugging diff --git a/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp b/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp index 3ee7133304..ade32d2983 100644 --- a/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp +++ b/Sming/Libraries/OtaUpgrade/OtaUpgrade/BasicStream.cpp @@ -23,10 +23,9 @@ extern const uint64_t BuildTimestamp; DECLARE_FSTR_ARRAY(AppFlashRegionOffsets, uint32_t); -BasicStream::Slot::Slot() +// Lookup slot details from partition table +BasicStream::Slot::Slot() : partition(OtaManager.getNextBootPartition()) { - // Lookup slot details from partition table - partition = OtaManager.getNextBootPartition(); } BasicStream::BasicStream() diff --git a/Sming/Libraries/Spiffs/src/FileMeta.cpp b/Sming/Libraries/Spiffs/src/FileMeta.cpp index f603a8f27d..e7ea32dbe7 100644 --- a/Sming/Libraries/Spiffs/src/FileMeta.cpp +++ b/Sming/Libraries/Spiffs/src/FileMeta.cpp @@ -64,6 +64,7 @@ int SpiffsMetaBuffer::enumxattr(AttributeEnumCallback callback, void* buffer, si } } +#if SPIFFS_USER_METALEN for(unsigned i = 0; i < SPIFFS_USER_METALEN;) { uint8_t tagIndex = user[i++]; uint8_t tagSize = user[i++]; @@ -76,6 +77,7 @@ int SpiffsMetaBuffer::enumxattr(AttributeEnumCallback callback, void* buffer, si } i += tagSize; } +#endif return count; } diff --git a/Sming/Services/CommandProcessing/CommandExecutor.h b/Sming/Services/CommandProcessing/CommandExecutor.h index 9bf51f749d..2e74f548ba 100644 --- a/Sming/Services/CommandProcessing/CommandExecutor.h +++ b/Sming/Services/CommandProcessing/CommandExecutor.h @@ -20,6 +20,9 @@ class CommandExecutor { public: + CommandExecutor(const CommandExecutor&) = delete; + CommandExecutor& operator=(const CommandExecutor&) = delete; + #ifndef DISABLE_NETWORK CommandExecutor(TcpClient* cmdClient); CommandExecutor(WebsocketConnection* reqSocket); diff --git a/Tools/ci/build.sh b/Tools/ci/build.sh index 55ec52efc3..d155155e36 100755 --- a/Tools/ci/build.sh +++ b/Tools/ci/build.sh @@ -4,8 +4,8 @@ set -ex # exit with nonzero exit code if anything fails # Build times benefit from parallel building export MAKE_PARALLEL="make -j3" -cd $SMING_HOME -source Arch/$SMING_ARCH/Tools/ci/build.setup.sh +cd "$SMING_HOME" +source "Arch/$SMING_ARCH/Tools/ci/build.setup.sh" # Don't leak this! unset SMING_SECRET @@ -14,21 +14,21 @@ env # Move samples and tests into directory outside of the Sming repo. export SMING_PROJECTS_DIR=$HOME/projects -mkdir -p $SMING_PROJECTS_DIR -cd $SMING_HOME/.. -mv samples $SMING_PROJECTS_DIR -mv tests $SMING_PROJECTS_DIR +mkdir -p "$SMING_PROJECTS_DIR" +cd "$SMING_HOME/.." +mv samples "$SMING_PROJECTS_DIR" +mv tests "$SMING_PROJECTS_DIR" # Full compile checks please export STRICT=1 # Diagnostic info -cd $SMING_PROJECTS_DIR/samples/Basic_Blink +cd "$SMING_PROJECTS_DIR/samples/Basic_Blink" make help make list-config $MAKE_PARALLEL # Run ARCH build/tests -cd $SMING_HOME -source Arch/$SMING_ARCH/Tools/ci/build.run.sh +cd "$SMING_HOME" +source "Arch/$SMING_ARCH/Tools/ci/build.run.sh" diff --git a/Tools/ci/deploy.sh b/Tools/ci/deploy.sh index 69c5e7c75f..637858fa83 100755 --- a/Tools/ci/deploy.sh +++ b/Tools/ci/deploy.sh @@ -2,14 +2,14 @@ set -ex # exit with nonzero exit code if anything fails TAG=$1 -if [ -z $TAG ]; then +if [ -z "$TAG" ]; then printf "Usage:\n\t$0 \n"; exit 1; fi # [ Create archive of all submodules used in this release (pulled in during documentation build stage) ] -cd $CI_BUILD_DIR -ALL_SUBMODULE_DIRS=$(find $SMING_HOME -name '.submodule' | xargs dirname | sed 's/^\(.*\)\/\(Sming\/.*\)$/\2/') +cd "$CI_BUILD_DIR" +ALL_SUBMODULE_DIRS=$(find "$SMING_HOME" -name '.submodule' | xargs dirname | sed 's/^\(.*\)\/\(Sming\/.*\)$/\2/') SUBMODULE_ARCHIVE=sming-submodules.tgz tar czf $SUBMODULE_ARCHIVE $ALL_SUBMODULE_DIRS @@ -41,7 +41,7 @@ PACKAGES_TO_CHANGE="sming sming.source" for PACKAGE in $PACKAGES_TO_CHANGE; do - xmlstarlet ed --inplace -N "ns=http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd" -u "/ns:package/ns:metadata/ns:version" -v "$TAG" packages/$PACKAGE/*.nuspec; + xmlstarlet ed --inplace -N "ns=http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd" -u "/ns:package/ns:metadata/ns:version" -v "$TAG" "packages/$PACKAGE/$PACKAGE.nuspec"; done git config user.email "appveyor@sminghub.local" diff --git a/Tools/export.sh b/Tools/export.sh index 81cf497ea8..12365503f1 100755 --- a/Tools/export.sh +++ b/Tools/export.sh @@ -1,3 +1,4 @@ +#!/bin/bash # # Set default environment variables from shell: # @@ -18,12 +19,12 @@ # if [ -z "$SMING_HOME" ]; then - if [ $(basename $SHELL) = "zsh" ]; then + if [ "$(basename $SHELL)" = "zsh" ]; then _SOURCE=${(%):-%N} else _SOURCE=$BASH_SOURCE fi - export SMING_HOME=$(readlink -m $_SOURCE/../../Sming) + export SMING_HOME=$(readlink -m "$_SOURCE/../../Sming") fi # Common diff --git a/Tools/install.sh b/Tools/install.sh index 76fdc2eeff..68b1f27c55 100755 --- a/Tools/install.sh +++ b/Tools/install.sh @@ -53,7 +53,7 @@ SMINGTOOLS=https://github.com/SmingHub/SmingTools/releases/download/1.0 # Set default environment variables and WGET options if [ -z "$APPVEYOR" ]; then - source $(dirname $BASH_SOURCE)/export.sh + source $(dirname "$BASH_SOURCE")/export.sh # Ensure default path is writeable sudo mkdir -p /opt @@ -153,11 +153,11 @@ if [ -f "/usr/bin/clang-format-8" ]; then sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-8 100 fi -python3 -m pip install --upgrade pip -r $SMING_HOME/../Tools/requirements.txt +python3 -m pip install --upgrade pip -r "$SMING_HOME/../Tools/requirements.txt" install() { - source $SMING_HOME/Arch/$1/Tools/install.sh + source "$SMING_HOME/Arch/$1/Tools/install.sh" } if [ $inst_host -eq 1 ]; then @@ -165,7 +165,7 @@ if [ $inst_host -eq 1 ]; then fi if [ $inst_doc -eq 1 ]; then - source $SMING_HOME/../docs/Tools/install.sh + source "$SMING_HOME/../docs/Tools/install.sh" fi if [ $inst_esp8266 -eq 1 ]; then @@ -177,7 +177,7 @@ if [ $inst_esp32 -eq 1 ]; then fi if [ -z "$KEEP_DOWNLOADS" ]; then - rm -f $DOWNLOADS/* + rm -f "$DOWNLOADS/*" fi diff --git a/docs/Tools/install.sh b/docs/Tools/install.sh old mode 100644 new mode 100755 From e66e4f3049267df751c7de5f2e4675e1b2e50fbc Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 15 Oct 2021 08:48:48 +0100 Subject: [PATCH 079/130] Initial support for Rp2040 architecture (#2392) Raspberry PI Pico, etc. **Initial RP2040 implementation** Basic system working with: - timers - task callbacks - flash memory/filing systems - UART driver implementation See arch. readme for details. **Support non-recursive submodule imports** The pico-sdk contains TinyUSB which isn't used, and is massive (c. 1.7GB). Components can block recursive initialisation with a `.no-recursive` file. **HostTests** Allow building/running without network Add System module with commonly-used system functions FP calculations in Clock module can be slightly inaccurate, so allow error of 2 ticks **Compile fixes** Fix some compiler type mismatch errors. Now builds on ARM architecture so invariably some discrepancies in system types. In particular, printf format strings are a pain! **Build/Installation/CI** Add installer and CI builds, add HostTests to all architectures Move complex AWK expression into script file The 'help' output is generated with AWK, moved that into separate script file makes it easier to read. --- .gitmodules | 14 + Sming/Arch/Esp32/Components/esp32/README.rst | 7 - .../Esp32/Components/sming-arch/component.mk | 1 - Sming/Arch/Esp32/build.mk | 1 + .../Arch/Esp8266/Components/fatfs/README.rst | 6 - Sming/Arch/Esp8266/Components/fatfs/diskio.h | 85 - Sming/Arch/Esp8266/Components/fatfs/ff.c | 4635 ----------------- Sming/Arch/Esp8266/Components/fatfs/ff.h | 350 -- Sming/Arch/Esp8266/Components/fatfs/ffconf.h | 266 - Sming/Arch/Esp8266/Components/fatfs/integer.h | 37 - .../Components/sming-arch/component.mk | 1 - Sming/Arch/Esp8266/build.mk | 1 + Sming/Arch/Host/Components/fatfs/README.rst | 4 - Sming/Arch/Host/build.mk | 1 + Sming/Arch/Rp2040/Components/driver/Kconfig | 22 + .../Arch/Rp2040/Components/driver/README.rst | 10 + .../Rp2040/Components/driver/component.mk | 8 + Sming/Arch/Rp2040/Components/driver/gpio.rst | 8 + .../Rp2040/Components/driver/hw_timer.cpp | 63 + .../Rp2040/Components/driver/hw_timer.rst | 12 + .../Components/driver/include/driver/gpio.h | 28 + .../driver/include/driver/hw_timer.h | 156 + .../driver/include/driver/os_timer.h | 57 + .../Components/driver/include/driver/pwm.h | 88 + .../Components/driver/include/driver/uart.h | 7 + .../Rp2040/Components/driver/os_timer.cpp | 199 + .../Rp2040/Components/driver/os_timer.rst | 8 + Sming/Arch/Rp2040/Components/driver/pwm.rst | 9 + Sming/Arch/Rp2040/Components/driver/uart.cpp | 857 +++ Sming/Arch/Rp2040/Components/driver/uart.rst | 8 + .../Arch/Rp2040/Components/gdbstub/README.rst | 6 + .../Rp2040/Components/gdbstub/component.mk | 6 + .../Rp2040/Components/gdbstub/gdb_syscall.cpp | 8 + Sming/Arch/Rp2040/Components/gdbstub/gdbcmds | 10 + .../Arch/Rp2040/Components/gdbstub/gdbstub.c | 29 + Sming/Arch/Rp2040/Components/libc/README.rst | 4 + .../Arch/Rp2040/Components/libc/component.mk | 11 + Sming/Arch/Rp2040/Components/libc/src/heap.c | 16 + .../Components/libc/src/include/esp_attr.h | 32 + .../Rp2040/Components/libc/src/include/heap.h | 13 + .../libc/src/include/sys/pgmspace.h | 80 + .../Rp2040/Components/picotool/README.rst | 6 + .../Rp2040/Components/picotool/component.mk | 35 + .../Components/picotool/libusb/libusb-1.0.dll | Bin 0 -> 262758 bytes .../Components/picotool/libusb/libusb.h | 2113 ++++++++ .../Arch/Rp2040/Components/picotool/picotool | 1 + Sming/Arch/Rp2040/Components/rp2040/Kconfig | 15 + .../Arch/Rp2040/Components/rp2040/README.rst | 30 + .../Rp2040/Components/rp2040/board-info.awk | 27 + .../Rp2040/Components/rp2040/component.mk | 162 + Sming/Arch/Rp2040/Components/rp2040/pico-sdk | 1 + .../Components/rp2040/pico-sdk.no-recursive} | 0 .../Rp2040/Components/rp2040/pico-sdk.patch | 13 + .../Components/rp2040/sdk/CMakeLists.txt | 76 + .../Components/rp2040/sdk/pico_bit_ops.mk | 9 + .../Components/rp2040/sdk/pico_divider.mk | 9 + .../Components/rp2040/sdk/pico_double.mk | 64 + .../Components/rp2040/sdk/pico_float.mk | 64 + .../Components/rp2040/sdk/pico_int64_ops.mk | 4 + .../Components/rp2040/sdk/pico_mem_ops.mk | 11 + .../Components/rp2040/sdk/pico_stdio.mk | 8 + .../Components/rp2040/sdk/tusb_config.h | 92 + Sming/Arch/Rp2040/Components/rp2040/src/clk.c | 116 + .../Components/rp2040/src/esp_cplusplus.cpp | 29 + .../Components/rp2040/src/functexcept.cpp | 93 + .../Components/rp2040/src/include/esp_clk.h | 19 + .../Components/rp2040/src/include/esp_libc.h | 17 + .../Components/rp2040/src/include/esp_sleep.h | 21 + .../rp2040/src/include/esp_system.h | 46 + .../rp2040/src/include/esp_systemapi.h | 51 + .../Components/rp2040/src/include/esp_tasks.h | 44 + .../rp2040/src/include/esp_tasks_ll.h | 16 + .../Arch/Rp2040/Components/rp2040/src/libc.c | 38 + .../Arch/Rp2040/Components/rp2040/src/sleep.c | 10 + .../Rp2040/Components/rp2040/src/startup.cpp | 130 + .../Rp2040/Components/rp2040/src/system.cpp | 97 + .../Rp2040/Components/rp2040/src/tasks.cpp | 110 + .../Rp2040/Components/sming-arch/README.rst | 5 + .../Rp2040/Components/sming-arch/component.mk | 25 + .../Rp2040/Components/spi_flash/README.rst | 10 + .../Arch/Rp2040/Components/spi_flash/api.rst | 6 + .../Rp2040/Components/spi_flash/component.mk | 2 + .../Rp2040/Components/spi_flash/flashmem.cpp | 408 ++ .../spi_flash/include/esp_spi_flash.h | 175 + .../spi_flash/include/iram_precache.h | 17 + Sming/Arch/Rp2040/Components/uf2/LICENSE.txt | 25 + Sming/Arch/Rp2040/Components/uf2/README.rst | 18 + Sming/Arch/Rp2040/Components/uf2/component.mk | 61 + Sming/Arch/Rp2040/Components/uf2/uf2.h | 63 + Sming/Arch/Rp2040/Components/uf2/uf2.md | 356 ++ Sming/Arch/Rp2040/Components/uf2/uf2conv.md | 35 + Sming/Arch/Rp2040/Components/uf2/uf2conv.py | 400 ++ .../Rp2040/Components/uf2/uf2families.json | 172 + Sming/Arch/Rp2040/Core/.cs | 0 Sming/Arch/Rp2040/Core/Digital.cpp | 132 + Sming/Arch/Rp2040/Core/HardwarePWM.cpp.todo | 136 + Sming/Arch/Rp2040/Core/Interrupts.cpp | 130 + Sming/Arch/Rp2040/Core/SPI.cpp.todo | 345 ++ Sming/Arch/Rp2040/Core/SPI.h | 63 + Sming/Arch/Rp2040/Core/adc.cpp.todo | 10 + Sming/Arch/Rp2040/Core/pins_arduino.h | 34 + Sming/Arch/Rp2040/Core/twi_arch.h | 19 + Sming/Arch/Rp2040/Platform/.cs | 0 Sming/Arch/Rp2040/Platform/Clocks.h | 25 + Sming/Arch/Rp2040/Platform/RTC.cpp | 79 + Sming/Arch/Rp2040/README.rst | 224 + .../Rp2040/Services/Profiling/TaskStat.cpp | 32 + .../Arch/Rp2040/System/include/user_config.h | 21 + Sming/Arch/Rp2040/Tools/ci/build.run.cmd | 12 + Sming/Arch/Rp2040/Tools/ci/build.run.sh | 7 + Sming/Arch/Rp2040/Tools/ci/build.setup.cmd | 1 + Sming/Arch/Rp2040/Tools/ci/build.setup.sh | 3 + Sming/Arch/Rp2040/Tools/ci/install.cmd | 19 + Sming/Arch/Rp2040/Tools/install.sh | 19 + Sming/Arch/Rp2040/Tools/memanalyzer.py | 72 + Sming/Arch/Rp2040/app.mk | 38 + Sming/Arch/Rp2040/build.mk | 53 + Sming/Arch/Rp2040/standard.hw | 19 + Sming/Components/FlashString | 2 +- .../Storage/Tools/hwconfig/partition.py | 6 +- Sming/Components/axtls-8266/axtls-8266.patch | 26 +- .../Esp32 => }/Components/fatfs/README.rst | 0 .../Esp32 => }/Components/fatfs/diskio.h | 0 Sming/{Arch/Esp32 => }/Components/fatfs/ff.c | 0 Sming/{Arch/Esp32 => }/Components/fatfs/ff.h | 0 .../Esp32 => }/Components/fatfs/ffconf.h | 0 .../Esp32 => }/Components/fatfs/integer.h | 0 Sming/Components/malloc_count/component.mk | 14 +- .../Components/malloc_count/malloc_count.cpp | 21 +- Sming/Core/Interrupts.h | 6 +- Sming/Core/SmingCore.h | 2 - Sming/Kconfig | 2 +- Sming/Libraries/SDCard/SDCard.cpp | 1 - Sming/Libraries/SDCard/SDCard.h | 2 + Sming/Libraries/SDCard/component.mk | 1 + Sming/Platform/System.h | 15 +- Sming/System/include/gdb/gdb_hooks.h | 4 +- Sming/Wiring/WConstants.h | 3 +- Sming/build.mk | 42 +- Sming/building.rst | 11 + Sming/help.awk | 21 + Tools/ci/build.sh | 3 + Tools/export.sh | 3 + Tools/install.sh | 7 +- appveyor.yml | 5 + docs/source/arch/rp2040/debugging/index.rst | 6 + docs/source/information/debugging.rst | 2 +- .../information/develop/documentation.rst | 2 +- .../information/develop/external-sources.rst | 2 +- docs/source/link-roles.py | 1 + samples/Basic_Blink/app/application.cpp | 4 + samples/Basic_ProgMem/app/TestProgmem.cpp | 13 +- samples/Basic_Serial/component.mk | 2 +- samples/Basic_Storage/app/application.cpp | 2 +- ...asic_storage_2m.hw => basic_storage-2m.hw} | 8 +- samples/Basic_Storage/component.mk | 2 +- tests/HostTests/Kconfig | 13 + tests/HostTests/component.mk | 13 +- tests/HostTests/host-tests-1m.hw | 17 + tests/HostTests/include/modules.h | 21 +- tests/HostTests/modules/BitSet.cpp | 1 + tests/HostTests/modules/Clocks.cpp | 5 +- .../Network}/Arch/Host/Hosted.cpp | 0 .../Network}/Arch/Host/HttpRequest.cpp | 0 .../Network}/Arch/Host/TcpClient.cpp | 0 .../modules/{ => Network}/Base64.cpp | 23 + .../HostTests/modules/{ => Network}/Http.cpp | 0 tests/HostTests/modules/{ => Network}/Url.cpp | 0 tests/HostTests/modules/SpiFlash.cpp | 10 +- tests/HostTests/modules/Stream.cpp | 22 +- tests/HostTests/modules/System.cpp | 78 + 171 files changed, 8952 insertions(+), 5504 deletions(-) delete mode 100644 Sming/Arch/Esp8266/Components/fatfs/README.rst delete mode 100644 Sming/Arch/Esp8266/Components/fatfs/diskio.h delete mode 100644 Sming/Arch/Esp8266/Components/fatfs/ff.c delete mode 100644 Sming/Arch/Esp8266/Components/fatfs/ff.h delete mode 100644 Sming/Arch/Esp8266/Components/fatfs/ffconf.h delete mode 100644 Sming/Arch/Esp8266/Components/fatfs/integer.h delete mode 100644 Sming/Arch/Host/Components/fatfs/README.rst create mode 100644 Sming/Arch/Rp2040/Components/driver/Kconfig create mode 100644 Sming/Arch/Rp2040/Components/driver/README.rst create mode 100644 Sming/Arch/Rp2040/Components/driver/component.mk create mode 100644 Sming/Arch/Rp2040/Components/driver/gpio.rst create mode 100644 Sming/Arch/Rp2040/Components/driver/hw_timer.cpp create mode 100644 Sming/Arch/Rp2040/Components/driver/hw_timer.rst create mode 100644 Sming/Arch/Rp2040/Components/driver/include/driver/gpio.h create mode 100644 Sming/Arch/Rp2040/Components/driver/include/driver/hw_timer.h create mode 100644 Sming/Arch/Rp2040/Components/driver/include/driver/os_timer.h create mode 100644 Sming/Arch/Rp2040/Components/driver/include/driver/pwm.h create mode 100644 Sming/Arch/Rp2040/Components/driver/include/driver/uart.h create mode 100644 Sming/Arch/Rp2040/Components/driver/os_timer.cpp create mode 100644 Sming/Arch/Rp2040/Components/driver/os_timer.rst create mode 100644 Sming/Arch/Rp2040/Components/driver/pwm.rst create mode 100644 Sming/Arch/Rp2040/Components/driver/uart.cpp create mode 100644 Sming/Arch/Rp2040/Components/driver/uart.rst create mode 100644 Sming/Arch/Rp2040/Components/gdbstub/README.rst create mode 100644 Sming/Arch/Rp2040/Components/gdbstub/component.mk create mode 100644 Sming/Arch/Rp2040/Components/gdbstub/gdb_syscall.cpp create mode 100644 Sming/Arch/Rp2040/Components/gdbstub/gdbcmds create mode 100644 Sming/Arch/Rp2040/Components/gdbstub/gdbstub.c create mode 100644 Sming/Arch/Rp2040/Components/libc/README.rst create mode 100644 Sming/Arch/Rp2040/Components/libc/component.mk create mode 100644 Sming/Arch/Rp2040/Components/libc/src/heap.c create mode 100644 Sming/Arch/Rp2040/Components/libc/src/include/esp_attr.h create mode 100644 Sming/Arch/Rp2040/Components/libc/src/include/heap.h create mode 100644 Sming/Arch/Rp2040/Components/libc/src/include/sys/pgmspace.h create mode 100644 Sming/Arch/Rp2040/Components/picotool/README.rst create mode 100644 Sming/Arch/Rp2040/Components/picotool/component.mk create mode 100644 Sming/Arch/Rp2040/Components/picotool/libusb/libusb-1.0.dll create mode 100644 Sming/Arch/Rp2040/Components/picotool/libusb/libusb.h create mode 160000 Sming/Arch/Rp2040/Components/picotool/picotool create mode 100644 Sming/Arch/Rp2040/Components/rp2040/Kconfig create mode 100644 Sming/Arch/Rp2040/Components/rp2040/README.rst create mode 100644 Sming/Arch/Rp2040/Components/rp2040/board-info.awk create mode 100644 Sming/Arch/Rp2040/Components/rp2040/component.mk create mode 160000 Sming/Arch/Rp2040/Components/rp2040/pico-sdk rename Sming/Arch/{Host/Components/fatfs/ff.h => Rp2040/Components/rp2040/pico-sdk.no-recursive} (100%) create mode 100644 Sming/Arch/Rp2040/Components/rp2040/pico-sdk.patch create mode 100644 Sming/Arch/Rp2040/Components/rp2040/sdk/CMakeLists.txt create mode 100644 Sming/Arch/Rp2040/Components/rp2040/sdk/pico_bit_ops.mk create mode 100644 Sming/Arch/Rp2040/Components/rp2040/sdk/pico_divider.mk create mode 100644 Sming/Arch/Rp2040/Components/rp2040/sdk/pico_double.mk create mode 100644 Sming/Arch/Rp2040/Components/rp2040/sdk/pico_float.mk create mode 100644 Sming/Arch/Rp2040/Components/rp2040/sdk/pico_int64_ops.mk create mode 100644 Sming/Arch/Rp2040/Components/rp2040/sdk/pico_mem_ops.mk create mode 100644 Sming/Arch/Rp2040/Components/rp2040/sdk/pico_stdio.mk create mode 100644 Sming/Arch/Rp2040/Components/rp2040/sdk/tusb_config.h create mode 100644 Sming/Arch/Rp2040/Components/rp2040/src/clk.c create mode 100644 Sming/Arch/Rp2040/Components/rp2040/src/esp_cplusplus.cpp create mode 100644 Sming/Arch/Rp2040/Components/rp2040/src/functexcept.cpp create mode 100644 Sming/Arch/Rp2040/Components/rp2040/src/include/esp_clk.h create mode 100644 Sming/Arch/Rp2040/Components/rp2040/src/include/esp_libc.h create mode 100644 Sming/Arch/Rp2040/Components/rp2040/src/include/esp_sleep.h create mode 100644 Sming/Arch/Rp2040/Components/rp2040/src/include/esp_system.h create mode 100644 Sming/Arch/Rp2040/Components/rp2040/src/include/esp_systemapi.h create mode 100644 Sming/Arch/Rp2040/Components/rp2040/src/include/esp_tasks.h create mode 100644 Sming/Arch/Rp2040/Components/rp2040/src/include/esp_tasks_ll.h create mode 100644 Sming/Arch/Rp2040/Components/rp2040/src/libc.c create mode 100644 Sming/Arch/Rp2040/Components/rp2040/src/sleep.c create mode 100644 Sming/Arch/Rp2040/Components/rp2040/src/startup.cpp create mode 100644 Sming/Arch/Rp2040/Components/rp2040/src/system.cpp create mode 100644 Sming/Arch/Rp2040/Components/rp2040/src/tasks.cpp create mode 100644 Sming/Arch/Rp2040/Components/sming-arch/README.rst create mode 100644 Sming/Arch/Rp2040/Components/sming-arch/component.mk create mode 100644 Sming/Arch/Rp2040/Components/spi_flash/README.rst create mode 100644 Sming/Arch/Rp2040/Components/spi_flash/api.rst create mode 100644 Sming/Arch/Rp2040/Components/spi_flash/component.mk create mode 100644 Sming/Arch/Rp2040/Components/spi_flash/flashmem.cpp create mode 100644 Sming/Arch/Rp2040/Components/spi_flash/include/esp_spi_flash.h create mode 100644 Sming/Arch/Rp2040/Components/spi_flash/include/iram_precache.h create mode 100644 Sming/Arch/Rp2040/Components/uf2/LICENSE.txt create mode 100644 Sming/Arch/Rp2040/Components/uf2/README.rst create mode 100644 Sming/Arch/Rp2040/Components/uf2/component.mk create mode 100644 Sming/Arch/Rp2040/Components/uf2/uf2.h create mode 100644 Sming/Arch/Rp2040/Components/uf2/uf2.md create mode 100644 Sming/Arch/Rp2040/Components/uf2/uf2conv.md create mode 100644 Sming/Arch/Rp2040/Components/uf2/uf2conv.py create mode 100644 Sming/Arch/Rp2040/Components/uf2/uf2families.json create mode 100644 Sming/Arch/Rp2040/Core/.cs create mode 100644 Sming/Arch/Rp2040/Core/Digital.cpp create mode 100644 Sming/Arch/Rp2040/Core/HardwarePWM.cpp.todo create mode 100644 Sming/Arch/Rp2040/Core/Interrupts.cpp create mode 100644 Sming/Arch/Rp2040/Core/SPI.cpp.todo create mode 100644 Sming/Arch/Rp2040/Core/SPI.h create mode 100644 Sming/Arch/Rp2040/Core/adc.cpp.todo create mode 100644 Sming/Arch/Rp2040/Core/pins_arduino.h create mode 100644 Sming/Arch/Rp2040/Core/twi_arch.h create mode 100644 Sming/Arch/Rp2040/Platform/.cs create mode 100644 Sming/Arch/Rp2040/Platform/Clocks.h create mode 100644 Sming/Arch/Rp2040/Platform/RTC.cpp create mode 100644 Sming/Arch/Rp2040/README.rst create mode 100644 Sming/Arch/Rp2040/Services/Profiling/TaskStat.cpp create mode 100644 Sming/Arch/Rp2040/System/include/user_config.h create mode 100644 Sming/Arch/Rp2040/Tools/ci/build.run.cmd create mode 100644 Sming/Arch/Rp2040/Tools/ci/build.run.sh create mode 100644 Sming/Arch/Rp2040/Tools/ci/build.setup.cmd create mode 100755 Sming/Arch/Rp2040/Tools/ci/build.setup.sh create mode 100644 Sming/Arch/Rp2040/Tools/ci/install.cmd create mode 100755 Sming/Arch/Rp2040/Tools/install.sh create mode 100644 Sming/Arch/Rp2040/Tools/memanalyzer.py create mode 100644 Sming/Arch/Rp2040/app.mk create mode 100644 Sming/Arch/Rp2040/build.mk create mode 100644 Sming/Arch/Rp2040/standard.hw rename Sming/{Arch/Esp32 => }/Components/fatfs/README.rst (100%) rename Sming/{Arch/Esp32 => }/Components/fatfs/diskio.h (100%) rename Sming/{Arch/Esp32 => }/Components/fatfs/ff.c (100%) rename Sming/{Arch/Esp32 => }/Components/fatfs/ff.h (100%) rename Sming/{Arch/Esp32 => }/Components/fatfs/ffconf.h (100%) rename Sming/{Arch/Esp32 => }/Components/fatfs/integer.h (100%) create mode 100644 Sming/Libraries/SDCard/component.mk create mode 100644 Sming/help.awk create mode 100644 docs/source/arch/rp2040/debugging/index.rst rename samples/Basic_Storage/{basic_storage_2m.hw => basic_storage-2m.hw} (85%) create mode 100644 tests/HostTests/Kconfig create mode 100644 tests/HostTests/host-tests-1m.hw rename tests/HostTests/{ => modules/Network}/Arch/Host/Hosted.cpp (100%) rename tests/HostTests/{ => modules/Network}/Arch/Host/HttpRequest.cpp (100%) rename tests/HostTests/{ => modules/Network}/Arch/Host/TcpClient.cpp (100%) rename tests/HostTests/modules/{ => Network}/Base64.cpp (80%) rename tests/HostTests/modules/{ => Network}/Http.cpp (100%) rename tests/HostTests/modules/{ => Network}/Url.cpp (100%) create mode 100644 tests/HostTests/modules/System.cpp diff --git a/.gitmodules b/.gitmodules index 99b5694b90..cf0fb63baa 100644 --- a/.gitmodules +++ b/.gitmodules @@ -112,6 +112,20 @@ ### NONE ### +# +# `Rp2040` Components +# + +[submodule "Rp2040.picotool"] + path = Sming/Arch/Rp2040/Components/picotool/picotool + url = https://github.com/mikee47/picotool + ignore = dirty + +[submodule "Rp2040.Sdk"] + path = Sming/Arch/Rp2040/Components/rp2040/pico-sdk + url = https://github.com/raspberrypi/pico-sdk + ignore = dirty + # # `Host` Components # diff --git a/Sming/Arch/Esp32/Components/esp32/README.rst b/Sming/Arch/Esp32/Components/esp32/README.rst index 94c906a6ee..fad1bd1905 100644 --- a/Sming/Arch/Esp32/Components/esp32/README.rst +++ b/Sming/Arch/Esp32/Components/esp32/README.rst @@ -21,13 +21,6 @@ Followed by:: Configuration variables ----------------------- -Option variables: - -.. envvar:: ESP_VARIANT - - Build for for esp32 or esp32s2 device - - The following variables may need to be changed if tools are installed in a different location, or if multiple versions are installed. By default, the most current version will be used. diff --git a/Sming/Arch/Esp32/Components/sming-arch/component.mk b/Sming/Arch/Esp32/Components/sming-arch/component.mk index 24c3b98a72..27cbf54268 100644 --- a/Sming/Arch/Esp32/Components/sming-arch/component.mk +++ b/Sming/Arch/Esp32/Components/sming-arch/component.mk @@ -18,7 +18,6 @@ COMPONENT_DEPENDS := \ spi_flash \ driver \ heap \ - fatfs \ esp32 \ gdbstub \ esptool diff --git a/Sming/Arch/Esp32/build.mk b/Sming/Arch/Esp32/build.mk index 0092030400..d4d3ff331d 100644 --- a/Sming/Arch/Esp32/build.mk +++ b/Sming/Arch/Esp32/build.mk @@ -113,6 +113,7 @@ CC := $(TOOLSPEC)-gcc CXX := $(TOOLSPEC)-g++ AR := $(TOOLSPEC)-ar LD := $(TOOLSPEC)-gcc +NM := $(TOOLSPEC)-nm OBJCOPY := $(TOOLSPEC)-objcopy OBJDUMP := $(TOOLSPEC)-objdump GDB := $(TOOLSPEC)-gdb diff --git a/Sming/Arch/Esp8266/Components/fatfs/README.rst b/Sming/Arch/Esp8266/Components/fatfs/README.rst deleted file mode 100644 index 0f85a7d7a7..0000000000 --- a/Sming/Arch/Esp8266/Components/fatfs/README.rst +++ /dev/null @@ -1,6 +0,0 @@ -FAT Filing System -================= - -Required by the SDCard library. - -http://elm-chan.org/fsw/ff/00index_e.html diff --git a/Sming/Arch/Esp8266/Components/fatfs/diskio.h b/Sming/Arch/Esp8266/Components/fatfs/diskio.h deleted file mode 100644 index c7e901d332..0000000000 --- a/Sming/Arch/Esp8266/Components/fatfs/diskio.h +++ /dev/null @@ -1,85 +0,0 @@ -/*----------------------------------------------------------------------- -/ Low level disk interface modlue include file (C)ChaN, 2014 -/-----------------------------------------------------------------------*/ - -#ifndef _DISKIO_DEFINED -#define _DISKIO_DEFINED - -#ifdef __cplusplus -extern "C" { -#endif - -#include "integer.h" - - -/* Status of Disk Functions */ -typedef BYTE DSTATUS; - -/* Results of Disk Functions */ -typedef enum { - RES_OK = 0, /* 0: Successful */ - RES_ERROR, /* 1: R/W Error */ - RES_WRPRT, /* 2: Write Protected */ - RES_NOTRDY, /* 3: Not Ready */ - RES_PARERR /* 4: Invalid Parameter */ -} DRESULT; - - -/*---------------------------------------*/ -/* Prototypes for disk control functions */ - - -DSTATUS disk_initialize (BYTE pdrv); -DSTATUS disk_status (BYTE pdrv); -DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); -DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); -DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); - - -/* Disk Status Bits (DSTATUS) */ -#define STA_NOINIT 0x01 /* Drive not initialized */ -#define STA_NODISK 0x02 /* No medium in the drive */ -#define STA_PROTECT 0x04 /* Write protected */ - - -/* Command code for disk_ioctrl fucntion */ - -/* Generic command (Used by FatFs) */ -#define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */ -#define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */ -#define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */ -#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */ -#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */ - -/* Generic command (Not used by FatFs) */ -#define CTRL_POWER 5 /* Get/Set power status */ -#define CTRL_LOCK 6 /* Lock/Unlock media removal */ -#define CTRL_EJECT 7 /* Eject media */ -#define CTRL_FORMAT 8 /* Create physical format on the media */ - -/* MMC/SDC specific command (Not used by FatFs) */ -#define MMC_GET_TYPE 10 /* Get card type */ -#define MMC_GET_CSD 11 /* Get CSD */ -#define MMC_GET_CID 12 /* Get CID */ -#define MMC_GET_OCR 13 /* Get OCR */ -#define MMC_GET_SDSTAT 14 /* Get SD status */ - -/* ATA/CF specific command (Not used by FatFs) */ -#define ATA_GET_REV 20 /* Get F/W revision */ -#define ATA_GET_MODEL 21 /* Get model name */ -#define ATA_GET_SN 22 /* Get serial number */ - - -/* MMC card type flags (MMC_GET_TYPE) */ -#define CT_MMC 0x01 /* MMC ver 3 */ -#define CT_SD1 0x02 /* SD ver 1 */ -#define CT_SD2 0x04 /* SD ver 2 */ -#define CT_SDC (CT_SD1|CT_SD2) /* SD */ -#define CT_BLOCK 0x08 /* Block addressing */ - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/Sming/Arch/Esp8266/Components/fatfs/ff.c b/Sming/Arch/Esp8266/Components/fatfs/ff.c deleted file mode 100644 index 30d2e714c8..0000000000 --- a/Sming/Arch/Esp8266/Components/fatfs/ff.c +++ /dev/null @@ -1,4635 +0,0 @@ -/*----------------------------------------------------------------------------/ -/ FatFs - FAT file system module R0.11 (C)ChaN, 2015 -/-----------------------------------------------------------------------------/ -/ FatFs module is a free software that opened under license policy of -/ following conditions. -/ -/ Copyright (C) 2015, ChaN, all right reserved. -/ -/ 1. Redistributions of source code must retain the above copyright notice, -/ this condition and the following disclaimer. -/ -/ This software is provided by the copyright holder and contributors "AS IS" -/ and any warranties related to this software are DISCLAIMED. -/ The copyright owner or contributors be NOT LIABLE for any damages caused -/ by use of this software. -/----------------------------------------------------------------------------*/ - - -#include "ff.h" /* Declarations of FatFs API */ -#include "diskio.h" /* Declarations of disk I/O functions */ - - -/*-------------------------------------------------------------------------- - - Module Private Definitions - ----------------------------------------------------------------------------*/ - -#if _FATFS != 32020 /* Revision ID */ -#error Wrong include file (ff.h). -#endif - - -/* Reentrancy related */ -#if _FS_REENTRANT -#if _USE_LFN == 1 -#error Static LFN work area cannot be used at thread-safe configuration -#endif -#define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; } -#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } -#else -#define ENTER_FF(fs) -#define LEAVE_FF(fs, res) return res -#endif - -#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } - - -/* Definitions of sector size */ -#if (_MAX_SS < _MIN_SS) || (_MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096) || (_MIN_SS != 512 && _MIN_SS != 1024 && _MIN_SS != 2048 && _MIN_SS != 4096) -#error Wrong sector size configuration -#endif -#if _MAX_SS == _MIN_SS -#define SS(fs) ((UINT)_MAX_SS) /* Fixed sector size */ -#else -#define SS(fs) ((fs)->ssize) /* Variable sector size */ -#endif - - -/* Timestamp feature */ -#if _FS_NORTC == 1 -#if _NORTC_YEAR < 1980 || _NORTC_YEAR > 2107 || _NORTC_MON < 1 || _NORTC_MON > 12 || _NORTC_MDAY < 1 || _NORTC_MDAY > 31 -#error Invalid _FS_NORTC settings -#endif -#define GET_FATTIME() ((DWORD)(_NORTC_YEAR - 1980) << 25 | (DWORD)_NORTC_MON << 21 | (DWORD)_NORTC_MDAY << 16) -#else -#define GET_FATTIME() get_fattime() -#endif - - -/* File access control feature */ -#if _FS_LOCK -#if _FS_READONLY -#error _FS_LOCK must be 0 at read-only configuration -#endif -typedef struct { - FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ - DWORD clu; /* Object ID 2, directory (0:root) */ - WORD idx; /* Object ID 3, directory index */ - WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */ -} FILESEM; -#endif - - - -/* DBCS code ranges and SBCS extend character conversion table */ - -#if _CODE_PAGE == 932 /* Japanese Shift-JIS */ -#define _DF1S 0x81 /* DBC 1st byte range 1 start */ -#define _DF1E 0x9F /* DBC 1st byte range 1 end */ -#define _DF2S 0xE0 /* DBC 1st byte range 2 start */ -#define _DF2E 0xFC /* DBC 1st byte range 2 end */ -#define _DS1S 0x40 /* DBC 2nd byte range 1 start */ -#define _DS1E 0x7E /* DBC 2nd byte range 1 end */ -#define _DS2S 0x80 /* DBC 2nd byte range 2 start */ -#define _DS2E 0xFC /* DBC 2nd byte range 2 end */ - -#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ -#define _DF1S 0x81 -#define _DF1E 0xFE -#define _DS1S 0x40 -#define _DS1E 0x7E -#define _DS2S 0x80 -#define _DS2E 0xFE - -#elif _CODE_PAGE == 949 /* Korean */ -#define _DF1S 0x81 -#define _DF1E 0xFE -#define _DS1S 0x41 -#define _DS1E 0x5A -#define _DS2S 0x61 -#define _DS2E 0x7A -#define _DS3S 0x81 -#define _DS3E 0xFE - -#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ -#define _DF1S 0x81 -#define _DF1E 0xFE -#define _DS1S 0x40 -#define _DS1E 0x7E -#define _DS2S 0xA1 -#define _DS2E 0xFE - -#elif _CODE_PAGE == 437 /* U.S. (OEM) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ - 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} - -#elif _CODE_PAGE == 720 /* Arabic (OEM) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ - 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} - -#elif _CODE_PAGE == 737 /* Greek (OEM) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ - 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} - -#elif _CODE_PAGE == 775 /* Baltic (OEM) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ - 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} - -#elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ - 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} - -#elif _CODE_PAGE == 852 /* Latin 2 (OEM) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \ - 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} - -#elif _CODE_PAGE == 855 /* Cyrillic (OEM) */ -#define _DF1S 0 -#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ - 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ - 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} - -#elif _CODE_PAGE == 857 /* Turkish (OEM) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ - 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} - -#elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ - 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} - -#elif _CODE_PAGE == 862 /* Hebrew (OEM) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ - 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} - -#elif _CODE_PAGE == 866 /* Russian (OEM) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ - 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} - -#elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ - 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} - -#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ - 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} - -#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ - 0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF} - -#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \ - 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} - -#elif _CODE_PAGE == 1253 /* Greek (Windows) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ - 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \ - 0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF} - -#elif _CODE_PAGE == 1254 /* Turkish (Windows) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \ - 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} - -#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ - 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} - -#elif _CODE_PAGE == 1256 /* Arabic (Windows) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \ - 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF} - -#elif _CODE_PAGE == 1257 /* Baltic (Windows) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ - 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} - -#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */ -#define _DF1S 0 -#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \ - 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ - 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F} - -#elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ -#if _USE_LFN -#error Cannot use LFN feature without valid code page. -#endif -#define _DF1S 0 - -#else -#error Unknown code page - -#endif - - -/* Character code support macros */ -#define IsUpper(c) (((c)>='A')&&((c)<='Z')) -#define IsLower(c) (((c)>='a')&&((c)<='z')) -#define IsDigit(c) (((c)>='0')&&((c)<='9')) - -#if _DF1S /* Code page is DBCS */ - -#ifdef _DF2S /* Two 1st byte areas */ -#define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) -#else /* One 1st byte area */ -#define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) -#endif - -#ifdef _DS3S /* Three 2nd byte areas */ -#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) -#else /* Two 2nd byte areas */ -#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) -#endif - -#else /* Code page is SBCS */ - -#define IsDBCS1(c) 0 -#define IsDBCS2(c) 0 - -#endif /* _DF1S */ - - -/* Name status flags */ -#define NSFLAG 11 /* Index of name status byte in fn[] */ -#define NS_LOSS 0x01 /* Out of 8.3 format */ -#define NS_LFN 0x02 /* Force to create LFN entry */ -#define NS_LAST 0x04 /* Last segment */ -#define NS_BODY 0x08 /* Lower case flag (body) */ -#define NS_EXT 0x10 /* Lower case flag (ext) */ -#define NS_DOT 0x20 /* Dot entry */ - - -/* FAT sub-type boundaries (Differ from specs but correct for real DOS/Windows) */ -#define MIN_FAT16 4086U /* Minimum number of clusters as FAT16 */ -#define MIN_FAT32 65526U /* Minimum number of clusters as FAT32 */ - - -/* FatFs refers the members in the FAT structures as byte array instead of -/ structure member because the structure is not binary compatible between -/ different platforms */ - -#define BS_jmpBoot 0 /* x86 jump instruction (3) */ -#define BS_OEMName 3 /* OEM name (8) */ -#define BPB_BytsPerSec 11 /* Sector size [byte] (2) */ -#define BPB_SecPerClus 13 /* Cluster size [sector] (1) */ -#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (2) */ -#define BPB_NumFATs 16 /* Number of FAT copies (1) */ -#define BPB_RootEntCnt 17 /* Number of root directory entries for FAT12/16 (2) */ -#define BPB_TotSec16 19 /* Volume size [sector] (2) */ -#define BPB_Media 21 /* Media descriptor (1) */ -#define BPB_FATSz16 22 /* FAT size [sector] (2) */ -#define BPB_SecPerTrk 24 /* Track size [sector] (2) */ -#define BPB_NumHeads 26 /* Number of heads (2) */ -#define BPB_HiddSec 28 /* Number of special hidden sectors (4) */ -#define BPB_TotSec32 32 /* Volume size [sector] (4) */ -#define BS_DrvNum 36 /* Physical drive number (2) */ -#define BS_BootSig 38 /* Extended boot signature (1) */ -#define BS_VolID 39 /* Volume serial number (4) */ -#define BS_VolLab 43 /* Volume label (8) */ -#define BS_FilSysType 54 /* File system type (1) */ -#define BPB_FATSz32 36 /* FAT size [sector] (4) */ -#define BPB_ExtFlags 40 /* Extended flags (2) */ -#define BPB_FSVer 42 /* File system version (2) */ -#define BPB_RootClus 44 /* Root directory first cluster (4) */ -#define BPB_FSInfo 48 /* Offset of FSINFO sector (2) */ -#define BPB_BkBootSec 50 /* Offset of backup boot sector (2) */ -#define BS_DrvNum32 64 /* Physical drive number (2) */ -#define BS_BootSig32 66 /* Extended boot signature (1) */ -#define BS_VolID32 67 /* Volume serial number (4) */ -#define BS_VolLab32 71 /* Volume label (8) */ -#define BS_FilSysType32 82 /* File system type (1) */ -#define FSI_LeadSig 0 /* FSI: Leading signature (4) */ -#define FSI_StrucSig 484 /* FSI: Structure signature (4) */ -#define FSI_Free_Count 488 /* FSI: Number of free clusters (4) */ -#define FSI_Nxt_Free 492 /* FSI: Last allocated cluster (4) */ -#define MBR_Table 446 /* MBR: Partition table offset (2) */ -#define SZ_PTE 16 /* MBR: Size of a partition table entry */ -#define BS_55AA 510 /* Signature word (2) */ - -#define DIR_Name 0 /* Short file name (11) */ -#define DIR_Attr 11 /* Attribute (1) */ -#define DIR_NTres 12 /* Lower case flag (1) */ -#define DIR_CrtTimeTenth 13 /* Created time sub-second (1) */ -#define DIR_CrtTime 14 /* Created time (2) */ -#define DIR_CrtDate 16 /* Created date (2) */ -#define DIR_LstAccDate 18 /* Last accessed date (2) */ -#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (2) */ -#define DIR_WrtTime 22 /* Modified time (2) */ -#define DIR_WrtDate 24 /* Modified date (2) */ -#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (2) */ -#define DIR_FileSize 28 /* File size (4) */ -#define LDIR_Ord 0 /* LFN entry order and LLE flag (1) */ -#define LDIR_Attr 11 /* LFN attribute (1) */ -#define LDIR_Type 12 /* LFN type (1) */ -#define LDIR_Chksum 13 /* Sum of corresponding SFN entry */ -#define LDIR_FstClusLO 26 /* Must be zero (0) */ -#define SZ_DIRE 32 /* Size of a directory entry */ -#define LLEF 0x40 /* Last long entry flag in LDIR_Ord */ -#define DDEM 0xE5 /* Deleted directory entry mark at DIR_Name[0] */ -#define RDDEM 0x05 /* Replacement of the character collides with DDEM */ - - - - -/*------------------------------------------------------------*/ -/* Module private work area */ -/*------------------------------------------------------------*/ -/* Remark: Uninitialized variables with static duration are -/ guaranteed zero/null at start-up. If not, either the linker -/ or start-up routine being used is out of ANSI-C standard. -*/ - -#if _VOLUMES < 1 || _VOLUMES > 9 -#error Wrong _VOLUMES setting -#endif -static FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */ -static WORD Fsid; /* File system mount ID */ - -#if _FS_RPATH && _VOLUMES >= 2 -static BYTE CurrVol; /* Current drive */ -#endif - -#if _FS_LOCK -static FILESEM Files[_FS_LOCK]; /* Open object lock semaphores */ -#endif - -#if _USE_LFN == 0 /* Non LFN feature */ -#define DEFINE_NAMEBUF BYTE sfn[12] -#define INIT_BUF(dobj) (dobj).fn = sfn -#define FREE_BUF() -#else -#if _MAX_LFN < 12 || _MAX_LFN > 255 -#error Wrong _MAX_LFN setting -#endif -#if _USE_LFN == 1 /* LFN feature with static working buffer */ -static WCHAR LfnBuf[_MAX_LFN + 1]; -#define DEFINE_NAMEBUF BYTE sfn[12] -#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = LfnBuf; } -#define FREE_BUF() -#elif _USE_LFN == 2 /* LFN feature with dynamic working buffer on the stack */ -#define DEFINE_NAMEBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN + 1] -#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = lbuf; } -#define FREE_BUF() -#elif _USE_LFN == 3 /* LFN feature with dynamic working buffer on the heap */ -#define DEFINE_NAMEBUF BYTE sfn[12]; WCHAR *lfn -#define INIT_BUF(dobj) { lfn = ff_memalloc((_MAX_LFN + 1) * 2); if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); (dobj).lfn = lfn; (dobj).fn = sfn; } -#define FREE_BUF() ff_memfree(lfn) -#else -#error Wrong _USE_LFN setting -#endif -#endif - -#ifdef _EXCVT -static const BYTE ExCvt[] = _EXCVT; /* Upper conversion table for extended characters */ -#endif - - - - - - -/*-------------------------------------------------------------------------- - - Module Private Functions - ----------------------------------------------------------------------------*/ - - -/*-----------------------------------------------------------------------*/ -/* String functions */ -/*-----------------------------------------------------------------------*/ - -/* Copy memory to memory */ -static -void mem_cpy (void* dst, const void* src, UINT cnt) { - BYTE *d = (BYTE*)dst; - const BYTE *s = (const BYTE*)src; - -#if _WORD_ACCESS == 1 - while (cnt >= sizeof (int)) { - *(int*)d = *(int*)s; - d += sizeof (int); s += sizeof (int); - cnt -= sizeof (int); - } -#endif - while (cnt--) - *d++ = *s++; -} - -/* Fill memory */ -static -void mem_set (void* dst, int val, UINT cnt) { - BYTE *d = (BYTE*)dst; - - while (cnt--) - *d++ = (BYTE)val; -} - -/* Compare memory to memory */ -static -int mem_cmp (const void* dst, const void* src, UINT cnt) { - const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; - int r = 0; - - while (cnt-- && (r = *d++ - *s++) == 0) ; - return r; -} - -/* Check if chr is contained in the string */ -static -int chk_chr (const char* str, int chr) { - while (*str && *str != chr) str++; - return *str; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Request/Release grant to access the volume */ -/*-----------------------------------------------------------------------*/ -#if _FS_REENTRANT -static -int lock_fs ( - FATFS* fs /* File system object */ -) -{ - return ff_req_grant(fs->sobj); -} - - -static -void unlock_fs ( - FATFS* fs, /* File system object */ - FRESULT res /* Result code to be returned */ -) -{ - if (fs && - res != FR_NOT_ENABLED && - res != FR_INVALID_DRIVE && - res != FR_INVALID_OBJECT && - res != FR_TIMEOUT) { - ff_rel_grant(fs->sobj); - } -} -#endif - - - - -/*-----------------------------------------------------------------------*/ -/* File lock control functions */ -/*-----------------------------------------------------------------------*/ -#if _FS_LOCK - -static -FRESULT chk_lock ( /* Check if the file can be accessed */ - DIR* dp, /* Directory object pointing the file to be checked */ - int acc /* Desired access type (0:Read, 1:Write, 2:Delete/Rename) */ -) -{ - UINT i, be; - - /* Search file semaphore table */ - for (i = be = 0; i < _FS_LOCK; i++) { - if (Files[i].fs) { /* Existing entry */ - if (Files[i].fs == dp->fs && /* Check if the object matched with an open object */ - Files[i].clu == dp->sclust && - Files[i].idx == dp->index) break; - } else { /* Blank entry */ - be = 1; - } - } - if (i == _FS_LOCK) /* The object is not opened */ - return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new object? */ - - /* The object has been opened. Reject any open against writing file and all write mode open */ - return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; -} - - -static -int enq_lock (void) /* Check if an entry is available for a new object */ -{ - UINT i; - - for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; - return (i == _FS_LOCK) ? 0 : 1; -} - - -static -UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */ - DIR* dp, /* Directory object pointing the file to register or increment */ - int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ -) -{ - UINT i; - - - for (i = 0; i < _FS_LOCK; i++) { /* Find the object */ - if (Files[i].fs == dp->fs && - Files[i].clu == dp->sclust && - Files[i].idx == dp->index) break; - } - - if (i == _FS_LOCK) { /* Not opened. Register it as new. */ - for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; - if (i == _FS_LOCK) return 0; /* No free entry to register (int err) */ - Files[i].fs = dp->fs; - Files[i].clu = dp->sclust; - Files[i].idx = dp->index; - Files[i].ctr = 0; - } - - if (acc && Files[i].ctr) return 0; /* Access violation (int err) */ - - Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ - - return i + 1; -} - - -static -FRESULT dec_lock ( /* Decrement object open counter */ - UINT i /* Semaphore index (1..) */ -) -{ - WORD n; - FRESULT res; - - - if (--i < _FS_LOCK) { /* Shift index number origin from 0 */ - n = Files[i].ctr; - if (n == 0x100) n = 0; /* If write mode open, delete the entry */ - if (n) n--; /* Decrement read mode open count */ - Files[i].ctr = n; - if (!n) Files[i].fs = 0; /* Delete the entry if open count gets zero */ - res = FR_OK; - } else { - res = FR_INT_ERR; /* Invalid index nunber */ - } - return res; -} - - -static -void clear_lock ( /* Clear lock entries of the volume */ - FATFS *fs -) -{ - UINT i; - - for (i = 0; i < _FS_LOCK; i++) { - if (Files[i].fs == fs) Files[i].fs = 0; - } -} -#endif - - - - -/*-----------------------------------------------------------------------*/ -/* Move/Flush disk access window in the file system object */ -/*-----------------------------------------------------------------------*/ -#if !_FS_READONLY -static -FRESULT sync_window ( - FATFS* fs /* File system object */ -) -{ - DWORD wsect; - UINT nf; - FRESULT res = FR_OK; - - - if (fs->wflag) { /* Write back the sector if it is dirty */ - wsect = fs->winsect; /* Current sector number */ - if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) { - res = FR_DISK_ERR; - } else { - fs->wflag = 0; - if (wsect - fs->fatbase < fs->fsize) { /* Is it in the FAT area? */ - for (nf = fs->n_fats; nf >= 2; nf--) { /* Reflect the change to all FAT copies */ - wsect += fs->fsize; - disk_write(fs->drv, fs->win, wsect, 1); - } - } - } - } - return res; -} -#endif - - -static -FRESULT move_window ( - FATFS* fs, /* File system object */ - DWORD sector /* Sector number to make appearance in the fs->win[] */ -) -{ - FRESULT res = FR_OK; - - - if (sector != fs->winsect) { /* Window offset changed? */ -#if !_FS_READONLY - res = sync_window(fs); /* Write-back changes */ -#endif - if (res == FR_OK) { /* Fill sector window with new data */ - if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK) { - sector = 0xFFFFFFFF; /* Invalidate window if data is not reliable */ - res = FR_DISK_ERR; - } - fs->winsect = sector; - } - } - return res; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Synchronize file system and strage device */ -/*-----------------------------------------------------------------------*/ -#if !_FS_READONLY -static -FRESULT sync_fs ( /* FR_OK: successful, FR_DISK_ERR: failed */ - FATFS* fs /* File system object */ -) -{ - FRESULT res; - - - res = sync_window(fs); - if (res == FR_OK) { - /* Update FSINFO sector if needed */ - if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { - /* Create FSINFO structure */ - mem_set(fs->win, 0, SS(fs)); - ST_WORD(fs->win + BS_55AA, 0xAA55); - ST_DWORD(fs->win + FSI_LeadSig, 0x41615252); - ST_DWORD(fs->win + FSI_StrucSig, 0x61417272); - ST_DWORD(fs->win + FSI_Free_Count, fs->free_clust); - ST_DWORD(fs->win + FSI_Nxt_Free, fs->last_clust); - /* Write it into the FSINFO sector */ - fs->winsect = fs->volbase + 1; - disk_write(fs->drv, fs->win, fs->winsect, 1); - fs->fsi_flag = 0; - } - /* Make sure that no pending write process in the physical drive */ - if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK) - res = FR_DISK_ERR; - } - - return res; -} -#endif - - - - -/*-----------------------------------------------------------------------*/ -/* Get sector# from cluster# */ -/*-----------------------------------------------------------------------*/ -/* Hidden API for hacks and disk tools */ - -DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */ - FATFS* fs, /* File system object */ - DWORD clst /* Cluster# to be converted */ -) -{ - clst -= 2; - if (clst >= fs->n_fatent - 2) return 0; /* Invalid cluster# */ - return clst * fs->csize + fs->database; -} - - - - -/*-----------------------------------------------------------------------*/ -/* FAT access - Read value of a FAT entry */ -/*-----------------------------------------------------------------------*/ -/* Hidden API for hacks and disk tools */ - -DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x0FFFFFFF:Cluster status */ - FATFS* fs, /* File system object */ - DWORD clst /* FAT index number (cluster number) to get the value */ -) -{ - UINT wc, bc; - BYTE *p; - DWORD val; - - - if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ - val = 1; /* Internal error */ - - } else { - val = 0xFFFFFFFF; /* Default value falls on disk error */ - - switch (fs->fs_type) { - case FS_FAT12 : - bc = (UINT)clst; bc += bc / 2; - if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; - wc = fs->win[bc++ % SS(fs)]; - if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; - wc |= fs->win[bc % SS(fs)] << 8; - val = clst & 1 ? wc >> 4 : (wc & 0xFFF); - break; - - case FS_FAT16 : - if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break; - p = &fs->win[clst * 2 % SS(fs)]; - val = LD_WORD(p); - break; - - case FS_FAT32 : - if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; - p = &fs->win[clst * 4 % SS(fs)]; - val = LD_DWORD(p) & 0x0FFFFFFF; - break; - - default: - val = 1; /* Internal error */ - } - } - - return val; -} - - - - -/*-----------------------------------------------------------------------*/ -/* FAT access - Change value of a FAT entry */ -/*-----------------------------------------------------------------------*/ -/* Hidden API for hacks and disk tools */ - -#if !_FS_READONLY -FRESULT put_fat ( - FATFS* fs, /* File system object */ - DWORD clst, /* FAT index number (cluster number) to be changed */ - DWORD val /* New value to be set to the entry */ -) -{ - UINT bc; - BYTE *p; - FRESULT res; - - - if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ - res = FR_INT_ERR; - - } else { - switch (fs->fs_type) { - case FS_FAT12 : - bc = (UINT)clst; bc += bc / 2; - res = move_window(fs, fs->fatbase + (bc / SS(fs))); - if (res != FR_OK) break; - p = &fs->win[bc++ % SS(fs)]; - *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; - fs->wflag = 1; - res = move_window(fs, fs->fatbase + (bc / SS(fs))); - if (res != FR_OK) break; - p = &fs->win[bc % SS(fs)]; - *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); - fs->wflag = 1; - break; - - case FS_FAT16 : - res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); - if (res != FR_OK) break; - p = &fs->win[clst * 2 % SS(fs)]; - ST_WORD(p, (WORD)val); - fs->wflag = 1; - break; - - case FS_FAT32 : - res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); - if (res != FR_OK) break; - p = &fs->win[clst * 4 % SS(fs)]; - val |= LD_DWORD(p) & 0xF0000000; - ST_DWORD(p, val); - fs->wflag = 1; - break; - - default : - res = FR_INT_ERR; - } - } - - return res; -} -#endif /* !_FS_READONLY */ - - - - -/*-----------------------------------------------------------------------*/ -/* FAT handling - Remove a cluster chain */ -/*-----------------------------------------------------------------------*/ -#if !_FS_READONLY -static -FRESULT remove_chain ( - FATFS* fs, /* File system object */ - DWORD clst /* Cluster# to remove a chain from */ -) -{ - FRESULT res; - DWORD nxt; -#if _USE_TRIM - DWORD scl = clst, ecl = clst, rt[2]; -#endif - - if (clst < 2 || clst >= fs->n_fatent) { /* Check range */ - res = FR_INT_ERR; - - } else { - res = FR_OK; - while (clst < fs->n_fatent) { /* Not a last link? */ - nxt = get_fat(fs, clst); /* Get cluster status */ - if (nxt == 0) break; /* Empty cluster? */ - if (nxt == 1) { res = FR_INT_ERR; break; } /* Internal error? */ - if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } /* Disk error? */ - res = put_fat(fs, clst, 0); /* Mark the cluster "empty" */ - if (res != FR_OK) break; - if (fs->free_clust != 0xFFFFFFFF) { /* Update FSINFO */ - fs->free_clust++; - fs->fsi_flag |= 1; - } -#if _USE_TRIM - if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ - ecl = nxt; - } else { /* End of contiguous clusters */ - rt[0] = clust2sect(fs, scl); /* Start sector */ - rt[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ - disk_ioctl(fs->drv, CTRL_TRIM, rt); /* Erase the block */ - scl = ecl = nxt; - } -#endif - clst = nxt; /* Next cluster */ - } - } - - return res; -} -#endif - - - - -/*-----------------------------------------------------------------------*/ -/* FAT handling - Stretch or Create a cluster chain */ -/*-----------------------------------------------------------------------*/ -#if !_FS_READONLY -static -DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ - FATFS* fs, /* File system object */ - DWORD clst /* Cluster# to stretch. 0 means create a new chain. */ -) -{ - DWORD cs, ncl, scl; - FRESULT res; - - - if (clst == 0) { /* Create a new chain */ - scl = fs->last_clust; /* Get suggested start point */ - if (!scl || scl >= fs->n_fatent) scl = 1; - } - else { /* Stretch the current chain */ - cs = get_fat(fs, clst); /* Check the cluster status */ - if (cs < 2) return 1; /* Invalid value */ - if (cs == 0xFFFFFFFF) return cs; /* A disk error occurred */ - if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ - scl = clst; - } - - ncl = scl; /* Start cluster */ - for (;;) { - ncl++; /* Next cluster */ - if (ncl >= fs->n_fatent) { /* Check wrap around */ - ncl = 2; - if (ncl > scl) return 0; /* No free cluster */ - } - cs = get_fat(fs, ncl); /* Get the cluster status */ - if (cs == 0) break; /* Found a free cluster */ - if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */ - return cs; - if (ncl == scl) return 0; /* No free cluster */ - } - - res = put_fat(fs, ncl, 0x0FFFFFFF); /* Mark the new cluster "last link" */ - if (res == FR_OK && clst != 0) { - res = put_fat(fs, clst, ncl); /* Link it to the previous one if needed */ - } - if (res == FR_OK) { - fs->last_clust = ncl; /* Update FSINFO */ - if (fs->free_clust != 0xFFFFFFFF) { - fs->free_clust--; - fs->fsi_flag |= 1; - } - } else { - ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; - } - - return ncl; /* Return new cluster number or error code */ -} -#endif /* !_FS_READONLY */ - - - - -/*-----------------------------------------------------------------------*/ -/* FAT handling - Convert offset into cluster with link map table */ -/*-----------------------------------------------------------------------*/ - -#if _USE_FASTSEEK -static -DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ - FIL* fp, /* Pointer to the file object */ - DWORD ofs /* File offset to be converted to cluster# */ -) -{ - DWORD cl, ncl, *tbl; - - - tbl = fp->cltbl + 1; /* Top of CLMT */ - cl = ofs / SS(fp->fs) / fp->fs->csize; /* Cluster order from top of the file */ - for (;;) { - ncl = *tbl++; /* Number of cluters in the fragment */ - if (!ncl) return 0; /* End of table? (error) */ - if (cl < ncl) break; /* In this fragment? */ - cl -= ncl; tbl++; /* Next fragment */ - } - return cl + *tbl; /* Return the cluster number */ -} -#endif /* _USE_FASTSEEK */ - - - - -/*-----------------------------------------------------------------------*/ -/* Directory handling - Set directory index */ -/*-----------------------------------------------------------------------*/ - -static -FRESULT dir_sdi ( - DIR* dp, /* Pointer to directory object */ - UINT idx /* Index of directory table */ -) -{ - DWORD clst, sect; - UINT ic; - - - dp->index = (WORD)idx; /* Current index */ - clst = dp->sclust; /* Table start cluster (0:root) */ - if (clst == 1 || clst >= dp->fs->n_fatent) /* Check start cluster range */ - return FR_INT_ERR; - if (!clst && dp->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */ - clst = dp->fs->dirbase; - - if (clst == 0) { /* Static table (root-directory in FAT12/16) */ - if (idx >= dp->fs->n_rootdir) /* Is index out of range? */ - return FR_INT_ERR; - sect = dp->fs->dirbase; - } - else { /* Dynamic table (root-directory in FAT32 or sub-directory) */ - ic = SS(dp->fs) / SZ_DIRE * dp->fs->csize; /* Entries per cluster */ - while (idx >= ic) { /* Follow cluster chain */ - clst = get_fat(dp->fs, clst); /* Get next cluster */ - if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ - if (clst < 2 || clst >= dp->fs->n_fatent) /* Reached to end of table or internal error */ - return FR_INT_ERR; - idx -= ic; - } - sect = clust2sect(dp->fs, clst); - } - dp->clust = clst; /* Current cluster# */ - if (!sect) return FR_INT_ERR; - dp->sect = sect + idx / (SS(dp->fs) / SZ_DIRE); /* Sector# of the directory entry */ - dp->dir = dp->fs->win + (idx % (SS(dp->fs) / SZ_DIRE)) * SZ_DIRE; /* Ptr to the entry in the sector */ - - return FR_OK; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Directory handling - Move directory table index next */ -/*-----------------------------------------------------------------------*/ - -static -FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */ - DIR* dp, /* Pointer to the directory object */ - int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ -) -{ - DWORD clst; - UINT i; -#if !_FS_READONLY - UINT c; -#endif - - - i = dp->index + 1; - if (!(i & 0xFFFF) || !dp->sect) /* Report EOT when index has reached 65535 */ - return FR_NO_FILE; - - if (!(i % (SS(dp->fs) / SZ_DIRE))) { /* Sector changed? */ - dp->sect++; /* Next sector */ - - if (!dp->clust) { /* Static table */ - if (i >= dp->fs->n_rootdir) /* Report EOT if it reached end of static table */ - return FR_NO_FILE; - } - else { /* Dynamic table */ - if (((i / (SS(dp->fs) / SZ_DIRE)) & (dp->fs->csize - 1)) == 0) { /* Cluster changed? */ - clst = get_fat(dp->fs, dp->clust); /* Get next cluster */ - if (clst <= 1) return FR_INT_ERR; - if (clst == 0xFFFFFFFF) return FR_DISK_ERR; - if (clst >= dp->fs->n_fatent) { /* If it reached end of dynamic table, */ -#if !_FS_READONLY - if (!stretch) return FR_NO_FILE; /* If do not stretch, report EOT */ - clst = create_chain(dp->fs, dp->clust); /* Stretch cluster chain */ - if (clst == 0) return FR_DENIED; /* No free cluster */ - if (clst == 1) return FR_INT_ERR; - if (clst == 0xFFFFFFFF) return FR_DISK_ERR; - /* Clean-up stretched table */ - if (sync_window(dp->fs)) return FR_DISK_ERR;/* Flush disk access window */ - mem_set(dp->fs->win, 0, SS(dp->fs)); /* Clear window buffer */ - dp->fs->winsect = clust2sect(dp->fs, clst); /* Cluster start sector */ - for (c = 0; c < dp->fs->csize; c++) { /* Fill the new cluster with 0 */ - dp->fs->wflag = 1; - if (sync_window(dp->fs)) return FR_DISK_ERR; - dp->fs->winsect++; - } - dp->fs->winsect -= c; /* Rewind window offset */ -#else - if (!stretch) return FR_NO_FILE; /* If do not stretch, report EOT (this is to suppress warning) */ - return FR_NO_FILE; /* Report EOT */ -#endif - } - dp->clust = clst; /* Initialize data for new cluster */ - dp->sect = clust2sect(dp->fs, clst); - } - } - } - - dp->index = (WORD)i; /* Current index */ - dp->dir = dp->fs->win + (i % (SS(dp->fs) / SZ_DIRE)) * SZ_DIRE; /* Current entry in the window */ - - return FR_OK; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Directory handling - Reserve directory entry */ -/*-----------------------------------------------------------------------*/ - -#if !_FS_READONLY -static -FRESULT dir_alloc ( - DIR* dp, /* Pointer to the directory object */ - UINT nent /* Number of contiguous entries to allocate (1-21) */ -) -{ - FRESULT res; - UINT n; - - - res = dir_sdi(dp, 0); - if (res == FR_OK) { - n = 0; - do { - res = move_window(dp->fs, dp->sect); - if (res != FR_OK) break; - if (dp->dir[0] == DDEM || dp->dir[0] == 0) { /* Is it a free entry? */ - if (++n == nent) break; /* A block of contiguous free entries is found */ - } else { - n = 0; /* Not a blank entry. Restart to search */ - } - res = dir_next(dp, 1); /* Next entry with table stretch enabled */ - } while (res == FR_OK); - } - if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ - return res; -} -#endif - - - - -/*-----------------------------------------------------------------------*/ -/* Directory handling - Load/Store start cluster number */ -/*-----------------------------------------------------------------------*/ - -static -DWORD ld_clust ( - FATFS* fs, /* Pointer to the fs object */ - BYTE* dir /* Pointer to the directory entry */ -) -{ - DWORD cl; - - cl = LD_WORD(dir + DIR_FstClusLO); - if (fs->fs_type == FS_FAT32) - cl |= (DWORD)LD_WORD(dir + DIR_FstClusHI) << 16; - - return cl; -} - - -#if !_FS_READONLY -static -void st_clust ( - BYTE* dir, /* Pointer to the directory entry */ - DWORD cl /* Value to be set */ -) -{ - ST_WORD(dir + DIR_FstClusLO, cl); - ST_WORD(dir + DIR_FstClusHI, cl >> 16); -} -#endif - - - - -/*-----------------------------------------------------------------------*/ -/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */ -/*-----------------------------------------------------------------------*/ -#if _USE_LFN -static -const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN characters in the directory entry */ - - -static -int cmp_lfn ( /* 1:Matched, 0:Not matched */ - WCHAR* lfnbuf, /* Pointer to the LFN to be compared */ - BYTE* dir /* Pointer to the directory entry containing a part of LFN */ -) -{ - UINT i, s; - WCHAR wc, uc; - - - i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Get offset in the LFN buffer */ - s = 0; wc = 1; - do { - uc = LD_WORD(dir + LfnOfs[s]); /* Pick an LFN character from the entry */ - if (wc) { /* Last character has not been processed */ - wc = ff_wtoupper(uc); /* Convert it to upper case */ - if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++])) /* Compare it */ - return 0; /* Not matched */ - } else { - if (uc != 0xFFFF) return 0; /* Check filler */ - } - } while (++s < 13); /* Repeat until all characters in the entry are checked */ - - if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) /* Last segment matched but different length */ - return 0; - - return 1; /* The part of LFN matched */ -} - - - -static -int pick_lfn ( /* 1:Succeeded, 0:Buffer overflow */ - WCHAR* lfnbuf, /* Pointer to the Unicode-LFN buffer */ - BYTE* dir /* Pointer to the directory entry */ -) -{ - UINT i, s; - WCHAR wc, uc; - - - i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ - - s = 0; wc = 1; - do { - uc = LD_WORD(dir + LfnOfs[s]); /* Pick an LFN character from the entry */ - if (wc) { /* Last character has not been processed */ - if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ - lfnbuf[i++] = wc = uc; /* Store it */ - } else { - if (uc != 0xFFFF) return 0; /* Check filler */ - } - } while (++s < 13); /* Read all character in the entry */ - - if (dir[LDIR_Ord] & LLEF) { /* Put terminator if it is the last LFN part */ - if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ - lfnbuf[i] = 0; - } - - return 1; -} - - -#if !_FS_READONLY -static -void fit_lfn ( - const WCHAR* lfnbuf, /* Pointer to the LFN buffer */ - BYTE* dir, /* Pointer to the directory entry */ - BYTE ord, /* LFN order (1-20) */ - BYTE sum /* SFN sum */ -) -{ - UINT i, s; - WCHAR wc; - - - dir[LDIR_Chksum] = sum; /* Set check sum */ - dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ - dir[LDIR_Type] = 0; - ST_WORD(dir + LDIR_FstClusLO, 0); - - i = (ord - 1) * 13; /* Get offset in the LFN buffer */ - s = wc = 0; - do { - if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective character */ - ST_WORD(dir+LfnOfs[s], wc); /* Put it */ - if (!wc) wc = 0xFFFF; /* Padding characters following last character */ - } while (++s < 13); - if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLEF; /* Bottom LFN part is the start of LFN sequence */ - dir[LDIR_Ord] = ord; /* Set the LFN order */ -} - -#endif -#endif - - - - -/*-----------------------------------------------------------------------*/ -/* Create numbered name */ -/*-----------------------------------------------------------------------*/ -#if _USE_LFN -static -void gen_numname ( - BYTE* dst, /* Pointer to the buffer to store numbered SFN */ - const BYTE* src, /* Pointer to SFN */ - const WCHAR* lfn, /* Pointer to LFN */ - UINT seq /* Sequence number */ -) -{ - BYTE ns[8], c; - UINT i, j; - WCHAR wc; - DWORD sr; - - - mem_cpy(dst, src, 11); - - if (seq > 5) { /* On many collisions, generate a hash number instead of sequential number */ - sr = seq; - while (*lfn) { /* Create a CRC */ - wc = *lfn++; - for (i = 0; i < 16; i++) { - sr = (sr << 1) + (wc & 1); - wc >>= 1; - if (sr & 0x10000) sr ^= 0x11021; - } - } - seq = (UINT)sr; - } - - /* itoa (hexdecimal) */ - i = 7; - do { - c = (seq % 16) + '0'; - if (c > '9') c += 7; - ns[i--] = c; - seq /= 16; - } while (seq); - ns[i] = '~'; - - /* Append the number */ - for (j = 0; j < i && dst[j] != ' '; j++) { - if (IsDBCS1(dst[j])) { - if (j == i - 1) break; - j++; - } - } - do { - dst[j++] = (i < 8) ? ns[i++] : ' '; - } while (j < 8); -} -#endif - - - - -/*-----------------------------------------------------------------------*/ -/* Calculate sum of an SFN */ -/*-----------------------------------------------------------------------*/ -#if _USE_LFN -static -BYTE sum_sfn ( - const BYTE* dir /* Pointer to the SFN entry */ -) -{ - BYTE sum = 0; - UINT n = 11; - - do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); - return sum; -} -#endif - - - - -/*-----------------------------------------------------------------------*/ -/* Directory handling - Find an object in the directory */ -/*-----------------------------------------------------------------------*/ - -static -FRESULT dir_find ( - DIR* dp /* Pointer to the directory object linked to the file name */ -) -{ - FRESULT res; - BYTE c, *dir; -#if _USE_LFN - BYTE a, ord, sum; -#endif - - res = dir_sdi(dp, 0); /* Rewind directory object */ - if (res != FR_OK) return res; - -#if _USE_LFN - ord = sum = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */ -#endif - do { - res = move_window(dp->fs, dp->sect); - if (res != FR_OK) break; - dir = dp->dir; /* Ptr to the directory entry of current index */ - c = dir[DIR_Name]; - if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ -#if _USE_LFN /* LFN configuration */ - a = dir[DIR_Attr] & AM_MASK; - if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ - ord = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */ - } else { - if (a == AM_LFN) { /* An LFN entry is found */ - if (dp->lfn) { - if (c & LLEF) { /* Is it start of LFN sequence? */ - sum = dir[LDIR_Chksum]; - c &= ~LLEF; ord = c; /* LFN start order */ - dp->lfn_idx = dp->index; /* Start index of LFN */ - } - /* Check validity of the LFN entry and compare it with given name */ - ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF; - } - } else { /* An SFN entry is found */ - if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */ - if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dir, dp->fn, 11)) break; /* SFN matched? */ - ord = 0xFF; dp->lfn_idx = 0xFFFF; /* Reset LFN sequence */ - } - } -#else /* Non LFN configuration */ - if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dp->fn, 11)) /* Is it a valid entry? */ - break; -#endif - res = dir_next(dp, 0); /* Next entry */ - } while (res == FR_OK); - - return res; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Read an object from the directory */ -/*-----------------------------------------------------------------------*/ -#if _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 -static -FRESULT dir_read ( - DIR* dp, /* Pointer to the directory object */ - int vol /* Filtered by 0:file/directory or 1:volume label */ -) -{ - FRESULT res; - BYTE a, c, *dir; -#if _USE_LFN - BYTE ord = 0xFF, sum = 0xFF; -#endif - - res = FR_NO_FILE; - while (dp->sect) { - res = move_window(dp->fs, dp->sect); - if (res != FR_OK) break; - dir = dp->dir; /* Ptr to the directory entry of current index */ - c = dir[DIR_Name]; - if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ - a = dir[DIR_Attr] & AM_MASK; -#if _USE_LFN /* LFN configuration */ - if (c == DDEM || (!_FS_RPATH && c == '.') || (int)((a & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ - ord = 0xFF; - } else { - if (a == AM_LFN) { /* An LFN entry is found */ - if (c & LLEF) { /* Is it start of LFN sequence? */ - sum = dir[LDIR_Chksum]; - c &= ~LLEF; ord = c; - dp->lfn_idx = dp->index; - } - /* Check LFN validity and capture it */ - ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF; - } else { /* An SFN entry is found */ - if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */ - dp->lfn_idx = 0xFFFF; /* It has no LFN. */ - break; - } - } -#else /* Non LFN configuration */ - if (c != DDEM && (_FS_RPATH || c != '.') && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) /* Is it a valid entry? */ - break; -#endif - res = dir_next(dp, 0); /* Next entry */ - if (res != FR_OK) break; - } - - if (res != FR_OK) dp->sect = 0; - - return res; -} -#endif /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */ - - - - -/*-----------------------------------------------------------------------*/ -/* Register an object to the directory */ -/*-----------------------------------------------------------------------*/ -#if !_FS_READONLY -static -FRESULT dir_register ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */ - DIR* dp /* Target directory with object name to be created */ -) -{ - FRESULT res; -#if _USE_LFN /* LFN configuration */ - UINT n, nent; - BYTE sn[12], *fn, sum; - WCHAR *lfn; - - - fn = dp->fn; lfn = dp->lfn; - mem_cpy(sn, fn, 12); - - if (_FS_RPATH && (sn[NSFLAG] & NS_DOT)) /* Cannot create dot entry */ - return FR_INVALID_NAME; - - if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ - fn[NSFLAG] = 0; dp->lfn = 0; /* Find only SFN */ - for (n = 1; n < 100; n++) { - gen_numname(fn, sn, lfn, n); /* Generate a numbered name */ - res = dir_find(dp); /* Check if the name collides with existing SFN */ - if (res != FR_OK) break; - } - if (n == 100) return FR_DENIED; /* Abort if too many collisions */ - if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ - fn[NSFLAG] = sn[NSFLAG]; dp->lfn = lfn; - } - - if (sn[NSFLAG] & NS_LFN) { /* When LFN is to be created, allocate entries for an SFN + LFNs. */ - for (n = 0; lfn[n]; n++) ; - nent = (n + 25) / 13; - } else { /* Otherwise allocate an entry for an SFN */ - nent = 1; - } - res = dir_alloc(dp, nent); /* Allocate entries */ - - if (res == FR_OK && --nent) { /* Set LFN entry if needed */ - res = dir_sdi(dp, dp->index - nent); - if (res == FR_OK) { - sum = sum_sfn(dp->fn); /* Sum value of the SFN tied to the LFN */ - do { /* Store LFN entries in bottom first */ - res = move_window(dp->fs, dp->sect); - if (res != FR_OK) break; - fit_lfn(dp->lfn, dp->dir, (BYTE)nent, sum); - dp->fs->wflag = 1; - res = dir_next(dp, 0); /* Next entry */ - } while (res == FR_OK && --nent); - } - } -#else /* Non LFN configuration */ - res = dir_alloc(dp, 1); /* Allocate an entry for SFN */ -#endif - - if (res == FR_OK) { /* Set SFN entry */ - res = move_window(dp->fs, dp->sect); - if (res == FR_OK) { - mem_set(dp->dir, 0, SZ_DIRE); /* Clean the entry */ - mem_cpy(dp->dir, dp->fn, 11); /* Put SFN */ -#if _USE_LFN - dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ -#endif - dp->fs->wflag = 1; - } - } - - return res; -} -#endif /* !_FS_READONLY */ - - - - -/*-----------------------------------------------------------------------*/ -/* Remove an object from the directory */ -/*-----------------------------------------------------------------------*/ -#if !_FS_READONLY && !_FS_MINIMIZE -static -FRESULT dir_remove ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */ - DIR* dp /* Directory object pointing the entry to be removed */ -) -{ - FRESULT res; -#if _USE_LFN /* LFN configuration */ - UINT i; - - i = dp->index; /* SFN index */ - res = dir_sdi(dp, (dp->lfn_idx == 0xFFFF) ? i : dp->lfn_idx); /* Goto the SFN or top of the LFN entries */ - if (res == FR_OK) { - do { - res = move_window(dp->fs, dp->sect); - if (res != FR_OK) break; - mem_set(dp->dir, 0, SZ_DIRE); /* Clear and mark the entry "deleted" */ - *dp->dir = DDEM; - dp->fs->wflag = 1; - if (dp->index >= i) break; /* When reached SFN, all entries of the object has been deleted. */ - res = dir_next(dp, 0); /* Next entry */ - } while (res == FR_OK); - if (res == FR_NO_FILE) res = FR_INT_ERR; - } - -#else /* Non LFN configuration */ - res = dir_sdi(dp, dp->index); - if (res == FR_OK) { - res = move_window(dp->fs, dp->sect); - if (res == FR_OK) { - mem_set(dp->dir, 0, SZ_DIRE); /* Clear and mark the entry "deleted" */ - *dp->dir = DDEM; - dp->fs->wflag = 1; - } - } -#endif - - return res; -} -#endif /* !_FS_READONLY */ - - - - -/*-----------------------------------------------------------------------*/ -/* Get file information from directory entry */ -/*-----------------------------------------------------------------------*/ -#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 -static -void get_fileinfo ( /* No return code */ - DIR* dp, /* Pointer to the directory object */ - FILINFO* fno /* Pointer to the file information to be filled */ -) -{ - UINT i; - TCHAR *p, c; - BYTE *dir; -#if _USE_LFN - WCHAR w, *lfn; -#endif - - p = fno->fname; - if (dp->sect) { /* Get SFN */ - dir = dp->dir; - i = 0; - while (i < 11) { /* Copy name body and extension */ - c = (TCHAR)dir[i++]; - if (c == ' ') continue; /* Skip padding spaces */ - if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ - if (i == 9) *p++ = '.'; /* Insert a . if extension is exist */ -#if _USE_LFN - if (IsUpper(c) && (dir[DIR_NTres] & (i >= 9 ? NS_EXT : NS_BODY))) - c += 0x20; /* To lower */ -#if _LFN_UNICODE - if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dir[i])) - c = c << 8 | dir[i++]; - c = ff_convert(c, 1); /* OEM -> Unicode */ - if (!c) c = '?'; -#endif -#endif - *p++ = c; - } - fno->fattrib = dir[DIR_Attr]; /* Attribute */ - fno->fsize = LD_DWORD(dir + DIR_FileSize); /* Size */ - fno->fdate = LD_WORD(dir + DIR_WrtDate); /* Date */ - fno->ftime = LD_WORD(dir + DIR_WrtTime); /* Time */ - } - *p = 0; /* Terminate SFN string by a \0 */ - -#if _USE_LFN - if (fno->lfname) { - i = 0; p = fno->lfname; - if (dp->sect && fno->lfsize && dp->lfn_idx != 0xFFFF) { /* Get LFN if available */ - lfn = dp->lfn; - while ((w = *lfn++) != 0) { /* Get an LFN character */ -#if !_LFN_UNICODE - w = ff_convert(w, 0); /* Unicode -> OEM */ - if (!w) { i = 0; break; } /* No LFN if it could not be converted */ - if (_DF1S && w >= 0x100) /* Put 1st byte if it is a DBC (always false on SBCS cfg) */ - p[i++] = (TCHAR)(w >> 8); -#endif - if (i >= fno->lfsize - 1) { i = 0; break; } /* No LFN if buffer overflow */ - p[i++] = (TCHAR)w; - } - } - p[i] = 0; /* Terminate LFN string by a \0 */ - } -#endif -} -#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ - - - - -/*-----------------------------------------------------------------------*/ -/* Pattern matching */ -/*-----------------------------------------------------------------------*/ -#if _USE_FIND && _FS_MINIMIZE <= 1 -static -WCHAR get_achar ( /* Get a character and advances ptr 1 or 2 */ - const TCHAR** ptr /* Pointer to pointer to the SBCS/DBCS/Unicode string */ -) -{ - WCHAR chr; - -#if !_LFN_UNICODE - chr = (BYTE)*(*ptr)++; /* Get a byte */ - if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ - if (IsDBCS1(chr) && IsDBCS2(**ptr)) /* Get DBC 2nd byte if needed */ - chr = chr << 8 | (BYTE)*(*ptr)++; -#ifdef _EXCVT - if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ -#endif -#else - chr = ff_wtoupper(*(*ptr)++); /* Get a word and to upper */ -#endif - return chr; -} - - -static -int pattern_matching ( /* Return value: 0:mismatched, 1:matched */ - const TCHAR* pat, /* Matching pattern */ - const TCHAR* nam, /* String to be tested */ - int skip, /* Number of pre-skip chars (number of ?s) */ - int inf /* Infinite search (* specified) */ -) -{ - const TCHAR *pp, *np; - WCHAR pc, nc; - int nm, nx; - - - while (skip--) { /* Pre-skip name chars */ - if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */ - } - if (!*pat && inf) return 1; /* (short circuit) */ - - do { - pp = pat; np = nam; /* Top of pattern and name to match */ - for (;;) { - if (*pp == '?' || *pp == '*') { /* Wildcard? */ - nm = nx = 0; - do { /* Analyze the wildcard chars */ - if (*pp++ == '?') nm++; else nx = 1; - } while (*pp == '?' || *pp == '*'); - if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */ - nc = *np; break; /* Branch mismatched */ - } - pc = get_achar(&pp); /* Get a pattern char */ - nc = get_achar(&np); /* Get a name char */ - if (pc != nc) break; /* Branch mismatched? */ - if (!pc) return 1; /* Branch matched? (matched at end of both strings) */ - } - get_achar(&nam); /* nam++ */ - } while (inf && nc); /* Retry until end of name if infinite search is specified */ - - return 0; -} -#endif /* _USE_FIND && _FS_MINIMIZE <= 1 */ - - - - -/*-----------------------------------------------------------------------*/ -/* Pick a segment and create the object name in directory form */ -/*-----------------------------------------------------------------------*/ - -static -FRESULT create_name ( - DIR* dp, /* Pointer to the directory object */ - const TCHAR** path /* Pointer to pointer to the segment in the path string */ -) -{ -#if _USE_LFN /* LFN configuration */ - BYTE b, cf; - WCHAR w, *lfn; - UINT i, ni, si, di; - const TCHAR *p; - - /* Create LFN in Unicode */ - for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */ - lfn = dp->lfn; - si = di = 0; - for (;;) { - w = p[si++]; /* Get a character */ - if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */ - if (di >= _MAX_LFN) /* Reject too long name */ - return FR_INVALID_NAME; -#if !_LFN_UNICODE - w &= 0xFF; - if (IsDBCS1(w)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ - b = (BYTE)p[si++]; /* Get 2nd byte */ - w = (w << 8) + b; /* Create a DBC */ - if (!IsDBCS2(b)) - return FR_INVALID_NAME; /* Reject invalid sequence */ - } - w = ff_convert(w, 1); /* Convert ANSI/OEM to Unicode */ - if (!w) return FR_INVALID_NAME; /* Reject invalid code */ -#endif - if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal characters for LFN */ - return FR_INVALID_NAME; - lfn[di++] = w; /* Store the Unicode character */ - } - *path = &p[si]; /* Return pointer to the next segment */ - cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ -#if _FS_RPATH - if ((di == 1 && lfn[di - 1] == '.') || /* Is this a dot entry? */ - (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { - lfn[di] = 0; - for (i = 0; i < 11; i++) - dp->fn[i] = (i < di) ? '.' : ' '; - dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ - return FR_OK; - } -#endif - while (di) { /* Strip trailing spaces and dots */ - w = lfn[di - 1]; - if (w != ' ' && w != '.') break; - di--; - } - if (!di) return FR_INVALID_NAME; /* Reject nul string */ - - lfn[di] = 0; /* LFN is created */ - - /* Create SFN in directory form */ - mem_set(dp->fn, ' ', 11); - for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ - if (si) cf |= NS_LOSS | NS_LFN; - while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ - - b = i = 0; ni = 8; - for (;;) { - w = lfn[si++]; /* Get an LFN character */ - if (!w) break; /* Break on end of the LFN */ - if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ - cf |= NS_LOSS | NS_LFN; continue; - } - - if (i >= ni || si == di) { /* Extension or end of SFN */ - if (ni == 11) { /* Long extension */ - cf |= NS_LOSS | NS_LFN; break; - } - if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ - if (si > di) break; /* No extension */ - si = di; i = 8; ni = 11; /* Enter extension section */ - b <<= 2; continue; - } - - if (w >= 0x80) { /* Non ASCII character */ -#ifdef _EXCVT - w = ff_convert(w, 0); /* Unicode -> OEM code */ - if (w) w = ExCvt[w - 0x80]; /* Convert extended character to upper (SBCS) */ -#else - w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */ -#endif - cf |= NS_LFN; /* Force create LFN entry */ - } - - if (_DF1S && w >= 0x100) { /* DBC (always false at SBCS cfg) */ - if (i >= ni - 1) { - cf |= NS_LOSS | NS_LFN; i = ni; continue; - } - dp->fn[i++] = (BYTE)(w >> 8); - } else { /* SBC */ - if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal characters for SFN */ - w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ - } else { - if (IsUpper(w)) { /* ASCII large capital */ - b |= 2; - } else { - if (IsLower(w)) { /* ASCII small capital */ - b |= 1; w -= 0x20; - } - } - } - } - dp->fn[i++] = (BYTE)w; - } - - if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with deleted mark, replace it with RDDEM */ - - if (ni == 8) b <<= 2; - if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) /* Create LFN entry when there are composite capitals */ - cf |= NS_LFN; - if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */ - if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ - if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ - } - - dp->fn[NSFLAG] = cf; /* SFN is created */ - - return FR_OK; - - -#else /* Non-LFN configuration */ - BYTE b, c, d, *sfn; - UINT ni, si, i; - const char *p; - - /* Create file name in directory form */ - for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */ - sfn = dp->fn; - mem_set(sfn, ' ', 11); - si = i = b = 0; ni = 8; -#if _FS_RPATH - if (p[si] == '.') { /* Is this a dot entry? */ - for (;;) { - c = (BYTE)p[si++]; - if (c != '.' || si >= 3) break; - sfn[i++] = c; - } - if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; - *path = &p[si]; /* Return pointer to the next segment */ - sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */ - return FR_OK; - } -#endif - for (;;) { - c = (BYTE)p[si++]; - if (c <= ' ' || c == '/' || c == '\\') break; /* Break on end of segment */ - if (c == '.' || i >= ni) { - if (ni != 8 || c != '.') return FR_INVALID_NAME; - i = 8; ni = 11; - b <<= 2; continue; - } - if (c >= 0x80) { /* Extended character? */ - b |= 3; /* Eliminate NT flag */ -#ifdef _EXCVT - c = ExCvt[c - 0x80]; /* To upper extended characters (SBCS cfg) */ -#else -#if !_DF1S - return FR_INVALID_NAME; /* Reject extended characters (ASCII cfg) */ -#endif -#endif - } - if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ - d = (BYTE)p[si++]; /* Get 2nd byte */ - if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */ - return FR_INVALID_NAME; - sfn[i++] = c; - sfn[i++] = d; - } else { /* SBC */ - if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) /* Reject illegal chrs for SFN */ - return FR_INVALID_NAME; - if (IsUpper(c)) { /* ASCII large capital? */ - b |= 2; - } else { - if (IsLower(c)) { /* ASCII small capital? */ - b |= 1; c -= 0x20; - } - } - sfn[i++] = c; - } - } - *path = &p[si]; /* Return pointer to the next segment */ - c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ - - if (!i) return FR_INVALID_NAME; /* Reject nul string */ - if (sfn[0] == DDEM) sfn[0] = RDDEM; /* When first character collides with DDEM, replace it with RDDEM */ - - if (ni == 8) b <<= 2; - if ((b & 0x03) == 0x01) c |= NS_EXT; /* NT flag (Name extension has only small capital) */ - if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Name body has only small capital) */ - - sfn[NSFLAG] = c; /* Store NT flag, File name is created */ - - return FR_OK; -#endif -} - - - - -/*-----------------------------------------------------------------------*/ -/* Follow a file path */ -/*-----------------------------------------------------------------------*/ - -static -FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ - DIR* dp, /* Directory object to return last directory and found object */ - const TCHAR* path /* Full-path string to find a file or directory */ -) -{ - FRESULT res; - BYTE *dir, ns; - - -#if _FS_RPATH - if (*path == '/' || *path == '\\') { /* There is a heading separator */ - path++; dp->sclust = 0; /* Strip it and start from the root directory */ - } else { /* No heading separator */ - dp->sclust = dp->fs->cdir; /* Start from the current directory */ - } -#else - if (*path == '/' || *path == '\\') /* Strip heading separator if exist */ - path++; - dp->sclust = 0; /* Always start from the root directory */ -#endif - - if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */ - res = dir_sdi(dp, 0); - dp->dir = 0; - } else { /* Follow path */ - for (;;) { - res = create_name(dp, &path); /* Get a segment name of the path */ - if (res != FR_OK) break; - res = dir_find(dp); /* Find an object with the sagment name */ - ns = dp->fn[NSFLAG]; - if (res != FR_OK) { /* Failed to find the object */ - if (res == FR_NO_FILE) { /* Object is not found */ - if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, */ - dp->sclust = 0; dp->dir = 0; /* it is the root directory and stay there */ - if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */ - res = FR_OK; /* Ended at the root directroy. Function completed. */ - } else { /* Could not find the object */ - if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */ - } - } - break; - } - if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ - dir = dp->dir; /* Follow the sub-directory */ - if (!(dir[DIR_Attr] & AM_DIR)) { /* It is not a sub-directory and cannot follow */ - res = FR_NO_PATH; break; - } - dp->sclust = ld_clust(dp->fs, dir); - } - } - - return res; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Get logical drive number from path name */ -/*-----------------------------------------------------------------------*/ - -static -int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ - const TCHAR** path /* Pointer to pointer to the path name */ -) -{ - const TCHAR *tp, *tt; - UINT i; - int vol = -1; -#if _STR_VOLUME_ID /* Find string drive id */ - static const char* const str[] = {_VOLUME_STRS}; - const char *sp; - char c; - TCHAR tc; -#endif - - - if (*path) { /* If the pointer is not a null */ - for (tt = *path; (UINT)*tt >= (_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ; /* Find ':' in the path */ - if (*tt == ':') { /* If a ':' is exist in the path name */ - tp = *path; - i = *tp++ - '0'; - if (i < 10 && tp == tt) { /* Is there a numeric drive id? */ - if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */ - vol = (int)i; - *path = ++tt; - } - } -#if _STR_VOLUME_ID - else { /* No numeric drive number, find string drive id */ - i = 0; tt++; - do { - sp = str[i]; tp = *path; - do { /* Compare a string drive id with path name */ - c = *sp++; tc = *tp++; - if (IsLower(tc)) tc -= 0x20; - } while (c && (TCHAR)c == tc); - } while ((c || tp != tt) && ++i < _VOLUMES); /* Repeat for each id until pattern match */ - if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */ - vol = (int)i; - *path = tt; - } - } -#endif - return vol; - } -#if _FS_RPATH && _VOLUMES >= 2 - vol = CurrVol; /* Current drive */ -#else - vol = 0; /* Drive 0 */ -#endif - } - return vol; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Load a sector and check if it is an FAT boot sector */ -/*-----------------------------------------------------------------------*/ - -static -BYTE check_fs ( /* 0:FAT boor sector, 1:Valid boor sector but not FAT, 2:Not a boot sector, 3:Disk error */ - FATFS* fs, /* File system object */ - DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ -) -{ - fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */ - if (move_window(fs, sect) != FR_OK) /* Load boot record */ - return 3; - - if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check boot record signature (always placed at offset 510 even if the sector size is >512) */ - return 2; - - if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ - return 0; - if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ - return 0; - - return 1; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Find logical drive and check if the volume is mounted */ -/*-----------------------------------------------------------------------*/ - -static -FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ - FATFS** rfs, /* Pointer to pointer to the found file system object */ - const TCHAR** path, /* Pointer to pointer to the path name (drive number) */ - BYTE wmode /* !=0: Check write protection for write access */ -) -{ - BYTE fmt, *pt; - int vol; - DSTATUS stat; - DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4]; - WORD nrsv; - FATFS *fs; - UINT i; - - - /* Get logical drive number from the path name */ - *rfs = 0; - vol = get_ldnumber(path); - if (vol < 0) return FR_INVALID_DRIVE; - - /* Check if the file system object is valid or not */ - fs = FatFs[vol]; /* Get pointer to the file system object */ - if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */ - - ENTER_FF(fs); /* Lock the volume */ - *rfs = fs; /* Return pointer to the file system object */ - - if (fs->fs_type) { /* If the volume has been mounted */ - stat = disk_status(fs->drv); - if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */ - if (!_FS_READONLY && wmode && (stat & STA_PROTECT)) /* Check write protection if needed */ - return FR_WRITE_PROTECTED; - return FR_OK; /* The file system object is valid */ - } - } - - /* The file system object is not valid. */ - /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */ - - fs->fs_type = 0; /* Clear the file system object */ - fs->drv = LD2PD(vol); /* Bind the logical drive and a physical drive */ - stat = disk_initialize(fs->drv); /* Initialize the physical drive */ - if (stat & STA_NOINIT) /* Check if the initialization succeeded */ - return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */ - if (!_FS_READONLY && wmode && (stat & STA_PROTECT)) /* Check disk write protection if needed */ - return FR_WRITE_PROTECTED; -#if _MAX_SS != _MIN_SS /* Get sector size (multiple sector size cfg only) */ - if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK - || SS(fs) < _MIN_SS || SS(fs) > _MAX_SS) return FR_DISK_ERR; -#endif - /* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */ - bsect = 0; - fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT boot sector as SFD */ - if (fmt == 1 || (!fmt && (LD2PT(vol)))) { /* Not an FAT boot sector or forced partition number */ - for (i = 0; i < 4; i++) { /* Get partition offset */ - pt = fs->win + MBR_Table + i * SZ_PTE; - br[i] = pt[4] ? LD_DWORD(&pt[8]) : 0; - } - i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */ - if (i) i--; - do { /* Find an FAT volume */ - bsect = br[i]; - fmt = bsect ? check_fs(fs, bsect) : 2; /* Check the partition */ - } while (!LD2PT(vol) && fmt && ++i < 4); - } - if (fmt == 3) return FR_DISK_ERR; /* An error occured in the disk I/O layer */ - if (fmt) return FR_NO_FILESYSTEM; /* No FAT volume is found */ - - /* An FAT volume is found. Following code initializes the file system object */ - - if (LD_WORD(fs->win + BPB_BytsPerSec) != SS(fs)) /* (BPB_BytsPerSec must be equal to the physical sector size) */ - return FR_NO_FILESYSTEM; - - fasize = LD_WORD(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ - if (!fasize) fasize = LD_DWORD(fs->win + BPB_FATSz32); - fs->fsize = fasize; - - fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */ - if (fs->n_fats != 1 && fs->n_fats != 2) /* (Must be 1 or 2) */ - return FR_NO_FILESYSTEM; - fasize *= fs->n_fats; /* Number of sectors for FAT area */ - - fs->csize = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ - if (!fs->csize || (fs->csize & (fs->csize - 1))) /* (Must be power of 2) */ - return FR_NO_FILESYSTEM; - - fs->n_rootdir = LD_WORD(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ - if (fs->n_rootdir % (SS(fs) / SZ_DIRE)) /* (Must be sector aligned) */ - return FR_NO_FILESYSTEM; - - tsect = LD_WORD(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ - if (!tsect) tsect = LD_DWORD(fs->win + BPB_TotSec32); - - nrsv = LD_WORD(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ - if (!nrsv) return FR_NO_FILESYSTEM; /* (Must not be 0) */ - - /* Determine the FAT sub type */ - sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIRE); /* RSV + FAT + DIR */ - if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ - nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ - if (!nclst) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ - fmt = FS_FAT12; - if (nclst >= MIN_FAT16) fmt = FS_FAT16; - if (nclst >= MIN_FAT32) fmt = FS_FAT32; - - /* Boundaries and Limits */ - fs->n_fatent = nclst + 2; /* Number of FAT entries */ - fs->volbase = bsect; /* Volume start sector */ - fs->fatbase = bsect + nrsv; /* FAT start sector */ - fs->database = bsect + sysect; /* Data start sector */ - if (fmt == FS_FAT32) { - if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ - fs->dirbase = LD_DWORD(fs->win + BPB_RootClus); /* Root directory start cluster */ - szbfat = fs->n_fatent * 4; /* (Needed FAT size) */ - } else { - if (!fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */ - fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ - szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */ - fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); - } - if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) /* (BPB_FATSz must not be less than the size needed) */ - return FR_NO_FILESYSTEM; - -#if !_FS_READONLY - /* Initialize cluster allocation information */ - fs->last_clust = fs->free_clust = 0xFFFFFFFF; - - /* Get fsinfo if available */ - fs->fsi_flag = 0x80; -#if (_FS_NOFSINFO & 3) != 3 - if (fmt == FS_FAT32 /* Enable FSINFO only if FAT32 and BPB_FSInfo is 1 */ - && LD_WORD(fs->win + BPB_FSInfo) == 1 - && move_window(fs, bsect + 1) == FR_OK) - { - fs->fsi_flag = 0; - if (LD_WORD(fs->win + BS_55AA) == 0xAA55 /* Load FSINFO data if available */ - && LD_DWORD(fs->win + FSI_LeadSig) == 0x41615252 - && LD_DWORD(fs->win + FSI_StrucSig) == 0x61417272) - { -#if (_FS_NOFSINFO & 1) == 0 - fs->free_clust = LD_DWORD(fs->win + FSI_Free_Count); -#endif -#if (_FS_NOFSINFO & 2) == 0 - fs->last_clust = LD_DWORD(fs->win + FSI_Nxt_Free); -#endif - } - } -#endif -#endif - fs->fs_type = fmt; /* FAT sub-type */ - fs->id = ++Fsid; /* File system mount ID */ -#if _FS_RPATH - fs->cdir = 0; /* Set current directory to root */ -#endif -#if _FS_LOCK /* Clear file lock semaphores */ - clear_lock(fs); -#endif - - return FR_OK; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Check if the file/directory object is valid or not */ -/*-----------------------------------------------------------------------*/ - -static -FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ - void* obj /* Pointer to the object FIL/DIR to check validity */ -) -{ - FIL *fil = (FIL*)obj; /* Assuming offset of .fs and .id in the FIL/DIR structure is identical */ - - - if (!fil || !fil->fs || !fil->fs->fs_type || fil->fs->id != fil->id) - return FR_INVALID_OBJECT; - - ENTER_FF(fil->fs); /* Lock file system */ - - if (disk_status(fil->fs->drv) & STA_NOINIT) - return FR_NOT_READY; - - return FR_OK; -} - - - - -/*-------------------------------------------------------------------------- - - Public Functions - ---------------------------------------------------------------------------*/ - - - -/*-----------------------------------------------------------------------*/ -/* Mount/Unmount a Logical Drive */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_mount ( - FATFS* fs, /* Pointer to the file system object (NULL:unmount)*/ - const TCHAR* path, /* Logical drive number to be mounted/unmounted */ - BYTE opt /* 0:Do not mount (delayed mount), 1:Mount immediately */ -) -{ - FATFS *cfs; - int vol; - FRESULT res; - const TCHAR *rp = path; - - - vol = get_ldnumber(&rp); - if (vol < 0) return FR_INVALID_DRIVE; - cfs = FatFs[vol]; /* Pointer to fs object */ - - if (cfs) { -#if _FS_LOCK - clear_lock(cfs); -#endif -#if _FS_REENTRANT /* Discard sync object of the current volume */ - if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR; -#endif - cfs->fs_type = 0; /* Clear old fs object */ - } - - if (fs) { - fs->fs_type = 0; /* Clear new fs object */ -#if _FS_REENTRANT /* Create sync object for the new volume */ - if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR; -#endif - } - FatFs[vol] = fs; /* Register new fs object */ - - if (!fs || opt != 1) return FR_OK; /* Do not mount now, it will be mounted later */ - - res = find_volume(&fs, &path, 0); /* Force mounted the volume */ - LEAVE_FF(fs, res); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Open or Create a File */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_open ( - FIL* fp, /* Pointer to the blank file object */ - const TCHAR* path, /* Pointer to the file name */ - BYTE mode /* Access mode and file open mode flags */ -) -{ - FRESULT res; - DIR dj; - BYTE *dir; - DEFINE_NAMEBUF; -#if !_FS_READONLY - DWORD dw, cl; -#endif - - - if (!fp) return FR_INVALID_OBJECT; - fp->fs = 0; /* Clear file object */ - - /* Get logical drive number */ -#if !_FS_READONLY - mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW; - res = find_volume(&dj.fs, &path, (BYTE)(mode & ~FA_READ)); -#else - mode &= FA_READ; - res = find_volume(&dj.fs, &path, 0); -#endif - if (res == FR_OK) { - INIT_BUF(dj); - res = follow_path(&dj, path); /* Follow the file path */ - dir = dj.dir; -#if !_FS_READONLY /* R/W configuration */ - if (res == FR_OK) { - if (!dir) /* Default directory itself */ - res = FR_INVALID_NAME; -#if _FS_LOCK - else - res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); -#endif - } - /* Create or Open a file */ - if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { - if (res != FR_OK) { /* No file, create new */ - if (res == FR_NO_FILE) /* There is no file to open, create a new entry */ -#if _FS_LOCK - res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; -#else - res = dir_register(&dj); -#endif - mode |= FA_CREATE_ALWAYS; /* File is created */ - dir = dj.dir; /* New entry */ - } - else { /* Any object is already existing */ - if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */ - res = FR_DENIED; - } else { - if (mode & FA_CREATE_NEW) /* Cannot create as new file */ - res = FR_EXIST; - } - } - if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */ - dw = GET_FATTIME(); /* Created time */ - ST_DWORD(dir + DIR_CrtTime, dw); - dir[DIR_Attr] = 0; /* Reset attribute */ - ST_DWORD(dir + DIR_FileSize, 0);/* size = 0 */ - cl = ld_clust(dj.fs, dir); /* Get start cluster */ - st_clust(dir, 0); /* cluster = 0 */ - dj.fs->wflag = 1; - if (cl) { /* Remove the cluster chain if exist */ - dw = dj.fs->winsect; - res = remove_chain(dj.fs, cl); - if (res == FR_OK) { - dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */ - res = move_window(dj.fs, dw); - } - } - } - } - else { /* Open an existing file */ - if (res == FR_OK) { /* Follow succeeded */ - if (dir[DIR_Attr] & AM_DIR) { /* It is a directory */ - res = FR_NO_FILE; - } else { - if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ - res = FR_DENIED; - } - } - } - if (res == FR_OK) { - if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ - mode |= FA__WRITTEN; - fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ - fp->dir_ptr = dir; -#if _FS_LOCK - fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); - if (!fp->lockid) res = FR_INT_ERR; -#endif - } - -#else /* R/O configuration */ - if (res == FR_OK) { /* Follow succeeded */ - dir = dj.dir; - if (!dir) { /* Current directory itself */ - res = FR_INVALID_NAME; - } else { - if (dir[DIR_Attr] & AM_DIR) /* It is a directory */ - res = FR_NO_FILE; - } - } -#endif - FREE_BUF(); - - if (res == FR_OK) { - fp->flag = mode; /* File access mode */ - fp->err = 0; /* Clear error flag */ - fp->sclust = ld_clust(dj.fs, dir); /* File start cluster */ - fp->fsize = LD_DWORD(dir + DIR_FileSize); /* File size */ - fp->fptr = 0; /* File pointer */ - fp->dsect = 0; -#if _USE_FASTSEEK - fp->cltbl = 0; /* Normal seek mode */ -#endif - fp->fs = dj.fs; /* Validate file object */ - fp->id = fp->fs->id; - } - } - - LEAVE_FF(dj.fs, res); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Read File */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_read ( - FIL* fp, /* Pointer to the file object */ - void* buff, /* Pointer to data buffer */ - UINT btr, /* Number of bytes to read */ - UINT* br /* Pointer to number of bytes read */ -) -{ - FRESULT res; - DWORD clst, sect, remain; - UINT rcnt, cc; - BYTE csect, *rbuff = (BYTE*)buff; - - - *br = 0; /* Clear read byte counter */ - - res = validate(fp); /* Check validity */ - if (res != FR_OK) LEAVE_FF(fp->fs, res); - if (fp->err) /* Check error */ - LEAVE_FF(fp->fs, (FRESULT)fp->err); - if (!(fp->flag & FA_READ)) /* Check access mode */ - LEAVE_FF(fp->fs, FR_DENIED); - remain = fp->fsize - fp->fptr; - if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ - - for ( ; btr; /* Repeat until all data read */ - rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { - if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ - csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ - if (!csect) { /* On the cluster boundary? */ - if (fp->fptr == 0) { /* On the top of the file? */ - clst = fp->sclust; /* Follow from the origin */ - } else { /* Middle or end of the file */ -#if _USE_FASTSEEK - if (fp->cltbl) - clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ - else -#endif - clst = get_fat(fp->fs, fp->clust); /* Follow cluster chain on the FAT */ - } - if (clst < 2) ABORT(fp->fs, FR_INT_ERR); - if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); - fp->clust = clst; /* Update current cluster */ - } - sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ - if (!sect) ABORT(fp->fs, FR_INT_ERR); - sect += csect; - cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ - if (cc) { /* Read maximum contiguous sectors directly */ - if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ - cc = fp->fs->csize - csect; - if (disk_read(fp->fs->drv, rbuff, sect, cc) != RES_OK) - ABORT(fp->fs, FR_DISK_ERR); -#if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ -#if _FS_TINY - if (fp->fs->wflag && fp->fs->winsect - sect < cc) - mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs)); -#else - if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc) - mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs)); -#endif -#endif - rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ - continue; - } -#if !_FS_TINY - if (fp->dsect != sect) { /* Load data sector if not in cache */ -#if !_FS_READONLY - if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ - if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) - ABORT(fp->fs, FR_DISK_ERR); - fp->flag &= ~FA__DIRTY; - } -#endif - if (disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) /* Fill sector cache */ - ABORT(fp->fs, FR_DISK_ERR); - } -#endif - fp->dsect = sect; - } - rcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */ - if (rcnt > btr) rcnt = btr; -#if _FS_TINY - if (move_window(fp->fs, fp->dsect) != FR_OK) /* Move sector window */ - ABORT(fp->fs, FR_DISK_ERR); - mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ -#else - mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ -#endif - } - - LEAVE_FF(fp->fs, FR_OK); -} - - - - -#if !_FS_READONLY -/*-----------------------------------------------------------------------*/ -/* Write File */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_write ( - FIL* fp, /* Pointer to the file object */ - const void *buff, /* Pointer to the data to be written */ - UINT btw, /* Number of bytes to write */ - UINT* bw /* Pointer to number of bytes written */ -) -{ - FRESULT res; - DWORD clst, sect; - UINT wcnt, cc; - const BYTE *wbuff = (const BYTE*)buff; - BYTE csect; - - - *bw = 0; /* Clear write byte counter */ - - res = validate(fp); /* Check validity */ - if (res != FR_OK) LEAVE_FF(fp->fs, res); - if (fp->err) /* Check error */ - LEAVE_FF(fp->fs, (FRESULT)fp->err); - if (!(fp->flag & FA_WRITE)) /* Check access mode */ - LEAVE_FF(fp->fs, FR_DENIED); - if (fp->fptr + btw < fp->fptr) btw = 0; /* File size cannot reach 4GB */ - - for ( ; btw; /* Repeat until all data written */ - wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { - if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ - csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ - if (!csect) { /* On the cluster boundary? */ - if (fp->fptr == 0) { /* On the top of the file? */ - clst = fp->sclust; /* Follow from the origin */ - if (clst == 0) /* When no cluster is allocated, */ - clst = create_chain(fp->fs, 0); /* Create a new cluster chain */ - } else { /* Middle or end of the file */ -#if _USE_FASTSEEK - if (fp->cltbl) - clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ - else -#endif - clst = create_chain(fp->fs, fp->clust); /* Follow or stretch cluster chain on the FAT */ - } - if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ - if (clst == 1) ABORT(fp->fs, FR_INT_ERR); - if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); - fp->clust = clst; /* Update current cluster */ - if (fp->sclust == 0) fp->sclust = clst; /* Set start cluster if the first write */ - } -#if _FS_TINY - if (fp->fs->winsect == fp->dsect && sync_window(fp->fs)) /* Write-back sector cache */ - ABORT(fp->fs, FR_DISK_ERR); -#else - if (fp->flag & FA__DIRTY) { /* Write-back sector cache */ - if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) - ABORT(fp->fs, FR_DISK_ERR); - fp->flag &= ~FA__DIRTY; - } -#endif - sect = clust2sect(fp->fs, fp->clust); /* Get current sector */ - if (!sect) ABORT(fp->fs, FR_INT_ERR); - sect += csect; - cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ - if (cc) { /* Write maximum contiguous sectors directly */ - if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */ - cc = fp->fs->csize - csect; - if (disk_write(fp->fs->drv, wbuff, sect, cc) != RES_OK) - ABORT(fp->fs, FR_DISK_ERR); -#if _FS_MINIMIZE <= 2 -#if _FS_TINY - if (fp->fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ - mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs)); - fp->fs->wflag = 0; - } -#else - if (fp->dsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ - mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs)); - fp->flag &= ~FA__DIRTY; - } -#endif -#endif - wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ - continue; - } -#if _FS_TINY - if (fp->fptr >= fp->fsize) { /* Avoid silly cache filling at growing edge */ - if (sync_window(fp->fs)) ABORT(fp->fs, FR_DISK_ERR); - fp->fs->winsect = sect; - } -#else - if (fp->dsect != sect) { /* Fill sector cache with file data */ - if (fp->fptr < fp->fsize && - disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) - ABORT(fp->fs, FR_DISK_ERR); - } -#endif - fp->dsect = sect; - } - wcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */ - if (wcnt > btw) wcnt = btw; -#if _FS_TINY - if (move_window(fp->fs, fp->dsect) != FR_OK) /* Move sector window */ - ABORT(fp->fs, FR_DISK_ERR); - mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ - fp->fs->wflag = 1; -#else - mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ - fp->flag |= FA__DIRTY; -#endif - } - - if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */ - fp->flag |= FA__WRITTEN; /* Set file change flag */ - - LEAVE_FF(fp->fs, FR_OK); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Synchronize the File */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_sync ( - FIL* fp /* Pointer to the file object */ -) -{ - FRESULT res; - DWORD tm; - BYTE *dir; - - - res = validate(fp); /* Check validity of the object */ - if (res == FR_OK) { - if (fp->flag & FA__WRITTEN) { /* Has the file been written? */ - /* Write-back dirty buffer */ -#if !_FS_TINY - if (fp->flag & FA__DIRTY) { - if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) - LEAVE_FF(fp->fs, FR_DISK_ERR); - fp->flag &= ~FA__DIRTY; - } -#endif - /* Update the directory entry */ - res = move_window(fp->fs, fp->dir_sect); - if (res == FR_OK) { - dir = fp->dir_ptr; - dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ - ST_DWORD(dir + DIR_FileSize, fp->fsize); /* Update file size */ - st_clust(dir, fp->sclust); /* Update start cluster */ - tm = GET_FATTIME(); /* Update updated time */ - ST_DWORD(dir + DIR_WrtTime, tm); - ST_WORD(dir + DIR_LstAccDate, 0); - fp->flag &= ~FA__WRITTEN; - fp->fs->wflag = 1; - res = sync_fs(fp->fs); - } - } - } - - LEAVE_FF(fp->fs, res); -} - -#endif /* !_FS_READONLY */ - - - - -/*-----------------------------------------------------------------------*/ -/* Close File */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_close ( - FIL *fp /* Pointer to the file object to be closed */ -) -{ - FRESULT res; - - -#if !_FS_READONLY - res = f_sync(fp); /* Flush cached data */ - if (res == FR_OK) -#endif - { - res = validate(fp); /* Lock volume */ - if (res == FR_OK) { -#if _FS_REENTRANT - FATFS *fs = fp->fs; -#endif -#if _FS_LOCK - res = dec_lock(fp->lockid); /* Decrement file open counter */ - if (res == FR_OK) -#endif - fp->fs = 0; /* Invalidate file object */ -#if _FS_REENTRANT - unlock_fs(fs, FR_OK); /* Unlock volume */ -#endif - } - } - return res; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Change Current Directory or Current Drive, Get Current Directory */ -/*-----------------------------------------------------------------------*/ - -#if _FS_RPATH >= 1 -#if _VOLUMES >= 2 -FRESULT f_chdrive ( - const TCHAR* path /* Drive number */ -) -{ - int vol; - - - vol = get_ldnumber(&path); - if (vol < 0) return FR_INVALID_DRIVE; - - CurrVol = (BYTE)vol; - - return FR_OK; -} -#endif - - -FRESULT f_chdir ( - const TCHAR* path /* Pointer to the directory path */ -) -{ - FRESULT res; - DIR dj; - DEFINE_NAMEBUF; - - - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 0); - if (res == FR_OK) { - INIT_BUF(dj); - res = follow_path(&dj, path); /* Follow the path */ - FREE_BUF(); - if (res == FR_OK) { /* Follow completed */ - if (!dj.dir) { - dj.fs->cdir = dj.sclust; /* Start directory itself */ - } else { - if (dj.dir[DIR_Attr] & AM_DIR) /* Reached to the directory */ - dj.fs->cdir = ld_clust(dj.fs, dj.dir); - else - res = FR_NO_PATH; /* Reached but a file */ - } - } - if (res == FR_NO_FILE) res = FR_NO_PATH; - } - - LEAVE_FF(dj.fs, res); -} - - -#if _FS_RPATH >= 2 -FRESULT f_getcwd ( - TCHAR* buff, /* Pointer to the directory path */ - UINT len /* Size of path */ -) -{ - FRESULT res; - DIR dj; - UINT i, n; - DWORD ccl; - TCHAR *tp; - FILINFO fno; - DEFINE_NAMEBUF; - - - *buff = 0; - /* Get logical drive number */ - res = find_volume(&dj.fs, (const TCHAR**)&buff, 0); /* Get current volume */ - if (res == FR_OK) { - INIT_BUF(dj); - i = len; /* Bottom of buffer (directory stack base) */ - dj.sclust = dj.fs->cdir; /* Start to follow upper directory from current directory */ - while ((ccl = dj.sclust) != 0) { /* Repeat while current directory is a sub-directory */ - res = dir_sdi(&dj, 1); /* Get parent directory */ - if (res != FR_OK) break; - res = dir_read(&dj, 0); - if (res != FR_OK) break; - dj.sclust = ld_clust(dj.fs, dj.dir); /* Goto parent directory */ - res = dir_sdi(&dj, 0); - if (res != FR_OK) break; - do { /* Find the entry links to the child directory */ - res = dir_read(&dj, 0); - if (res != FR_OK) break; - if (ccl == ld_clust(dj.fs, dj.dir)) break; /* Found the entry */ - res = dir_next(&dj, 0); - } while (res == FR_OK); - if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ - if (res != FR_OK) break; -#if _USE_LFN - fno.lfname = buff; - fno.lfsize = i; -#endif - get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */ - tp = fno.fname; -#if _USE_LFN - if (*buff) tp = buff; -#endif - for (n = 0; tp[n]; n++) ; - if (i < n + 3) { - res = FR_NOT_ENOUGH_CORE; break; - } - while (n) buff[--i] = tp[--n]; - buff[--i] = '/'; - } - tp = buff; - if (res == FR_OK) { -#if _VOLUMES >= 2 - *tp++ = '0' + CurrVol; /* Put drive number */ - *tp++ = ':'; -#endif - if (i == len) { /* Root-directory */ - *tp++ = '/'; - } else { /* Sub-directroy */ - do /* Add stacked path str */ - *tp++ = buff[i++]; - while (i < len); - } - } - *tp = 0; - FREE_BUF(); - } - - LEAVE_FF(dj.fs, res); -} -#endif /* _FS_RPATH >= 2 */ -#endif /* _FS_RPATH >= 1 */ - - - -#if _FS_MINIMIZE <= 2 -/*-----------------------------------------------------------------------*/ -/* Seek File R/W Pointer */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_lseek ( - FIL* fp, /* Pointer to the file object */ - DWORD ofs /* File pointer from top of file */ -) -{ - FRESULT res; - DWORD clst, bcs, nsect, ifptr; -#if _USE_FASTSEEK - DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; -#endif - - - res = validate(fp); /* Check validity of the object */ - if (res != FR_OK) LEAVE_FF(fp->fs, res); - if (fp->err) /* Check error */ - LEAVE_FF(fp->fs, (FRESULT)fp->err); - -#if _USE_FASTSEEK - if (fp->cltbl) { /* Fast seek */ - if (ofs == CREATE_LINKMAP) { /* Create CLMT */ - tbl = fp->cltbl; - tlen = *tbl++; ulen = 2; /* Given table size and required table size */ - cl = fp->sclust; /* Top of the chain */ - if (cl) { - do { - /* Get a fragment */ - tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */ - do { - pcl = cl; ncl++; - cl = get_fat(fp->fs, cl); - if (cl <= 1) ABORT(fp->fs, FR_INT_ERR); - if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); - } while (cl == pcl + 1); - if (ulen <= tlen) { /* Store the length and top of the fragment */ - *tbl++ = ncl; *tbl++ = tcl; - } - } while (cl < fp->fs->n_fatent); /* Repeat until end of chain */ - } - *fp->cltbl = ulen; /* Number of items used */ - if (ulen <= tlen) - *tbl = 0; /* Terminate table */ - else - res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */ - - } else { /* Fast seek */ - if (ofs > fp->fsize) /* Clip offset at the file size */ - ofs = fp->fsize; - fp->fptr = ofs; /* Set file pointer */ - if (ofs) { - fp->clust = clmt_clust(fp, ofs - 1); - dsc = clust2sect(fp->fs, fp->clust); - if (!dsc) ABORT(fp->fs, FR_INT_ERR); - dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1); - if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) { /* Refill sector cache if needed */ -#if !_FS_TINY -#if !_FS_READONLY - if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ - if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) - ABORT(fp->fs, FR_DISK_ERR); - fp->flag &= ~FA__DIRTY; - } -#endif - if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK) /* Load current sector */ - ABORT(fp->fs, FR_DISK_ERR); -#endif - fp->dsect = dsc; - } - } - } - } else -#endif - - /* Normal Seek */ - { - if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ -#if !_FS_READONLY - && !(fp->flag & FA_WRITE) -#endif - ) ofs = fp->fsize; - - ifptr = fp->fptr; - fp->fptr = nsect = 0; - if (ofs) { - bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ - if (ifptr > 0 && - (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ - fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ - ofs -= fp->fptr; - clst = fp->clust; - } else { /* When seek to back cluster, */ - clst = fp->sclust; /* start from the first cluster */ -#if !_FS_READONLY - if (clst == 0) { /* If no cluster chain, create a new chain */ - clst = create_chain(fp->fs, 0); - if (clst == 1) ABORT(fp->fs, FR_INT_ERR); - if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); - fp->sclust = clst; - } -#endif - fp->clust = clst; - } - if (clst != 0) { - while (ofs > bcs) { /* Cluster following loop */ -#if !_FS_READONLY - if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ - clst = create_chain(fp->fs, clst); /* Force stretch if in write mode */ - if (clst == 0) { /* When disk gets full, clip file size */ - ofs = bcs; break; - } - } else -#endif - clst = get_fat(fp->fs, clst); /* Follow cluster chain if not in write mode */ - if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); - if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR); - fp->clust = clst; - fp->fptr += bcs; - ofs -= bcs; - } - fp->fptr += ofs; - if (ofs % SS(fp->fs)) { - nsect = clust2sect(fp->fs, clst); /* Current sector */ - if (!nsect) ABORT(fp->fs, FR_INT_ERR); - nsect += ofs / SS(fp->fs); - } - } - } - if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { /* Fill sector cache if needed */ -#if !_FS_TINY -#if !_FS_READONLY - if (fp->flag & FA__DIRTY) { /* Write-back dirty sector cache */ - if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) - ABORT(fp->fs, FR_DISK_ERR); - fp->flag &= ~FA__DIRTY; - } -#endif - if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK) /* Fill sector cache */ - ABORT(fp->fs, FR_DISK_ERR); -#endif - fp->dsect = nsect; - } -#if !_FS_READONLY - if (fp->fptr > fp->fsize) { /* Set file change flag if the file size is extended */ - fp->fsize = fp->fptr; - fp->flag |= FA__WRITTEN; - } -#endif - } - - LEAVE_FF(fp->fs, res); -} - - - -#if _FS_MINIMIZE <= 1 -/*-----------------------------------------------------------------------*/ -/* Create a Directory Object */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_opendir ( - DIR* dp, /* Pointer to directory object to create */ - const TCHAR* path /* Pointer to the directory path */ -) -{ - FRESULT res; - FATFS* fs; - DEFINE_NAMEBUF; - - - if (!dp) return FR_INVALID_OBJECT; - - /* Get logical drive number */ - res = find_volume(&fs, &path, 0); - if (res == FR_OK) { - dp->fs = fs; - INIT_BUF(*dp); - res = follow_path(dp, path); /* Follow the path to the directory */ - FREE_BUF(); - if (res == FR_OK) { /* Follow completed */ - if (dp->dir) { /* It is not the origin directory itself */ - if (dp->dir[DIR_Attr] & AM_DIR) /* The object is a sub directory */ - dp->sclust = ld_clust(fs, dp->dir); - else /* The object is a file */ - res = FR_NO_PATH; - } - if (res == FR_OK) { - dp->id = fs->id; - res = dir_sdi(dp, 0); /* Rewind directory */ -#if _FS_LOCK - if (res == FR_OK) { - if (dp->sclust) { - dp->lockid = inc_lock(dp, 0); /* Lock the sub directory */ - if (!dp->lockid) - res = FR_TOO_MANY_OPEN_FILES; - } else { - dp->lockid = 0; /* Root directory need not to be locked */ - } - } -#endif - } - } - if (res == FR_NO_FILE) res = FR_NO_PATH; - } - if (res != FR_OK) dp->fs = 0; /* Invalidate the directory object if function faild */ - - LEAVE_FF(fs, res); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Close Directory */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_closedir ( - DIR *dp /* Pointer to the directory object to be closed */ -) -{ - FRESULT res; - - - res = validate(dp); - if (res == FR_OK) { -#if _FS_REENTRANT - FATFS *fs = dp->fs; -#endif -#if _FS_LOCK - if (dp->lockid) /* Decrement sub-directory open counter */ - res = dec_lock(dp->lockid); - if (res == FR_OK) -#endif - dp->fs = 0; /* Invalidate directory object */ -#if _FS_REENTRANT - unlock_fs(fs, FR_OK); /* Unlock volume */ -#endif - } - return res; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Read Directory Entries in Sequence */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_readdir ( - DIR* dp, /* Pointer to the open directory object */ - FILINFO* fno /* Pointer to file information to return */ -) -{ - FRESULT res; - DEFINE_NAMEBUF; - - - res = validate(dp); /* Check validity of the object */ - if (res == FR_OK) { - if (!fno) { - res = dir_sdi(dp, 0); /* Rewind the directory object */ - } else { - INIT_BUF(*dp); - res = dir_read(dp, 0); /* Read an item */ - if (res == FR_NO_FILE) { /* Reached end of directory */ - dp->sect = 0; - res = FR_OK; - } - if (res == FR_OK) { /* A valid entry is found */ - get_fileinfo(dp, fno); /* Get the object information */ - res = dir_next(dp, 0); /* Increment index for next */ - if (res == FR_NO_FILE) { - dp->sect = 0; - res = FR_OK; - } - } - FREE_BUF(); - } - } - - LEAVE_FF(dp->fs, res); -} - - - -#if _USE_FIND -/*-----------------------------------------------------------------------*/ -/* Find next file */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_findnext ( - DIR* dp, /* Pointer to the open directory object */ - FILINFO* fno /* Pointer to the file information structure */ -) -{ - FRESULT res; - - - for (;;) { - res = f_readdir(dp, fno); /* Get a directory item */ - if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */ -#if _USE_LFN - if (fno->lfname && pattern_matching(dp->pat, fno->lfname, 0, 0)) break; /* Test for LFN if exist */ -#endif - if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for SFN */ - } - return res; - -} - - - -/*-----------------------------------------------------------------------*/ -/* Find first file */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_findfirst ( - DIR* dp, /* Pointer to the blank directory object */ - FILINFO* fno, /* Pointer to the file information structure */ - const TCHAR* path, /* Pointer to the directory to open */ - const TCHAR* pattern /* Pointer to the matching pattern */ -) -{ - FRESULT res; - - - dp->pat = pattern; /* Save pointer to pattern string */ - res = f_opendir(dp, path); /* Open the target directory */ - if (res == FR_OK) - res = f_findnext(dp, fno); /* Find the first item */ - return res; -} - -#endif /* _USE_FIND */ - - - -#if _FS_MINIMIZE == 0 -/*-----------------------------------------------------------------------*/ -/* Get File Status */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_stat ( - const TCHAR* path, /* Pointer to the file path */ - FILINFO* fno /* Pointer to file information to return */ -) -{ - FRESULT res; - DIR dj; - DEFINE_NAMEBUF; - - - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 0); - if (res == FR_OK) { - INIT_BUF(dj); - res = follow_path(&dj, path); /* Follow the file path */ - if (res == FR_OK) { /* Follow completed */ - if (dj.dir) { /* Found an object */ - if (fno) get_fileinfo(&dj, fno); - } else { /* It is root directory */ - res = FR_INVALID_NAME; - } - } - FREE_BUF(); - } - - LEAVE_FF(dj.fs, res); -} - - - -#if !_FS_READONLY -/*-----------------------------------------------------------------------*/ -/* Get Number of Free Clusters */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_getfree ( - const TCHAR* path, /* Path name of the logical drive number */ - DWORD* nclst, /* Pointer to a variable to return number of free clusters */ - FATFS** fatfs /* Pointer to return pointer to corresponding file system object */ -) -{ - FRESULT res; - FATFS *fs; - DWORD n, clst, sect, stat; - UINT i; - BYTE fat, *p; - - - /* Get logical drive number */ - res = find_volume(fatfs, &path, 0); - fs = *fatfs; - if (res == FR_OK) { - /* If free_clust is valid, return it without full cluster scan */ - if (fs->free_clust <= fs->n_fatent - 2) { - *nclst = fs->free_clust; - } else { - /* Get number of free clusters */ - fat = fs->fs_type; - n = 0; - if (fat == FS_FAT12) { - clst = 2; - do { - stat = get_fat(fs, clst); - if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } - if (stat == 1) { res = FR_INT_ERR; break; } - if (stat == 0) n++; - } while (++clst < fs->n_fatent); - } else { - clst = fs->n_fatent; - sect = fs->fatbase; - i = 0; p = 0; - do { - if (!i) { - res = move_window(fs, sect++); - if (res != FR_OK) break; - p = fs->win; - i = SS(fs); - } - if (fat == FS_FAT16) { - if (LD_WORD(p) == 0) n++; - p += 2; i -= 2; - } else { - if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++; - p += 4; i -= 4; - } - } while (--clst); - } - fs->free_clust = n; - fs->fsi_flag |= 1; - *nclst = n; - } - } - LEAVE_FF(fs, res); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Truncate File */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_truncate ( - FIL* fp /* Pointer to the file object */ -) -{ - FRESULT res; - DWORD ncl; - - - res = validate(fp); /* Check validity of the object */ - if (res == FR_OK) { - if (fp->err) { /* Check error */ - res = (FRESULT)fp->err; - } else { - if (!(fp->flag & FA_WRITE)) /* Check access mode */ - res = FR_DENIED; - } - } - if (res == FR_OK) { - if (fp->fsize > fp->fptr) { - fp->fsize = fp->fptr; /* Set file size to current R/W point */ - fp->flag |= FA__WRITTEN; - if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ - res = remove_chain(fp->fs, fp->sclust); - fp->sclust = 0; - } else { /* When truncate a part of the file, remove remaining clusters */ - ncl = get_fat(fp->fs, fp->clust); - res = FR_OK; - if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; - if (ncl == 1) res = FR_INT_ERR; - if (res == FR_OK && ncl < fp->fs->n_fatent) { - res = put_fat(fp->fs, fp->clust, 0x0FFFFFFF); - if (res == FR_OK) res = remove_chain(fp->fs, ncl); - } - } -#if !_FS_TINY - if (res == FR_OK && (fp->flag & FA__DIRTY)) { - if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK) - res = FR_DISK_ERR; - else - fp->flag &= ~FA__DIRTY; - } -#endif - } - if (res != FR_OK) fp->err = (FRESULT)res; - } - - LEAVE_FF(fp->fs, res); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Delete a File or Directory */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_unlink ( - const TCHAR* path /* Pointer to the file or directory path */ -) -{ - FRESULT res; - DIR dj, sdj; - BYTE *dir; - DWORD dclst = 0; - DEFINE_NAMEBUF; - - - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 1); - if (res == FR_OK) { - INIT_BUF(dj); - res = follow_path(&dj, path); /* Follow the file path */ - if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) - res = FR_INVALID_NAME; /* Cannot remove dot entry */ -#if _FS_LOCK - if (res == FR_OK) res = chk_lock(&dj, 2); /* Cannot remove open object */ -#endif - if (res == FR_OK) { /* The object is accessible */ - dir = dj.dir; - if (!dir) { - res = FR_INVALID_NAME; /* Cannot remove the origin directory */ - } else { - if (dir[DIR_Attr] & AM_RDO) - res = FR_DENIED; /* Cannot remove R/O object */ - } - if (res == FR_OK) { - dclst = ld_clust(dj.fs, dir); - if (dclst && (dir[DIR_Attr] & AM_DIR)) { /* Is it a sub-directory ? */ -#if _FS_RPATH - if (dclst == dj.fs->cdir) { /* Is it the current directory? */ - res = FR_DENIED; - } else -#endif - { - mem_cpy(&sdj, &dj, sizeof (DIR)); /* Open the sub-directory */ - sdj.sclust = dclst; - res = dir_sdi(&sdj, 2); - if (res == FR_OK) { - res = dir_read(&sdj, 0); /* Read an item (excluding dot entries) */ - if (res == FR_OK) res = FR_DENIED; /* Not empty? (cannot remove) */ - if (res == FR_NO_FILE) res = FR_OK; /* Empty? (can remove) */ - } - } - } - } - if (res == FR_OK) { - res = dir_remove(&dj); /* Remove the directory entry */ - if (res == FR_OK && dclst) /* Remove the cluster chain if exist */ - res = remove_chain(dj.fs, dclst); - if (res == FR_OK) res = sync_fs(dj.fs); - } - } - FREE_BUF(); - } - - LEAVE_FF(dj.fs, res); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Create a Directory */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_mkdir ( - const TCHAR* path /* Pointer to the directory path */ -) -{ - FRESULT res; - DIR dj; - BYTE *dir, n; - DWORD dsc, dcl, pcl, tm = GET_FATTIME(); - DEFINE_NAMEBUF; - - - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 1); - if (res == FR_OK) { - INIT_BUF(dj); - res = follow_path(&dj, path); /* Follow the file path */ - if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */ - if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) - res = FR_INVALID_NAME; - if (res == FR_NO_FILE) { /* Can create a new directory */ - dcl = create_chain(dj.fs, 0); /* Allocate a cluster for the new directory table */ - res = FR_OK; - if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */ - if (dcl == 1) res = FR_INT_ERR; - if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; - if (res == FR_OK) /* Flush FAT */ - res = sync_window(dj.fs); - if (res == FR_OK) { /* Initialize the new directory table */ - dsc = clust2sect(dj.fs, dcl); - dir = dj.fs->win; - mem_set(dir, 0, SS(dj.fs)); - mem_set(dir + DIR_Name, ' ', 11); /* Create "." entry */ - dir[DIR_Name] = '.'; - dir[DIR_Attr] = AM_DIR; - ST_DWORD(dir + DIR_WrtTime, tm); - st_clust(dir, dcl); - mem_cpy(dir + SZ_DIRE, dir, SZ_DIRE); /* Create ".." entry */ - dir[SZ_DIRE + 1] = '.'; pcl = dj.sclust; - if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase) - pcl = 0; - st_clust(dir + SZ_DIRE, pcl); - for (n = dj.fs->csize; n; n--) { /* Write dot entries and clear following sectors */ - dj.fs->winsect = dsc++; - dj.fs->wflag = 1; - res = sync_window(dj.fs); - if (res != FR_OK) break; - mem_set(dir, 0, SS(dj.fs)); - } - } - if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */ - if (res != FR_OK) { - remove_chain(dj.fs, dcl); /* Could not register, remove cluster chain */ - } else { - dir = dj.dir; - dir[DIR_Attr] = AM_DIR; /* Attribute */ - ST_DWORD(dir + DIR_WrtTime, tm); /* Created time */ - st_clust(dir, dcl); /* Table start cluster */ - dj.fs->wflag = 1; - res = sync_fs(dj.fs); - } - } - FREE_BUF(); - } - - LEAVE_FF(dj.fs, res); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Change Attribute */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_chmod ( - const TCHAR* path, /* Pointer to the file path */ - BYTE attr, /* Attribute bits */ - BYTE mask /* Attribute mask to change */ -) -{ - FRESULT res; - DIR dj; - BYTE *dir; - DEFINE_NAMEBUF; - - - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 1); - if (res == FR_OK) { - INIT_BUF(dj); - res = follow_path(&dj, path); /* Follow the file path */ - FREE_BUF(); - if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) - res = FR_INVALID_NAME; - if (res == FR_OK) { - dir = dj.dir; - if (!dir) { /* Is it a root directory? */ - res = FR_INVALID_NAME; - } else { /* File or sub directory */ - mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ - dir[DIR_Attr] = (attr & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ - dj.fs->wflag = 1; - res = sync_fs(dj.fs); - } - } - } - - LEAVE_FF(dj.fs, res); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Rename File/Directory */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_rename ( - const TCHAR* path_old, /* Pointer to the object to be renamed */ - const TCHAR* path_new /* Pointer to the new name */ -) -{ - FRESULT res; - DIR djo, djn; - BYTE buf[21], *dir; - DWORD dw; - DEFINE_NAMEBUF; - - - /* Get logical drive number of the source object */ - res = find_volume(&djo.fs, &path_old, 1); - if (res == FR_OK) { - djn.fs = djo.fs; - INIT_BUF(djo); - res = follow_path(&djo, path_old); /* Check old object */ - if (_FS_RPATH && res == FR_OK && (djo.fn[NSFLAG] & NS_DOT)) - res = FR_INVALID_NAME; -#if _FS_LOCK - if (res == FR_OK) res = chk_lock(&djo, 2); -#endif - if (res == FR_OK) { /* Old object is found */ - if (!djo.dir) { /* Is root dir? */ - res = FR_NO_FILE; - } else { - mem_cpy(buf, djo.dir + DIR_Attr, 21); /* Save information about object except name */ - mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ - if (get_ldnumber(&path_new) >= 0) /* Snip drive number off and ignore it */ - res = follow_path(&djn, path_new); /* and make sure if new object name is not conflicting */ - else - res = FR_INVALID_DRIVE; - if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */ - if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ - res = dir_register(&djn); /* Register the new entry */ - if (res == FR_OK) { -/* Start of critical section where any interruption can cause a cross-link */ - dir = djn.dir; /* Copy information about object except name */ - mem_cpy(dir + 13, buf + 2, 19); - dir[DIR_Attr] = buf[0] | AM_ARC; - djo.fs->wflag = 1; - if ((dir[DIR_Attr] & AM_DIR) && djo.sclust != djn.sclust) { /* Update .. entry in the sub-directory if needed */ - dw = clust2sect(djo.fs, ld_clust(djo.fs, dir)); - if (!dw) { - res = FR_INT_ERR; - } else { - res = move_window(djo.fs, dw); - dir = djo.fs->win + SZ_DIRE * 1; /* Ptr to .. entry */ - if (res == FR_OK && dir[1] == '.') { - st_clust(dir, djn.sclust); - djo.fs->wflag = 1; - } - } - } - if (res == FR_OK) { - res = dir_remove(&djo); /* Remove old entry */ - if (res == FR_OK) - res = sync_fs(djo.fs); - } -/* End of critical section */ - } - } - } - } - FREE_BUF(); - } - - LEAVE_FF(djo.fs, res); -} - - - - -/*-----------------------------------------------------------------------*/ -/* Change Timestamp */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_utime ( - const TCHAR* path, /* Pointer to the file/directory name */ - const FILINFO* fno /* Pointer to the time stamp to be set */ -) -{ - FRESULT res; - DIR dj; - BYTE *dir; - DEFINE_NAMEBUF; - - - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 1); - if (res == FR_OK) { - INIT_BUF(dj); - res = follow_path(&dj, path); /* Follow the file path */ - FREE_BUF(); - if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) - res = FR_INVALID_NAME; - if (res == FR_OK) { - dir = dj.dir; - if (!dir) { /* Root directory */ - res = FR_INVALID_NAME; - } else { /* File or sub-directory */ - ST_WORD(dir + DIR_WrtTime, fno->ftime); - ST_WORD(dir + DIR_WrtDate, fno->fdate); - dj.fs->wflag = 1; - res = sync_fs(dj.fs); - } - } - } - - LEAVE_FF(dj.fs, res); -} - -#endif /* !_FS_READONLY */ -#endif /* _FS_MINIMIZE == 0 */ -#endif /* _FS_MINIMIZE <= 1 */ -#endif /* _FS_MINIMIZE <= 2 */ - - - - -#if _USE_LABEL -/*-----------------------------------------------------------------------*/ -/* Get volume label */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_getlabel ( - const TCHAR* path, /* Path name of the logical drive number */ - TCHAR* label, /* Pointer to a buffer to return the volume label */ - DWORD* vsn /* Pointer to a variable to return the volume serial number */ -) -{ - FRESULT res; - DIR dj; - UINT i, j; -#if _USE_LFN && _LFN_UNICODE - WCHAR w; -#endif - - - /* Get logical drive number */ - res = find_volume(&dj.fs, &path, 0); - - /* Get volume label */ - if (res == FR_OK && label) { - dj.sclust = 0; /* Open root directory */ - res = dir_sdi(&dj, 0); - if (res == FR_OK) { - res = dir_read(&dj, 1); /* Get an entry with AM_VOL */ - if (res == FR_OK) { /* A volume label is exist */ -#if _USE_LFN && _LFN_UNICODE - i = j = 0; - do { - w = (i < 11) ? dj.dir[i++] : ' '; - if (IsDBCS1(w) && i < 11 && IsDBCS2(dj.dir[i])) - w = w << 8 | dj.dir[i++]; - label[j++] = ff_convert(w, 1); /* OEM -> Unicode */ - } while (j < 11); -#else - mem_cpy(label, dj.dir, 11); -#endif - j = 11; - do { - label[j] = 0; - if (!j) break; - } while (label[--j] == ' '); - } - if (res == FR_NO_FILE) { /* No label, return nul string */ - label[0] = 0; - res = FR_OK; - } - } - } - - /* Get volume serial number */ - if (res == FR_OK && vsn) { - res = move_window(dj.fs, dj.fs->volbase); - if (res == FR_OK) { - i = dj.fs->fs_type == FS_FAT32 ? BS_VolID32 : BS_VolID; - *vsn = LD_DWORD(&dj.fs->win[i]); - } - } - - LEAVE_FF(dj.fs, res); -} - - - -#if !_FS_READONLY -/*-----------------------------------------------------------------------*/ -/* Set volume label */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_setlabel ( - const TCHAR* label /* Pointer to the volume label to set */ -) -{ - FRESULT res; - DIR dj; - BYTE vn[11]; - UINT i, j, sl; - WCHAR w; - DWORD tm; - - - /* Get logical drive number */ - res = find_volume(&dj.fs, &label, 1); - if (res) LEAVE_FF(dj.fs, res); - - /* Create a volume label in directory form */ - vn[0] = 0; - for (sl = 0; label[sl]; sl++) ; /* Get name length */ - for ( ; sl && label[sl - 1] == ' '; sl--) ; /* Remove trailing spaces */ - if (sl) { /* Create volume label in directory form */ - i = j = 0; - do { -#if _USE_LFN && _LFN_UNICODE - w = ff_convert(ff_wtoupper(label[i++]), 0); -#else - w = (BYTE)label[i++]; - if (IsDBCS1(w)) - w = (j < 10 && i < sl && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; -#if _USE_LFN - w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0); -#else - if (IsLower(w)) w -= 0x20; /* To upper ASCII characters */ -#ifdef _EXCVT - if (w >= 0x80) w = ExCvt[w - 0x80]; /* To upper extended characters (SBCS cfg) */ -#else - if (!_DF1S && w >= 0x80) w = 0; /* Reject extended characters (ASCII cfg) */ -#endif -#endif -#endif - if (!w || chk_chr("\"*+,.:;<=>\?[]|\x7F", w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) /* Reject invalid characters for volume label */ - LEAVE_FF(dj.fs, FR_INVALID_NAME); - if (w >= 0x100) vn[j++] = (BYTE)(w >> 8); - vn[j++] = (BYTE)w; - } while (i < sl); - while (j < 11) vn[j++] = ' '; /* Fill remaining name field */ - if (vn[0] == DDEM) LEAVE_FF(dj.fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ - } - - /* Set volume label */ - dj.sclust = 0; /* Open root directory */ - res = dir_sdi(&dj, 0); - if (res == FR_OK) { - res = dir_read(&dj, 1); /* Get an entry with AM_VOL */ - if (res == FR_OK) { /* A volume label is found */ - if (vn[0]) { - mem_cpy(dj.dir, vn, 11); /* Change the volume label name */ - tm = GET_FATTIME(); - ST_DWORD(dj.dir + DIR_WrtTime, tm); - } else { - dj.dir[0] = DDEM; /* Remove the volume label */ - } - dj.fs->wflag = 1; - res = sync_fs(dj.fs); - } else { /* No volume label is found or error */ - if (res == FR_NO_FILE) { - res = FR_OK; - if (vn[0]) { /* Create volume label as new */ - res = dir_alloc(&dj, 1); /* Allocate an entry for volume label */ - if (res == FR_OK) { - mem_set(dj.dir, 0, SZ_DIRE); /* Set volume label */ - mem_cpy(dj.dir, vn, 11); - dj.dir[DIR_Attr] = AM_VOL; - tm = GET_FATTIME(); - ST_DWORD(dj.dir + DIR_WrtTime, tm); - dj.fs->wflag = 1; - res = sync_fs(dj.fs); - } - } - } - } - } - - LEAVE_FF(dj.fs, res); -} - -#endif /* !_FS_READONLY */ -#endif /* _USE_LABEL */ - - - -/*-----------------------------------------------------------------------*/ -/* Forward data to the stream directly (available on only tiny cfg) */ -/*-----------------------------------------------------------------------*/ -#if _USE_FORWARD && _FS_TINY - -FRESULT f_forward ( - FIL* fp, /* Pointer to the file object */ - UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ - UINT btf, /* Number of bytes to forward */ - UINT* bf /* Pointer to number of bytes forwarded */ -) -{ - FRESULT res; - DWORD remain, clst, sect; - UINT rcnt; - BYTE csect; - - - *bf = 0; /* Clear transfer byte counter */ - - res = validate(fp); /* Check validity of the object */ - if (res != FR_OK) LEAVE_FF(fp->fs, res); - if (fp->err) /* Check error */ - LEAVE_FF(fp->fs, (FRESULT)fp->err); - if (!(fp->flag & FA_READ)) /* Check access mode */ - LEAVE_FF(fp->fs, FR_DENIED); - - remain = fp->fsize - fp->fptr; - if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */ - - for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream becomes busy */ - fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { - csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */ - if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ - if (!csect) { /* On the cluster boundary? */ - clst = (fp->fptr == 0) ? /* On the top of the file? */ - fp->sclust : get_fat(fp->fs, fp->clust); - if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); - if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); - fp->clust = clst; /* Update current cluster */ - } - } - sect = clust2sect(fp->fs, fp->clust); /* Get current data sector */ - if (!sect) ABORT(fp->fs, FR_INT_ERR); - sect += csect; - if (move_window(fp->fs, sect) != FR_OK) /* Move sector window */ - ABORT(fp->fs, FR_DISK_ERR); - fp->dsect = sect; - rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs)); /* Forward data from sector window */ - if (rcnt > btf) rcnt = btf; - rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt); - if (!rcnt) ABORT(fp->fs, FR_INT_ERR); - } - - LEAVE_FF(fp->fs, FR_OK); -} -#endif /* _USE_FORWARD */ - - - -#if _USE_MKFS && !_FS_READONLY -/*-----------------------------------------------------------------------*/ -/* Create file system on the logical drive */ -/*-----------------------------------------------------------------------*/ -#define N_ROOTDIR 512 /* Number of root directory entries for FAT12/16 */ -#define N_FATS 1 /* Number of FATs (1 or 2) */ - - -FRESULT f_mkfs ( - const TCHAR* path, /* Logical drive number */ - BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */ - UINT au /* Size of allocation unit in unit of byte or sector */ -) -{ - static const WORD vst[] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 0}; - static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512}; - int vol; - BYTE fmt, md, sys, *tbl, pdrv, part; - DWORD n_clst, vs, n, wsect; - UINT i; - DWORD b_vol, b_fat, b_dir, b_data; /* LBA */ - DWORD n_vol, n_rsv, n_fat, n_dir; /* Size */ - FATFS *fs; - DSTATUS stat; -#if _USE_TRIM - DWORD eb[2]; -#endif - - - /* Check mounted drive and clear work area */ - if (sfd > 1) return FR_INVALID_PARAMETER; - vol = get_ldnumber(&path); - if (vol < 0) return FR_INVALID_DRIVE; - fs = FatFs[vol]; - if (!fs) return FR_NOT_ENABLED; - fs->fs_type = 0; - pdrv = LD2PD(vol); /* Physical drive */ - part = LD2PT(vol); /* Partition (0:auto detect, 1-4:get from partition table)*/ - - /* Get disk statics */ - stat = disk_initialize(pdrv); - if (stat & STA_NOINIT) return FR_NOT_READY; - if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; -#if _MAX_SS != _MIN_SS /* Get disk sector size */ - if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS || SS(fs) < _MIN_SS) - return FR_DISK_ERR; -#endif - if (_MULTI_PARTITION && part) { - /* Get partition information from partition table in the MBR */ - if (disk_read(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR; - if (LD_WORD(fs->win + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; - tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; - if (!tbl[4]) return FR_MKFS_ABORTED; /* No partition? */ - b_vol = LD_DWORD(tbl + 8); /* Volume start sector */ - n_vol = LD_DWORD(tbl + 12); /* Volume size */ - } else { - /* Create a partition in this function */ - if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128) - return FR_DISK_ERR; - b_vol = (sfd) ? 0 : 63; /* Volume start sector */ - n_vol -= b_vol; /* Volume size */ - } - - if (au & (au - 1)) au = 0; - if (!au) { /* AU auto selection */ - vs = n_vol / (2000 / (SS(fs) / 512)); - for (i = 0; vs < vst[i]; i++) ; - au = cst[i]; - } - if (au >= _MIN_SS) au /= SS(fs); /* Number of sectors per cluster */ - if (!au) au = 1; - if (au > 128) au = 128; - - /* Pre-compute number of clusters and FAT sub-type */ - n_clst = n_vol / au; - fmt = FS_FAT12; - if (n_clst >= MIN_FAT16) fmt = FS_FAT16; - if (n_clst >= MIN_FAT32) fmt = FS_FAT32; - - /* Determine offset and size of FAT structure */ - if (fmt == FS_FAT32) { - n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); - n_rsv = 32; - n_dir = 0; - } else { - n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4; - n_fat = (n_fat + SS(fs) - 1) / SS(fs); - n_rsv = 1; - n_dir = (DWORD)N_ROOTDIR * SZ_DIRE / SS(fs); - } - b_fat = b_vol + n_rsv; /* FAT area start sector */ - b_dir = b_fat + n_fat * N_FATS; /* Directory area start sector */ - b_data = b_dir + n_dir; /* Data area start sector */ - if (n_vol < b_data + au - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ - - /* Align data start sector to erase block boundary (for flash memory media) */ - if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1; - n = (b_data + n - 1) & ~(n - 1); /* Next nearest erase block from current data start */ - n = (n - b_data) / N_FATS; - if (fmt == FS_FAT32) { /* FAT32: Move FAT offset */ - n_rsv += n; - b_fat += n; - } else { /* FAT12/16: Expand FAT size */ - n_fat += n; - } - - /* Determine number of clusters and final check of validity of the FAT sub-type */ - n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au; - if ( (fmt == FS_FAT16 && n_clst < MIN_FAT16) - || (fmt == FS_FAT32 && n_clst < MIN_FAT32)) - return FR_MKFS_ABORTED; - - /* Determine system ID in the partition table */ - if (fmt == FS_FAT32) { - sys = 0x0C; /* FAT32X */ - } else { - if (fmt == FS_FAT12 && n_vol < 0x10000) { - sys = 0x01; /* FAT12(<65536) */ - } else { - sys = (n_vol < 0x10000) ? 0x04 : 0x06; /* FAT16(<65536) : FAT12/16(>=65536) */ - } - } - - if (_MULTI_PARTITION && part) { - /* Update system ID in the partition table */ - tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE]; - tbl[4] = sys; - if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) /* Write it to teh MBR */ - return FR_DISK_ERR; - md = 0xF8; - } else { - if (sfd) { /* No partition table (SFD) */ - md = 0xF0; - } else { /* Create partition table (FDISK) */ - mem_set(fs->win, 0, SS(fs)); - tbl = fs->win + MBR_Table; /* Create partition table for single partition in the drive */ - tbl[1] = 1; /* Partition start head */ - tbl[2] = 1; /* Partition start sector */ - tbl[3] = 0; /* Partition start cylinder */ - tbl[4] = sys; /* System type */ - tbl[5] = 254; /* Partition end head */ - n = (b_vol + n_vol) / 63 / 255; - tbl[6] = (BYTE)(n >> 2 | 63); /* Partition end sector */ - tbl[7] = (BYTE)n; /* End cylinder */ - ST_DWORD(tbl + 8, 63); /* Partition start in LBA */ - ST_DWORD(tbl + 12, n_vol); /* Partition size in LBA */ - ST_WORD(fs->win + BS_55AA, 0xAA55); /* MBR signature */ - if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) /* Write it to the MBR */ - return FR_DISK_ERR; - md = 0xF8; - } - } - - /* Create BPB in the VBR */ - tbl = fs->win; /* Clear sector */ - mem_set(tbl, 0, SS(fs)); - mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */ - i = SS(fs); /* Sector size */ - ST_WORD(tbl + BPB_BytsPerSec, i); - tbl[BPB_SecPerClus] = (BYTE)au; /* Sectors per cluster */ - ST_WORD(tbl + BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */ - tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ - i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR; /* Number of root directory entries */ - ST_WORD(tbl + BPB_RootEntCnt, i); - if (n_vol < 0x10000) { /* Number of total sectors */ - ST_WORD(tbl + BPB_TotSec16, n_vol); - } else { - ST_DWORD(tbl + BPB_TotSec32, n_vol); - } - tbl[BPB_Media] = md; /* Media descriptor */ - ST_WORD(tbl + BPB_SecPerTrk, 63); /* Number of sectors per track */ - ST_WORD(tbl + BPB_NumHeads, 255); /* Number of heads */ - ST_DWORD(tbl + BPB_HiddSec, b_vol); /* Hidden sectors */ - n = GET_FATTIME(); /* Use current time as VSN */ - if (fmt == FS_FAT32) { - ST_DWORD(tbl + BS_VolID32, n); /* VSN */ - ST_DWORD(tbl + BPB_FATSz32, n_fat); /* Number of sectors per FAT */ - ST_DWORD(tbl + BPB_RootClus, 2); /* Root directory start cluster (2) */ - ST_WORD(tbl + BPB_FSInfo, 1); /* FSINFO record offset (VBR + 1) */ - ST_WORD(tbl + BPB_BkBootSec, 6); /* Backup boot record offset (VBR + 6) */ - tbl[BS_DrvNum32] = 0x80; /* Drive number */ - tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ - mem_cpy(tbl + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ - } else { - ST_DWORD(tbl + BS_VolID, n); /* VSN */ - ST_WORD(tbl + BPB_FATSz16, n_fat); /* Number of sectors per FAT */ - tbl[BS_DrvNum] = 0x80; /* Drive number */ - tbl[BS_BootSig] = 0x29; /* Extended boot signature */ - mem_cpy(tbl + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ - } - ST_WORD(tbl + BS_55AA, 0xAA55); /* Signature (Offset is fixed here regardless of sector size) */ - if (disk_write(pdrv, tbl, b_vol, 1) != RES_OK) /* Write it to the VBR sector */ - return FR_DISK_ERR; - if (fmt == FS_FAT32) /* Write backup VBR if needed (VBR + 6) */ - disk_write(pdrv, tbl, b_vol + 6, 1); - - /* Initialize FAT area */ - wsect = b_fat; - for (i = 0; i < N_FATS; i++) { /* Initialize each FAT copy */ - mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */ - n = md; /* Media descriptor byte */ - if (fmt != FS_FAT32) { - n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00; - ST_DWORD(tbl + 0, n); /* Reserve cluster #0-1 (FAT12/16) */ - } else { - n |= 0xFFFFFF00; - ST_DWORD(tbl + 0, n); /* Reserve cluster #0-1 (FAT32) */ - ST_DWORD(tbl + 4, 0xFFFFFFFF); - ST_DWORD(tbl + 8, 0x0FFFFFFF); /* Reserve cluster #2 for root directory */ - } - if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) - return FR_DISK_ERR; - mem_set(tbl, 0, SS(fs)); /* Fill following FAT entries with zero */ - for (n = 1; n < n_fat; n++) { /* This loop may take a time on FAT32 volume due to many single sector writes */ - if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) - return FR_DISK_ERR; - } - } - - /* Initialize root directory */ - i = (fmt == FS_FAT32) ? au : (UINT)n_dir; - do { - if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK) - return FR_DISK_ERR; - } while (--i); - -#if _USE_TRIM /* Erase data area if needed */ - { - eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1; - disk_ioctl(pdrv, CTRL_TRIM, eb); - } -#endif - - /* Create FSINFO if needed */ - if (fmt == FS_FAT32) { - ST_DWORD(tbl + FSI_LeadSig, 0x41615252); - ST_DWORD(tbl + FSI_StrucSig, 0x61417272); - ST_DWORD(tbl + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ - ST_DWORD(tbl + FSI_Nxt_Free, 2); /* Last allocated cluster# */ - ST_WORD(tbl + BS_55AA, 0xAA55); - disk_write(pdrv, tbl, b_vol + 1, 1); /* Write original (VBR + 1) */ - disk_write(pdrv, tbl, b_vol + 7, 1); /* Write backup (VBR + 7) */ - } - - return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR; -} - - - -#if _MULTI_PARTITION -/*-----------------------------------------------------------------------*/ -/* Create partition table on the physical drive */ -/*-----------------------------------------------------------------------*/ - -FRESULT f_fdisk ( - BYTE pdrv, /* Physical drive number */ - const DWORD szt[], /* Pointer to the size table for each partitions */ - void* work /* Pointer to the working buffer */ -) -{ - UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl; - BYTE s_hd, e_hd, *p, *buf = (BYTE*)work; - DSTATUS stat; - DWORD sz_disk, sz_part, s_part; - - - stat = disk_initialize(pdrv); - if (stat & STA_NOINIT) return FR_NOT_READY; - if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; - if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR; - - /* Determine CHS in the table regardless of the drive geometry */ - for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; - if (n == 256) n--; - e_hd = n - 1; - sz_cyl = 63 * n; - tot_cyl = sz_disk / sz_cyl; - - /* Create partition table */ - mem_set(buf, 0, _MAX_SS); - p = buf + MBR_Table; b_cyl = 0; - for (i = 0; i < 4; i++, p += SZ_PTE) { - p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl; - if (!p_cyl) continue; - s_part = (DWORD)sz_cyl * b_cyl; - sz_part = (DWORD)sz_cyl * p_cyl; - if (i == 0) { /* Exclude first track of cylinder 0 */ - s_hd = 1; - s_part += 63; sz_part -= 63; - } else { - s_hd = 0; - } - e_cyl = b_cyl + p_cyl - 1; - if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER; - - /* Set partition table */ - p[1] = s_hd; /* Start head */ - p[2] = (BYTE)((b_cyl >> 2) + 1); /* Start sector */ - p[3] = (BYTE)b_cyl; /* Start cylinder */ - p[4] = 0x06; /* System type (temporary setting) */ - p[5] = e_hd; /* End head */ - p[6] = (BYTE)((e_cyl >> 2) + 63); /* End sector */ - p[7] = (BYTE)e_cyl; /* End cylinder */ - ST_DWORD(p + 8, s_part); /* Start sector in LBA */ - ST_DWORD(p + 12, sz_part); /* Partition size */ - - /* Next partition */ - b_cyl += p_cyl; - } - ST_WORD(p, 0xAA55); - - /* Write it to the MBR */ - return (disk_write(pdrv, buf, 0, 1) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK; -} - - -#endif /* _MULTI_PARTITION */ -#endif /* _USE_MKFS && !_FS_READONLY */ - - - - -#if _USE_STRFUNC -/*-----------------------------------------------------------------------*/ -/* Get a string from the file */ -/*-----------------------------------------------------------------------*/ - -TCHAR* f_gets ( - TCHAR* buff, /* Pointer to the string buffer to read */ - int len, /* Size of string buffer (characters) */ - FIL* fp /* Pointer to the file object */ -) -{ - int n = 0; - TCHAR c, *p = buff; - BYTE s[2]; - UINT rc; - - - while (n < len - 1) { /* Read characters until buffer gets filled */ -#if _USE_LFN && _LFN_UNICODE -#if _STRF_ENCODE == 3 /* Read a character in UTF-8 */ - f_read(fp, s, 1, &rc); - if (rc != 1) break; - c = s[0]; - if (c >= 0x80) { - if (c < 0xC0) continue; /* Skip stray trailer */ - if (c < 0xE0) { /* Two-byte sequence */ - f_read(fp, s, 1, &rc); - if (rc != 1) break; - c = (c & 0x1F) << 6 | (s[0] & 0x3F); - if (c < 0x80) c = '?'; - } else { - if (c < 0xF0) { /* Three-byte sequence */ - f_read(fp, s, 2, &rc); - if (rc != 2) break; - c = c << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F); - if (c < 0x800) c = '?'; - } else { /* Reject four-byte sequence */ - c = '?'; - } - } - } -#elif _STRF_ENCODE == 2 /* Read a character in UTF-16BE */ - f_read(fp, s, 2, &rc); - if (rc != 2) break; - c = s[1] + (s[0] << 8); -#elif _STRF_ENCODE == 1 /* Read a character in UTF-16LE */ - f_read(fp, s, 2, &rc); - if (rc != 2) break; - c = s[0] + (s[1] << 8); -#else /* Read a character in ANSI/OEM */ - f_read(fp, s, 1, &rc); - if (rc != 1) break; - c = s[0]; - if (IsDBCS1(c)) { - f_read(fp, s, 1, &rc); - if (rc != 1) break; - c = (c << 8) + s[0]; - } - c = ff_convert(c, 1); /* OEM -> Unicode */ - if (!c) c = '?'; -#endif -#else /* Read a character without conversion */ - f_read(fp, s, 1, &rc); - if (rc != 1) break; - c = s[0]; -#endif - if (_USE_STRFUNC == 2 && c == '\r') continue; /* Strip '\r' */ - *p++ = c; - n++; - if (c == '\n') break; /* Break on EOL */ - } - *p = 0; - return n ? buff : 0; /* When no data read (eof or error), return with error. */ -} - - - - -#if !_FS_READONLY -#include -/*-----------------------------------------------------------------------*/ -/* Put a character to the file */ -/*-----------------------------------------------------------------------*/ - -typedef struct { - FIL* fp; - int idx, nchr; - BYTE buf[64]; -} putbuff; - - -static -void putc_bfd ( - putbuff* pb, - TCHAR c -) -{ - UINT bw; - int i; - - - if (_USE_STRFUNC == 2 && c == '\n') /* LF -> CRLF conversion */ - putc_bfd(pb, '\r'); - - i = pb->idx; /* Buffer write index (-1:error) */ - if (i < 0) return; - -#if _USE_LFN && _LFN_UNICODE -#if _STRF_ENCODE == 3 /* Write a character in UTF-8 */ - if (c < 0x80) { /* 7-bit */ - pb->buf[i++] = (BYTE)c; - } else { - if (c < 0x800) { /* 11-bit */ - pb->buf[i++] = (BYTE)(0xC0 | c >> 6); - } else { /* 16-bit */ - pb->buf[i++] = (BYTE)(0xE0 | c >> 12); - pb->buf[i++] = (BYTE)(0x80 | (c >> 6 & 0x3F)); - } - pb->buf[i++] = (BYTE)(0x80 | (c & 0x3F)); - } -#elif _STRF_ENCODE == 2 /* Write a character in UTF-16BE */ - pb->buf[i++] = (BYTE)(c >> 8); - pb->buf[i++] = (BYTE)c; -#elif _STRF_ENCODE == 1 /* Write a character in UTF-16LE */ - pb->buf[i++] = (BYTE)c; - pb->buf[i++] = (BYTE)(c >> 8); -#else /* Write a character in ANSI/OEM */ - c = ff_convert(c, 0); /* Unicode -> OEM */ - if (!c) c = '?'; - if (c >= 0x100) - pb->buf[i++] = (BYTE)(c >> 8); - pb->buf[i++] = (BYTE)c; -#endif -#else /* Write a character without conversion */ - pb->buf[i++] = (BYTE)c; -#endif - - if (i >= (int)(sizeof pb->buf) - 3) { /* Write buffered characters to the file */ - f_write(pb->fp, pb->buf, (UINT)i, &bw); - i = (bw == (UINT)i) ? 0 : -1; - } - pb->idx = i; - pb->nchr++; -} - - - -int f_putc ( - TCHAR c, /* A character to be output */ - FIL* fp /* Pointer to the file object */ -) -{ - putbuff pb; - UINT nw; - - - pb.fp = fp; /* Initialize output buffer */ - pb.nchr = pb.idx = 0; - - putc_bfd(&pb, c); /* Put a character */ - - if ( pb.idx >= 0 /* Flush buffered characters to the file */ - && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK - && (UINT)pb.idx == nw) return pb.nchr; - return EOF; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Put a string to the file */ -/*-----------------------------------------------------------------------*/ - -int f_puts ( - const TCHAR* str, /* Pointer to the string to be output */ - FIL* fp /* Pointer to the file object */ -) -{ - putbuff pb; - UINT nw; - - - pb.fp = fp; /* Initialize output buffer */ - pb.nchr = pb.idx = 0; - - while (*str) /* Put the string */ - putc_bfd(&pb, *str++); - - if ( pb.idx >= 0 /* Flush buffered characters to the file */ - && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK - && (UINT)pb.idx == nw) return pb.nchr; - return EOF; -} - - - - -/*-----------------------------------------------------------------------*/ -/* Put a formatted string to the file */ -/*-----------------------------------------------------------------------*/ - -int f_printf ( - FIL* fp, /* Pointer to the file object */ - const TCHAR* fmt, /* Pointer to the format string */ - ... /* Optional arguments... */ -) -{ - va_list arp; - BYTE f, r; - UINT nw, i, j, w; - DWORD v; - TCHAR c, d, s[16], *p; - putbuff pb; - - - pb.fp = fp; /* Initialize output buffer */ - pb.nchr = pb.idx = 0; - - va_start(arp, fmt); - - for (;;) { - c = *fmt++; - if (c == 0) break; /* End of string */ - if (c != '%') { /* Non escape character */ - putc_bfd(&pb, c); - continue; - } - w = f = 0; - c = *fmt++; - if (c == '0') { /* Flag: '0' padding */ - f = 1; c = *fmt++; - } else { - if (c == '-') { /* Flag: left justified */ - f = 2; c = *fmt++; - } - } - while (IsDigit(c)) { /* Precision */ - w = w * 10 + c - '0'; - c = *fmt++; - } - if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ - f |= 4; c = *fmt++; - } - if (!c) break; - d = c; - if (IsLower(d)) d -= 0x20; - switch (d) { /* Type is... */ - case 'S' : /* String */ - p = va_arg(arp, TCHAR*); - for (j = 0; p[j]; j++) ; - if (!(f & 2)) { - while (j++ < w) putc_bfd(&pb, ' '); - } - while (*p) putc_bfd(&pb, *p++); - while (j++ < w) putc_bfd(&pb, ' '); - continue; - case 'C' : /* Character */ - putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue; - case 'B' : /* Binary */ - r = 2; break; - case 'O' : /* Octal */ - r = 8; break; - case 'D' : /* Signed decimal */ - case 'U' : /* Unsigned decimal */ - r = 10; break; - case 'X' : /* Hexdecimal */ - r = 16; break; - default: /* Unknown type (pass-through) */ - putc_bfd(&pb, c); continue; - } - - /* Get an argument and put it in numeral */ - v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); - if (d == 'D' && (v & 0x80000000)) { - v = 0 - v; - f |= 8; - } - i = 0; - do { - d = (TCHAR)(v % r); v /= r; - if (d > 9) d += (c == 'x') ? 0x27 : 0x07; - s[i++] = d + '0'; - } while (v && i < sizeof s / sizeof s[0]); - if (f & 8) s[i++] = '-'; - j = i; d = (f & 1) ? '0' : ' '; - while (!(f & 2) && j++ < w) putc_bfd(&pb, d); - do putc_bfd(&pb, s[--i]); while (i); - while (j++ < w) putc_bfd(&pb, d); - } - - va_end(arp); - - if ( pb.idx >= 0 /* Flush buffered characters to the file */ - && f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK - && (UINT)pb.idx == nw) return pb.nchr; - return EOF; -} - -#endif /* !_FS_READONLY */ -#endif /* _USE_STRFUNC */ diff --git a/Sming/Arch/Esp8266/Components/fatfs/ff.h b/Sming/Arch/Esp8266/Components/fatfs/ff.h deleted file mode 100644 index 22f1810b27..0000000000 --- a/Sming/Arch/Esp8266/Components/fatfs/ff.h +++ /dev/null @@ -1,350 +0,0 @@ -/*---------------------------------------------------------------------------/ -/ FatFs - FAT file system module include R0.11 (C)ChaN, 2015 -/----------------------------------------------------------------------------/ -/ FatFs module is a free software that opened under license policy of -/ following conditions. -/ -/ Copyright (C) 2015, ChaN, all right reserved. -/ -/ 1. Redistributions of source code must retain the above copyright notice, -/ this condition and the following disclaimer. -/ -/ This software is provided by the copyright holder and contributors "AS IS" -/ and any warranties related to this software are DISCLAIMED. -/ The copyright owner or contributors be NOT LIABLE for any damages caused -/ by use of this software. -/---------------------------------------------------------------------------*/ - - -#ifndef _FATFS -#define _FATFS 32020 /* Revision ID */ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "integer.h" /* Basic integer types */ -#include "ffconf.h" /* FatFs configuration options */ -#if _FATFS != _FFCONF -#error Wrong configuration file (ffconf.h). -#endif - - - -/* Definitions of volume management */ - -#if _MULTI_PARTITION /* Multiple partition configuration */ -typedef struct { - BYTE pd; /* Physical drive number */ - BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ -} PARTITION; -extern PARTITION VolToPart[]; /* Volume - Partition resolution table */ -#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */ -#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */ - -#else /* Single partition configuration */ -#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */ -#define LD2PT(vol) 0 /* Find first valid partition or in SFD */ - -#endif - - - -/* Type of path name strings on FatFs API */ - -#if _LFN_UNICODE /* Unicode string */ -#if !_USE_LFN -#error _LFN_UNICODE must be 0 at non-LFN cfg. -#endif -#ifndef _INC_TCHAR -typedef WCHAR TCHAR; -#define _T(x) L ## x -#define _TEXT(x) L ## x -#endif - -#else /* ANSI/OEM string */ -#ifndef _INC_TCHAR -typedef char TCHAR; -#define _T(x) x -#define _TEXT(x) x -#endif - -#endif - - - -/* File system object structure (FATFS) */ - -typedef struct { - BYTE fs_type; /* FAT sub-type (0:Not mounted) */ - BYTE drv; /* Physical drive number */ - BYTE csize; /* Sectors per cluster (1,2,4...128) */ - BYTE n_fats; /* Number of FAT copies (1 or 2) */ - BYTE wflag; /* win[] flag (b0:dirty) */ - BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ - WORD id; /* File system mount ID */ - WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ -#if _MAX_SS != _MIN_SS - WORD ssize; /* Bytes per sector (512, 1024, 2048 or 4096) */ -#endif -#if _FS_REENTRANT - _SYNC_t sobj; /* Identifier of sync object */ -#endif -#if !_FS_READONLY - DWORD last_clust; /* Last allocated cluster */ - DWORD free_clust; /* Number of free clusters */ -#endif -#if _FS_RPATH - DWORD cdir; /* Current directory start cluster (0:root) */ -#endif - DWORD n_fatent; /* Number of FAT entries, = number of clusters + 2 */ - DWORD fsize; /* Sectors per FAT */ - DWORD volbase; /* Volume start sector */ - DWORD fatbase; /* FAT start sector */ - DWORD dirbase; /* Root directory start sector (FAT32:Cluster#) */ - DWORD database; /* Data start sector */ - DWORD winsect; /* Current sector appearing in the win[] */ - BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */ -} FATFS; - - - -/* File object structure (FIL) */ - -typedef struct { - FATFS* fs; /* Pointer to the related file system object (**do not change order**) */ - WORD id; /* Owner file system mount ID (**do not change order**) */ - BYTE flag; /* Status flags */ - BYTE err; /* Abort flag (error code) */ - DWORD fptr; /* File read/write pointer (Zeroed on file open) */ - DWORD fsize; /* File size */ - DWORD sclust; /* File start cluster (0:no cluster chain, always 0 when fsize is 0) */ - DWORD clust; /* Current cluster of fpter (not valid when fprt is 0) */ - DWORD dsect; /* Sector number appearing in buf[] (0:invalid) */ -#if !_FS_READONLY - DWORD dir_sect; /* Sector number containing the directory entry */ - BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */ -#endif -#if _USE_FASTSEEK - DWORD* cltbl; /* Pointer to the cluster link map table (Nulled on file open) */ -#endif -#if _FS_LOCK - UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ -#endif -#if !_FS_TINY - BYTE buf[_MAX_SS]; /* File private data read/write window */ -#endif -} FIL; - - - -/* Directory object structure (DIR) */ - -typedef struct { - FATFS* fs; /* Pointer to the owner file system object (**do not change order**) */ - WORD id; /* Owner file system mount ID (**do not change order**) */ - WORD index; /* Current read/write index number */ - DWORD sclust; /* Table start cluster (0:Root dir) */ - DWORD clust; /* Current cluster */ - DWORD sect; /* Current sector */ - BYTE* dir; /* Pointer to the current SFN entry in the win[] */ - BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */ -#if _FS_LOCK - UINT lockid; /* File lock ID (index of file semaphore table Files[]) */ -#endif -#if _USE_LFN - WCHAR* lfn; /* Pointer to the LFN working buffer */ - WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */ -#endif -#if _USE_FIND - const TCHAR* pat; /* Pointer to the name matching pattern */ -#endif -} DIR; - - - -/* File information structure (FILINFO) */ - -typedef struct { - DWORD fsize; /* File size */ - WORD fdate; /* Last modified date */ - WORD ftime; /* Last modified time */ - BYTE fattrib; /* Attribute */ - TCHAR fname[13]; /* Short file name (8.3 format) */ -#if _USE_LFN - TCHAR* lfname; /* Pointer to the LFN buffer */ - UINT lfsize; /* Size of LFN buffer in TCHAR */ -#endif -} FILINFO; - - - -/* File function return code (FRESULT) */ - -typedef enum { - FR_OK = 0, /* (0) Succeeded */ - FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ - FR_INT_ERR, /* (2) Assertion failed */ - FR_NOT_READY, /* (3) The physical drive cannot work */ - FR_NO_FILE, /* (4) Could not find the file */ - FR_NO_PATH, /* (5) Could not find the path */ - FR_INVALID_NAME, /* (6) The path name format is invalid */ - FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ - FR_EXIST, /* (8) Access denied due to prohibited access */ - FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ - FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ - FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ - FR_NOT_ENABLED, /* (12) The volume has no work area */ - FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ - FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */ - FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ - FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ - FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ - FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_SHARE */ - FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ -} FRESULT; - - - -/*--------------------------------------------------------------*/ -/* FatFs module application interface */ - -FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ -FRESULT f_close (FIL* fp); /* Close an open file object */ -FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from a file */ -FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to a file */ -FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ -FRESULT f_lseek (FIL* fp, DWORD ofs); /* Move file pointer of a file object */ -FRESULT f_truncate (FIL* fp); /* Truncate file */ -FRESULT f_sync (FIL* fp); /* Flush cached data of a writing file */ -FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */ -FRESULT f_closedir (DIR* dp); /* Close an open directory */ -FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */ -FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ -FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */ -FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ -FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ -FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ -FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */ -FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of the file/dir */ -FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change times-tamp of the file/dir */ -FRESULT f_chdir (const TCHAR* path); /* Change current directory */ -FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ -FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ -FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ -FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ -FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ -FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ -FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au); /* Create a file system on the volume */ -FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work); /* Divide a physical drive into some partitions */ -int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ -int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */ -int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ -TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */ - -#define f_eof(fp) ((int)((fp)->fptr == (fp)->fsize)) -#define f_error(fp) ((fp)->err) -#define f_tell(fp) ((fp)->fptr) -#define f_size(fp) ((fp)->fsize) -#define f_rewind(fp) f_lseek((fp), 0) -#define f_rewinddir(dp) f_readdir((dp), 0) - -#ifndef EOF -#define EOF (-1) -#endif - - - - -/*--------------------------------------------------------------*/ -/* Additional user defined functions */ - -/* RTC function */ -#if !_FS_READONLY && !_FS_NORTC -DWORD get_fattime (void); -#endif - -/* Unicode support functions */ -#if _USE_LFN /* Unicode - OEM code conversion */ -WCHAR ff_convert (WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */ -WCHAR ff_wtoupper (WCHAR chr); /* Unicode upper-case conversion */ -#if _USE_LFN == 3 /* Memory functions */ -void* ff_memalloc (UINT msize); /* Allocate memory block */ -void ff_memfree (void* mblock); /* Free memory block */ -#endif -#endif - -/* Sync functions */ -#if _FS_REENTRANT -int ff_cre_syncobj (BYTE vol, _SYNC_t* sobj); /* Create a sync object */ -int ff_req_grant (_SYNC_t sobj); /* Lock sync object */ -void ff_rel_grant (_SYNC_t sobj); /* Unlock sync object */ -int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */ -#endif - - - - -/*--------------------------------------------------------------*/ -/* Flags and offset address */ - - -/* File access control and file status flags (FIL.flag) */ - -#define FA_READ 0x01 -#define FA_OPEN_EXISTING 0x00 - -#if !_FS_READONLY -#define FA_WRITE 0x02 -#define FA_CREATE_NEW 0x04 -#define FA_CREATE_ALWAYS 0x08 -#define FA_OPEN_ALWAYS 0x10 -#define FA__WRITTEN 0x20 -#define FA__DIRTY 0x40 -#endif - - -/* FAT sub type (FATFS.fs_type) */ - -#define FS_FAT12 1 -#define FS_FAT16 2 -#define FS_FAT32 3 - - -/* File attribute bits for directory entry */ - -#define AM_RDO 0x01 /* Read only */ -#define AM_HID 0x02 /* Hidden */ -#define AM_SYS 0x04 /* System */ -#define AM_VOL 0x08 /* Volume label */ -#define AM_LFN 0x0F /* LFN entry */ -#define AM_DIR 0x10 /* Directory */ -#define AM_ARC 0x20 /* Archive */ -#define AM_MASK 0x3F /* Mask of defined bits */ - - -/* Fast seek feature */ -#define CREATE_LINKMAP 0xFFFFFFFF - - - -/*--------------------------------*/ -/* Multi-byte word access macros */ - -#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */ -#define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr)) -#define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr)) -#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val) -#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val) -#else /* Use byte-by-byte access to the FAT structure */ -#define LD_WORD(ptr) (WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr)) -#define LD_DWORD(ptr) (DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr)) -#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8) -#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8); *((BYTE*)(ptr)+2)=(BYTE)((DWORD)(val)>>16); *((BYTE*)(ptr)+3)=(BYTE)((DWORD)(val)>>24) -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* _FATFS */ diff --git a/Sming/Arch/Esp8266/Components/fatfs/ffconf.h b/Sming/Arch/Esp8266/Components/fatfs/ffconf.h deleted file mode 100644 index 9f77061925..0000000000 --- a/Sming/Arch/Esp8266/Components/fatfs/ffconf.h +++ /dev/null @@ -1,266 +0,0 @@ -/*---------------------------------------------------------------------------/ -/ FatFs - FAT file system module configuration file R0.11 (C)ChaN, 2015 -/---------------------------------------------------------------------------*/ - -#define _FFCONF 32020 /* Revision ID */ - -/*---------------------------------------------------------------------------/ -/ Functions and Buffer Configurations -/---------------------------------------------------------------------------*/ - -#define _FS_TINY 1 -/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) -/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS -/ bytes. Instead of private sector buffer eliminated from the file object, -/ common sector buffer in the file system object (FATFS) is used for the file -/ data transfer. */ - - -#define _FS_READONLY 0 -/* This option switches read-only configuration. (0:Read/Write or 1:Read-only) -/ Read-only configuration removes writing API functions, f_write(), f_sync(), -/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() -/ and optional writing functions as well. */ - - -#define _FS_MINIMIZE 0 -/* This option defines minimization level to remove some basic API functions. -/ -/ 0: All basic functions are enabled. -/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(), -/ f_truncate() and f_rename() function are removed. -/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. -/ 3: f_lseek() function is removed in addition to 2. */ - - -#define _USE_STRFUNC 1 -/* This option switches string functions, f_gets(), f_putc(), f_puts() and -/ f_printf(). -/ -/ 0: Disable string functions. -/ 1: Enable without LF-CRLF conversion. -/ 2: Enable with LF-CRLF conversion. */ - - -#define _USE_FIND 1 -/* This option switches filtered directory read feature and related functions, -/ f_findfirst() and f_findnext(). (0:Disable or 1:Enable) */ - - -#define _USE_MKFS 0 -/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ - - -#define _USE_FASTSEEK 0 -/* This option switches fast seek feature. (0:Disable or 1:Enable) */ - - -#define _USE_LABEL 0 -/* This option switches volume label functions, f_getlabel() and f_setlabel(). -/ (0:Disable or 1:Enable) */ - - -#define _USE_FORWARD 0 -/* This option switches f_forward() function. (0:Disable or 1:Enable) -/ To enable it, also _FS_TINY need to be set to 1. */ - - -/*---------------------------------------------------------------------------/ -/ Locale and Namespace Configurations -/---------------------------------------------------------------------------*/ - -#define _CODE_PAGE 1 -/* This option specifies the OEM code page to be used on the target system. -/ Incorrect setting of the code page can cause a file open failure. -/ -/ 1 - ASCII (No extended character. Non-LFN cfg. only) -/ 437 - U.S. -/ 720 - Arabic -/ 737 - Greek -/ 775 - Baltic -/ 850 - Multilingual Latin 1 -/ 852 - Latin 2 -/ 855 - Cyrillic -/ 857 - Turkish -/ 858 - Multilingual Latin 1 + Euro -/ 862 - Hebrew -/ 866 - Russian -/ 874 - Thai -/ 932 - Japanese Shift_JIS (DBCS) -/ 936 - Simplified Chinese GBK (DBCS) -/ 949 - Korean (DBCS) -/ 950 - Traditional Chinese Big5 (DBCS) -*/ - - -#define _USE_LFN 0 -#define _MAX_LFN 255 -/* The _USE_LFN option switches the LFN feature. -/ -/ 0: Disable LFN feature. _MAX_LFN has no effect. -/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. -/ 2: Enable LFN with dynamic working buffer on the STACK. -/ 3: Enable LFN with dynamic working buffer on the HEAP. -/ -/ When enable the LFN feature, Unicode handling functions (option/unicode.c) must -/ be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. -/ When use stack for the working buffer, take care on stack overflow. When use heap -/ memory for the working buffer, memory management functions, ff_memalloc() and -/ ff_memfree(), must be added to the project. */ - - -#define _LFN_UNICODE 0 -/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode) -/ To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE -/ to 1. This option also affects behavior of string I/O functions. */ - - -#define _STRF_ENCODE 3 -/* When _LFN_UNICODE is 1, this option selects the character encoding on the file to -/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf(). -/ -/ 0: ANSI/OEM -/ 1: UTF-16LE -/ 2: UTF-16BE -/ 3: UTF-8 -/ -/ When _LFN_UNICODE is 0, this option has no effect. */ - - -#define _FS_RPATH 0 -/* This option configures relative path feature. -/ -/ 0: Disable relative path feature and remove related functions. -/ 1: Enable relative path feature. f_chdir() and f_chdrive() are available. -/ 2: f_getcwd() function is available in addition to 1. -/ -/ Note that directory items read via f_readdir() are affected by this option. */ - - -/*---------------------------------------------------------------------------/ -/ Drive/Volume Configurations -/---------------------------------------------------------------------------*/ - -#define _VOLUMES 1 -/* Number of volumes (logical drives) to be used. */ - - -#define _STR_VOLUME_ID 0 -#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3" -/* _STR_VOLUME_ID option switches string volume ID feature. -/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive -/ number in the path name. _VOLUME_STRS defines the drive ID strings for each -/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for -/ the drive ID strings are: A-Z and 0-9. */ - - -#define _MULTI_PARTITION 0 -/* This option switches multi-partition feature. By default (0), each logical drive -/ number is bound to the same physical drive number and only an FAT volume found on -/ the physical drive will be mounted. When multi-partition feature is enabled (1), -/ each logical drive number is bound to arbitrary physical drive and partition -/ listed in the VolToPart[]. Also f_fdisk() funciton will be available. */ - - -#define _MIN_SS 512 -#define _MAX_SS 512 -/* These options configure the range of sector size to be supported. (512, 1024, -/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and -/ harddisk. But a larger value may be required for on-board flash memory and some -/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured -/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the -/ disk_ioctl() function. */ - - -#define _USE_TRIM 0 -/* This option switches ATA-TRIM feature. (0:Disable or 1:Enable) -/ To enable Trim feature, also CTRL_TRIM command should be implemented to the -/ disk_ioctl() function. */ - - -#define _FS_NOFSINFO 0 -/* If you need to know correct free space on the FAT32 volume, set bit 0 of this -/ option, and f_getfree() function at first time after volume mount will force -/ a full FAT scan. Bit 1 controls the use of last allocated cluster number. -/ -/ bit0=0: Use free cluster count in the FSINFO if available. -/ bit0=1: Do not trust free cluster count in the FSINFO. -/ bit1=0: Use last allocated cluster number in the FSINFO if available. -/ bit1=1: Do not trust last allocated cluster number in the FSINFO. -*/ - - - -/*---------------------------------------------------------------------------/ -/ System Configurations -/---------------------------------------------------------------------------*/ - -#define _FS_NORTC 1 -#define _NORTC_MON 2 -#define _NORTC_MDAY 1 -#define _NORTC_YEAR 2015 -/* The _FS_NORTC option switches timestamp feature. If the system does not have -/ an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable -/ the timestamp feature. All objects modified by FatFs will have a fixed timestamp -/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR. -/ When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need -/ to be added to the project to read current time form RTC. _NORTC_MON, -/ _NORTC_MDAY and _NORTC_YEAR have no effect. -/ These options have no effect at read-only configuration (_FS_READONLY == 1). */ - - -#define _FS_LOCK 0 -/* The _FS_LOCK option switches file lock feature to control duplicated file open -/ and illegal operation to open objects. This option must be 0 when _FS_READONLY -/ is 1. -/ -/ 0: Disable file lock feature. To avoid volume corruption, application program -/ should avoid illegal open, remove and rename to the open objects. -/ >0: Enable file lock feature. The value defines how many files/sub-directories -/ can be opened simultaneously under file lock control. Note that the file -/ lock feature is independent of re-entrancy. */ - - -#define _FS_REENTRANT 0 -#define _FS_TIMEOUT 1000 -#define _SYNC_t HANDLE -/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs -/ module itself. Note that regardless of this option, file access to different -/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs() -/ and f_fdisk() function, are always not re-entrant. Only file/directory access -/ to the same volume is under control of this feature. -/ -/ 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect. -/ 1: Enable re-entrancy. Also user provided synchronization handlers, -/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() -/ function, must be added to the project. Samples are available in -/ option/syscall.c. -/ -/ The _FS_TIMEOUT defines timeout period in unit of time tick. -/ The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, -/ SemaphoreHandle_t and etc.. */ - - -#define _WORD_ACCESS 0 -/* The _WORD_ACCESS option is an only platform dependent option. It defines -/ which access method is used to the word data on the FAT volume. -/ -/ 0: Byte-by-byte access. Always compatible with all platforms. -/ 1: Word access. Do not choose this unless under both the following conditions. -/ -/ * Address misaligned memory access is always allowed to ALL instructions. -/ * Byte order on the memory is little-endian. -/ -/ If it is the case, _WORD_ACCESS can also be set to 1 to reduce code size. -/ Following table shows allowable settings of some processor types. -/ -/ ARM7TDMI 0 ColdFire 0 V850E 0 -/ Cortex-M3 0 Z80 0/1 V850ES 0/1 -/ Cortex-M0 0 x86 0/1 TLCS-870 0/1 -/ AVR 0/1 RX600(LE) 0/1 TLCS-900 0/1 -/ AVR32 0 RL78 0 R32C 0 -/ PIC18 0/1 SH-2 0 M16C 0/1 -/ PIC24 0 H8S 0 MSP430 0 -/ PIC32 0 H8/300H 0 8051 0/1 -*/ - diff --git a/Sming/Arch/Esp8266/Components/fatfs/integer.h b/Sming/Arch/Esp8266/Components/fatfs/integer.h deleted file mode 100644 index 16ad408233..0000000000 --- a/Sming/Arch/Esp8266/Components/fatfs/integer.h +++ /dev/null @@ -1,37 +0,0 @@ -/*-------------------------------------------*/ -/* Integer type definitions for FatFs module */ -/*-------------------------------------------*/ - -#ifndef _INTEGER -#define _INTEGER - -#ifdef _WIN32 /* FatFs development platform */ - -#include -#include - -#else /* Embedded platform */ - -/* These types must be 16-bit, 32-bit or larger integer */ -typedef int INT; -typedef unsigned int UINT; - -/* These types must be 8-bit integer */ -typedef char CHAR; -typedef unsigned char UCHAR; -typedef unsigned char BYTE; - -/* These types must be 16-bit integer */ -typedef short SHORT; -typedef unsigned short USHORT; -typedef unsigned short WORD; -typedef unsigned short WCHAR; - -/* These types must be 32-bit integer */ -typedef long LONG; -typedef unsigned long ULONG; -typedef unsigned long DWORD; - -#endif - -#endif diff --git a/Sming/Arch/Esp8266/Components/sming-arch/component.mk b/Sming/Arch/Esp8266/Components/sming-arch/component.mk index ba606f85aa..22b98ada28 100644 --- a/Sming/Arch/Esp8266/Components/sming-arch/component.mk +++ b/Sming/Arch/Esp8266/Components/sming-arch/component.mk @@ -17,7 +17,6 @@ COMPONENT_DEPENDS := \ esp8266 \ driver \ esptool \ - fatfs \ gdbstub \ spi_flash diff --git a/Sming/Arch/Esp8266/build.mk b/Sming/Arch/Esp8266/build.mk index 232658a06e..8dbd3fe7bb 100644 --- a/Sming/Arch/Esp8266/build.mk +++ b/Sming/Arch/Esp8266/build.mk @@ -36,6 +36,7 @@ AR := $(TOOLSPEC)ar LD := $(TOOLSPEC)gcc OBJCOPY := $(TOOLSPEC)objcopy OBJDUMP := $(TOOLSPEC)objdump +NM := $(TOOLSPEC)nm GDB := $(TOOLSPEC)gdb GCC_UPGRADE_URL := https://sming.readthedocs.io/en/latest/arch/esp8266/getting-started/eqt.html diff --git a/Sming/Arch/Host/Components/fatfs/README.rst b/Sming/Arch/Host/Components/fatfs/README.rst deleted file mode 100644 index e53896fe5f..0000000000 --- a/Sming/Arch/Host/Components/fatfs/README.rst +++ /dev/null @@ -1,4 +0,0 @@ -fatfs -===== - -This Component is required to allow some samples to compile for the Host, however it doesn't provide any functionality at present. diff --git a/Sming/Arch/Host/build.mk b/Sming/Arch/Host/build.mk index 0f98412d23..ce883373fe 100644 --- a/Sming/Arch/Host/build.mk +++ b/Sming/Arch/Host/build.mk @@ -17,6 +17,7 @@ CC := $(TOOLSPEC)gcc CXX := $(TOOLSPEC)g++ AR := $(TOOLSPEC)ar LD := $(TOOLSPEC)g++ +NM := $(TOOLSPEC)nm OBJCOPY := $(TOOLSPEC)objcopy OBJDUMP := $(TOOLSPEC)objdump GDB := $(TOOLSPEC)gdb diff --git a/Sming/Arch/Rp2040/Components/driver/Kconfig b/Sming/Arch/Rp2040/Components/driver/Kconfig new file mode 100644 index 0000000000..566932a5af --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/Kconfig @@ -0,0 +1,22 @@ +menu "Drivers" + config USE_US_TIMER + bool "Enable microsecond precision for software timers (Timer2)" + help + The following functions depend on Timer2: + - NOW() return value, the Timer2 tick count + - Software timers + - System time + + Software timers are driven by Timer2, which by default uses a /256 prescale + providing a resolution of 3.2us and a range of 1' 54". + + Enabling this setting increases the resolution to 200ns but reduces the maximum + software timer to 7" 9.5s. + + config ENABLE_CUSTOM_PWM + bool "Use the *New PWM* driver" + default y + help + New PWM is a drop-in replacement for the version provided in the Espressif SDK. + +endmenu diff --git a/Sming/Arch/Rp2040/Components/driver/README.rst b/Sming/Arch/Rp2040/Components/driver/README.rst new file mode 100644 index 0000000000..720bb0c5d5 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/README.rst @@ -0,0 +1,10 @@ +RP2040 Drivers +============== + +Provides low-level peripheral drivers. + +.. toctree:: + :glob: + :maxdepth: 1 + + * diff --git a/Sming/Arch/Rp2040/Components/driver/component.mk b/Sming/Arch/Rp2040/Components/driver/component.mk new file mode 100644 index 0000000000..42cf5d38a5 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/component.mk @@ -0,0 +1,8 @@ +COMPONENT_DEPENDS := rp2040 arch_driver + +COMPONENT_SRCDIRS := . +COMPONENT_INCDIRS := include + +COMPONENT_DOXYGEN_INPUT := include/driver + +COMPONENT_RELINK_VARS := PICO_BOARD diff --git a/Sming/Arch/Rp2040/Components/driver/gpio.rst b/Sming/Arch/Rp2040/Components/driver/gpio.rst new file mode 100644 index 0000000000..b1193c2738 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/gpio.rst @@ -0,0 +1,8 @@ +GPIO: General-Purpose I/O +========================= + +SDK definitions for GPIO. + +.. doxygengroup:: gpio_driver + :content-only: + :members: diff --git a/Sming/Arch/Rp2040/Components/driver/hw_timer.cpp b/Sming/Arch/Rp2040/Components/driver/hw_timer.cpp new file mode 100644 index 0000000000..aa1dad346f --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/hw_timer.cpp @@ -0,0 +1,63 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * hw_timer.cpp + * + ****/ + +#include +#include +#include + +hw_timer_private_t hw_timer_private; + +namespace +{ +void IRAM_ATTR timer1_isr() +{ + hw_clear_bits(&timer_hw->intr, BIT(0)); + auto& p = hw_timer_private; + if(p.timer1_callback != nullptr) { + p.timer1_callback(p.timer1_arg); + } + if(p.timer1_autoload) { + timer_hw->alarm[0] += p.timer1_ticks; + } +} + +} // namespace + +void hw_timer1_attach_interrupt(hw_timer_source_type_t source_type, hw_timer_callback_t callback, void* arg) +{ + (void)source_type; + auto& p = hw_timer_private; + irq_set_enabled(TIMER_IRQ_0, false); + p.timer1_callback = callback; + p.timer1_arg = arg; + irq_set_exclusive_handler(TIMER_IRQ_0, timer1_isr); + hw_set_bits(&timer_hw->inte, BIT(0)); + irq_set_enabled(TIMER_IRQ_0, true); +} + +void hw_timer1_detach_interrupt() +{ + hw_clear_bits(&timer_hw->inte, BIT(0)); + irq_set_enabled(TIMER_IRQ_0, false); + irq_remove_handler(TIMER_IRQ_0, timer1_isr); +} + +void IRAM_ATTR hw_timer1_enable(hw_timer_clkdiv_t div, hw_timer_intr_type_t intr_type, bool auto_load) +{ + (void)intr_type; + auto& p = hw_timer_private; + p.timer1_clkdiv = div; + p.timer1_autoload = auto_load; +} + +void hw_timer_init() +{ + // hardware_alarm_claim(0); +} diff --git a/Sming/Arch/Rp2040/Components/driver/hw_timer.rst b/Sming/Arch/Rp2040/Components/driver/hw_timer.rst new file mode 100644 index 0000000000..a496f7c24c --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/hw_timer.rst @@ -0,0 +1,12 @@ +hw_timer: Hardware Timers +========================= + +Driver for hardware timers. + + +API Documentation +----------------- + +.. doxygengroup:: hw_timer + :content-only: + :members: diff --git a/Sming/Arch/Rp2040/Components/driver/include/driver/gpio.h b/Sming/Arch/Rp2040/Components/driver/include/driver/gpio.h new file mode 100644 index 0000000000..70dbfef08a --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/include/driver/gpio.h @@ -0,0 +1,28 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * gpio.h + * + ****/ + +#pragma once + +#include + +/** + * @defgroup gpio_driver GPIO driver + * @ingroup drivers + * @{ + */ + +enum GPIO_INT_TYPE { + GPIO_PIN_INTR_DISABLE = 0, + GPIO_PIN_INTR_POSEDGE = GPIO_IRQ_EDGE_RISE, + GPIO_PIN_INTR_NEGEDGE = GPIO_IRQ_EDGE_FALL, + GPIO_PIN_INTR_ANYEDGE = GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, + GPIO_PIN_INTR_LOLEVEL = GPIO_IRQ_LEVEL_LOW, + GPIO_PIN_INTR_HILEVEL = GPIO_IRQ_LEVEL_HIGH, +}; diff --git a/Sming/Arch/Rp2040/Components/driver/include/driver/hw_timer.h b/Sming/Arch/Rp2040/Components/driver/include/driver/hw_timer.h new file mode 100644 index 0000000000..2d2270ec9e --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/include/driver/hw_timer.h @@ -0,0 +1,156 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * hw_timer.h + * + ****/ + +#pragma once + +#include +#include + +#define HW_TIMER_BASE_CLK 1000000U + +/** + * @defgroup hw_timer Hardware Timer Driver + * @ingroup drivers + * + * @note Timers are 64-bit but for compatibility limit to 31 bits + * @{ + */ + +/** + * @brief Fetch 32-bit microsecond count + * + * All timers reference from a single 64-bit counter. + * We use only the lower 32 bits here as it provides lowest latency + * and compatibility with existing API. + */ +__forceinline uint32_t IRAM_ATTR hw_timer_ticks() +{ + return timer_hw->timerawl; +} + +/************************************* + * + * Timer1 (countdown alarm) + * + *************************************/ + +/** + * @brief Maximum timer interval in ticks + */ +#define MAX_HW_TIMER1_INTERVAL 0x7fffffff + +/** + * @brief Minimum hardware interval in microseconds + * @note Attempting to use repetitive interrupts below this level can lead + * to system instabliity and lockups, due to the software overhead in servicing + * the interrupts. + */ +#define MIN_HW_TIMER1_INTERVAL_US 50U + +typedef void (*hw_timer_callback_t)(void* arg); + +typedef enum { + TIMER_CLKDIV_1, + TIMER_CLKDIV_16, + TIMER_CLKDIV_256, +} hw_timer_clkdiv_t; + +typedef enum { + TIMER_EDGE_INT, // edge interrupt + TIMER_LEVEL_INT, // level interrupt +} hw_timer_intr_type_t; + +typedef enum { + TIMER_FRC1_SOURCE, + TIMER_NMI_SOURCE, +} hw_timer_source_type_t; + +// Internal data +struct hw_timer_private_t { + hw_timer_clkdiv_t timer1_clkdiv; + hw_timer_callback_t timer1_callback; + uint32_t timer1_ticks; + bool timer1_autoload; + void* timer1_arg; +}; + +extern hw_timer_private_t hw_timer_private; + +/** + * @brief Attach an interrupt for the timer + * @param source_type + * @param callback Callback function invoked via timer interrupt + * @param arg Passed to callback function + */ +void IRAM_ATTR hw_timer1_attach_interrupt(hw_timer_source_type_t source_type, hw_timer_callback_t callback, void* arg); + +/** + * @brief Detach interrupt from the timer + */ +void hw_timer1_detach_interrupt(); + +/** + * @brief Enable the timer + * @param div + * @param intr_type + * @param auto_load + */ +void IRAM_ATTR hw_timer1_enable(hw_timer_clkdiv_t div, hw_timer_intr_type_t intr_type, bool auto_load); + +/** + * @brief Set the timer interval and arm + * @param ticks + */ +__forceinline void IRAM_ATTR hw_timer1_write(uint32_t ticks) +{ + ticks <<= hw_timer_private.timer1_clkdiv; + hw_timer_private.timer1_ticks = ticks; + timer_hw->alarm[0] = hw_timer_ticks() + ticks; +} + +/** + * @brief Disable the timer + */ +__forceinline void IRAM_ATTR hw_timer1_disable() +{ + timer_hw->armed = BIT(0); +} + +/** + * @brief Get timer1 count + * @retval uint32_t Current count value, counts from initial value down to 0 + */ +__forceinline uint32_t hw_timer1_read() +{ + int time = hw_timer_ticks() - timer_hw->alarm[0]; + return (time > 0) ? (time >> hw_timer_private.timer1_clkdiv) : 0; +} + +/************************************* + * + * FRC2 timer + * + * This is a 32-bit count-up timer. + * Used for software timers - see os_timer.h + * + *************************************/ + +constexpr uint32_t HW_TIMER2_CLK = HW_TIMER_BASE_CLK; + +/** + * @brief Read current timer2 value + * @retval uint32_t + */ +__forceinline uint32_t hw_timer2_read() +{ + return hw_timer_ticks(); +} + +/** @} */ diff --git a/Sming/Arch/Rp2040/Components/driver/include/driver/os_timer.h b/Sming/Arch/Rp2040/Components/driver/include/driver/os_timer.h new file mode 100644 index 0000000000..d634865d0a --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/include/driver/os_timer.h @@ -0,0 +1,57 @@ +/* + * This implementation mimics the behaviour of the ESP8266 Non-OS SDK timers, + * using Timer2 as the reference (which is _not_ in microseconds!) + * + * The ESP32 IDF contains more sophisticated timer implementations, but also + * this same API which it refers to as the 'legacy' timer API. + */ + +#pragma once + +#include + +// Disarmed +#define OS_TIMER_DEFAULT() \ + { \ + .timer_next = (os_timer_t*)-1, \ + } + +typedef void os_timer_func_t(void* timer_arg); + +/** + * @brief This is the structure used by the Espressif timer API + * @note This is used as an element in a linked list + * The Espressif implementation orders the list according to next expiry time. + * os_timer_setfn and os_timer_disarm set timer_next to -1 + * When expired, timer_next is 0 + */ +struct os_timer_t { + /// If disarmed, set to -1, otherwise points to the next queued timer (or NULL if last in the list) + struct os_timer_t* timer_next; + /// Set to the next Timer2 count value when the timer will expire + uint32_t timer_expire; + /// 0 if this is a one-shot timer, otherwise defines the interval in Timer2 ticks + uint32_t timer_period; + /// User-provided callback function pointer + os_timer_func_t* timer_func; + /// Argument passed to the callback function + void* timer_arg; +}; + +void os_timer_arm_ticks(os_timer_t* ptimer, uint32_t ticks, bool repeat_flag); + +void os_timer_arm(os_timer_t* ptimer, uint32_t time, bool repeat_flag); +void os_timer_arm_us(os_timer_t* ptimer, uint32_t time, bool repeat_flag); + +void os_timer_disarm(os_timer_t* ptimer); +void os_timer_setfn(os_timer_t* ptimer, os_timer_func_t* pfunction, void* parg); + +static inline uint64_t os_timer_expire(const os_timer_t* ptimer) +{ + if(ptimer == nullptr || int(ptimer->timer_next) == -1) { + return 0; + } + return ptimer->timer_expire; +} + +void os_timer_done(os_timer_t* ptimer); diff --git a/Sming/Arch/Rp2040/Components/driver/include/driver/pwm.h b/Sming/Arch/Rp2040/Components/driver/include/driver/pwm.h new file mode 100644 index 0000000000..8eb9f2560e --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/include/driver/pwm.h @@ -0,0 +1,88 @@ +#pragma once + +#if defined(__cplusplus) +extern "C" { +#endif + +// #include + +#define PWM_CHANNEL_NUM_MAX 16 + +/** + * @defgroup pwm_driver PWM driver + * @ingroup drivers + * @{ + */ + +/** + * @fn void pwm_init(uint32 period, uint32 *duty,uint32 pwm_channel_num,uint32 (*pin_info_list)[3]) + * @brief Initialize PWM function, including GPIO selection, period and duty cycle + * @param period PWM period + * @param duty duty cycle of each output + * @param pwm_channel_num PWM channel number + * @param pin_info_list Array containing an entry for each channel giving + * @note This API can be called only once. + * + * Example: + * + * uint32 io_info[][3] = { + * {PWM_0_OUT_IO_MUX, PWM_0_OUT_IO_FUNC, PWM_0_OUT_IO_NUM}, + * {PWM_1_OUT_IO_MUX, PWM_1_OUT_IO_FUNC, PWM_1_OUT_IO_NUM}, + * {PWM_2_OUT_IO_MUX, PWM_2_OUT_IO_FUNC, PWM_2_OUT_IO_NUM} + * }; + * + * pwm_init(light_param.pwm_period, light_param.pwm_duty, 3, io_info); + * + */ + +/** + * @fn void pwm_start(void) + * @brief Starts PWM + * + * This function needs to be called after PWM configuration is changed. + */ + +/** + * @fn void pwm_set_duty(uint32 duty, uint8 channel) + * @brief Sets duty cycle of a PWM output + * @param duty The time that high-level single will last, duty cycle will be (duty*45)/(period*1000) + * @param channel PWM channel, which depends on how many PWM channels are used + * + * Set the time that high-level signal will last. + * The range of duty depends on PWM period. Its maximum value of which can be Period * 1000 / 45. + * + * For example, for 1-KHz PWM, the duty range is 0 ~ 22222. + */ + +/** + * @fn uint32 pwm_get_duty(uint8 channel) + * @brief Get duty cycle of PWM output + * @param channel PWM channel, which depends on how many PWM channels are used + * @retval uint32 Duty cycle of PWM output + * + * Duty cycle will be (duty*45) / (period*1000). + */ + +/** + * @fn void pwm_set_period(uint32 period) + * @brief Set PWM period + * @param period PWM period in us. For example, 1-KHz PWM period = 1000us. + */ + +/** + * @fn uint32 pwm_get_period(void) + * @brief Get PWM period + * @retval uint32 Return PWM period in us. + */ + +/** + * @fn uint32 get_pwm_version(void) + * @brief Get version information of PWM + * @retval uint32 PWM version + */ + +/** @} */ + +#if defined(__cplusplus) +} +#endif diff --git a/Sming/Arch/Rp2040/Components/driver/include/driver/uart.h b/Sming/Arch/Rp2040/Components/driver/include/driver/uart.h new file mode 100644 index 0000000000..a789636242 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/include/driver/uart.h @@ -0,0 +1,7 @@ +#pragma once + +#define UART0 0 +#define UART1 1 +#define UART_COUNT 2 ///< Number of UARTs on the system, virtual or otherwise + +#include_next diff --git a/Sming/Arch/Rp2040/Components/driver/os_timer.cpp b/Sming/Arch/Rp2040/Components/driver/os_timer.cpp new file mode 100644 index 0000000000..f1d4d31e8b --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/os_timer.cpp @@ -0,0 +1,199 @@ +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_OSTIMER_DEBUG +#define debug_tmr(fmt, ...) m_printf("%u [TMR] " fmt "\r\n", hw_timer2_read(), ##__VA_ARGS__) + +#else +#define debug_tmr(fmt, ...) \ + do { \ + } while(0) +#endif + +namespace +{ +os_timer_t* timer_list; + +/** + * @brief Protect certain code blocks which are callable from interrupt context + */ +class CriticalLock +{ +public: + CriticalLock() + { + level = save_and_disable_interrupts(); + } + + ~CriticalLock() + { + restore_interrupts(level); + } + +private: + uint32_t level; +}; + +static void IRAM_ATTR timer_insert(uint32_t expire, os_timer_t* ptimer) +{ + debug_tmr("insert %p %u", ptimer, expire); + + os_timer_t* t_prev = nullptr; + auto t = timer_list; + while(t != nullptr) { + if(int(t->timer_expire - expire) > 0) { + break; + } + t_prev = t; + t = t->timer_next; + } + if(t_prev == nullptr) { + timer_list = ptimer; + } else { + t_prev->timer_next = ptimer; + } + ptimer->timer_next = t; + ptimer->timer_expire = expire; + +#ifdef ENABLE_OSTIMER_DEBUG + t = timer_list; + while(t != nullptr) { + debug_tmr("%p @ %u", t, t->timer_expire); + t = t->timer_next; + } +#endif +} + +// Schedule next timer, if there is one +void IRAM_ATTR timer_schedule() +{ + // Schedule next timer, if there is one + if(timer_list == nullptr) { + debug_tmr("cancel"); + // Cancel hardware timer + timer_hw->armed = BIT(1); + return; + } + + constexpr int TIMER2_MIN_US{50}; + auto now = hw_timer2_read(); + if(int(timer_list->timer_expire - now) < TIMER2_MIN_US) { + timer_hw->alarm[1] = now + TIMER2_MIN_US; + } else { + timer_hw->alarm[1] = timer_list->timer_expire; + } +} + +os_timer_t* find_expired_timer() +{ + if(timer_list == nullptr) { + return nullptr; + } + + // Using Timer2 hardware to schedule software timers + if(timer_hw->armed & BIT(1)) { + return nullptr; + } + + CriticalLock lock; + + // Pop timer from head of queue + auto t = timer_list; + timer_list = t->timer_next; + t->timer_next = reinterpret_cast(-1); + + debug_tmr("fired %p", t); + + // Repeating timer? + if(t->timer_period != 0) { + timer_insert(t->timer_expire + t->timer_period, t); + } + + timer_schedule(); + + return t; +} + +} // namespace + +void IRAM_ATTR os_timer_arm_ticks(os_timer_t* ptimer, uint32_t ticks, bool repeat_flag) +{ + if(ptimer == nullptr) { + return; + } + + os_timer_disarm(ptimer); + ptimer->timer_period = repeat_flag ? ticks : 0; + + CriticalLock lock; + timer_insert(hw_timer2_read() + ticks, ptimer); + timer_schedule(); +} + +void IRAM_ATTR os_timer_arm(struct os_timer_t* ptimer, uint32_t time, bool repeat_flag) +{ + using R = std::ratio; + auto ticks = muldiv(time); + os_timer_arm_ticks(ptimer, ticks, repeat_flag); +} + +void IRAM_ATTR os_timer_arm_us(struct os_timer_t* ptimer, uint32_t time, bool repeat_flag) +{ + using R = std::ratio; + auto ticks = muldiv(time); + os_timer_arm_ticks(ptimer, ticks, repeat_flag); +} + +void IRAM_ATTR os_timer_disarm(struct os_timer_t* ptimer) +{ + if(ptimer == nullptr || timer_list == nullptr || int(ptimer->timer_next) == -1) { + return; // not armed + } + + CriticalLock lock; + + // Remove timer from list + if(ptimer == timer_list) { + timer_list = ptimer->timer_next; + timer_schedule(); + } else { + for(auto t = timer_list; t->timer_next != nullptr; t = t->timer_next) { + if(t->timer_next == ptimer) { + t->timer_next = ptimer->timer_next; + break; + } + } + } + ptimer->timer_next = reinterpret_cast(-1); +} + +void os_timer_setfn(struct os_timer_t* ptimer, os_timer_func_t* pfunction, void* parg) +{ + if(ptimer != nullptr) { + ptimer->timer_func = pfunction; + ptimer->timer_arg = parg; + ptimer->timer_next = reinterpret_cast(-1); + } +} + +void os_timer_done(struct os_timer_t* ptimer) +{ + os_timer_disarm(ptimer); +} + +void system_init_timers() +{ + // hardware_alarm_claim(1); +} + +void system_service_timers() +{ + auto t = find_expired_timer(); + if(t != nullptr && t->timer_func != nullptr) { + t->timer_func(t->timer_arg); + } +} diff --git a/Sming/Arch/Rp2040/Components/driver/os_timer.rst b/Sming/Arch/Rp2040/Components/driver/os_timer.rst new file mode 100644 index 0000000000..cdce8b181c --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/os_timer.rst @@ -0,0 +1,8 @@ +OS Timer +======== + +Driver for software timer queues + +.. doxygengroup:: os_timer + :content-only: + :members: diff --git a/Sming/Arch/Rp2040/Components/driver/pwm.rst b/Sming/Arch/Rp2040/Components/driver/pwm.rst new file mode 100644 index 0000000000..b6d9372e8b --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/pwm.rst @@ -0,0 +1,9 @@ +PWM: Pulse-Width Modulation +=========================== + +API Documentation +----------------- + +.. doxygengroup:: pwm_driver + :content-only: + :members: diff --git a/Sming/Arch/Rp2040/Components/driver/uart.cpp b/Sming/Arch/Rp2040/Components/driver/uart.cpp new file mode 100644 index 0000000000..c4a270354c --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/uart.cpp @@ -0,0 +1,857 @@ +/* + uart.cpp - RP2040 UART driver + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +using uart_dev_t = uart_hw_t; + +int s_uart_debug_nr = UART_NO; + +// Keep track of interrupt enable state for each UART +uint8_t isrMask; + +struct smg_uart_pins_t { + uint8_t tx; + uint8_t rx; +}; + +#define UART0_PIN_DEFAULT PICO_DEFAULT_UART_TX_PIN, PICO_DEFAULT_UART_RX_PIN +#define UART1_PIN_DEFAULT 4, 5 + +constexpr smg_uart_pins_t defaultPins[UART_COUNT] = { + {UART0_PIN_DEFAULT}, + {UART1_PIN_DEFAULT}, +}; + +uart_dev_t* IRAM_ATTR getDevice(uint8_t uart_nr) +{ + if(uart_nr == 0) { + return uart0_hw; + } + if(uart_nr == 1) { + return uart1_hw; + } + assert(false); + return nullptr; +} + +// Keep a reference to all created UARTS +struct smg_uart_instance_t { + smg_uart_t* uart; + smg_uart_notify_callback_t callback; +}; + +smg_uart_instance_t uartInstances[UART_COUNT]; + +// Return true if transmit FIFO is full +__forceinline bool uart_txfifo_full(uart_dev_t* dev) +{ + return !!(dev->fr & UART_UARTFR_TXFF_BITS); +} + +// Return true if transmit FIFO is empty +__forceinline bool uart_txfifo_empty(uart_dev_t* dev) +{ + return !!(dev->fr & UART_UARTFR_TXFE_BITS); +} + +// Return true if receive FIFO is empty +__forceinline bool uart_rxfifo_empty(uart_dev_t* dev) +{ + return !!(dev->fr & UART_UARTFR_RXFE_BITS); +} + +/** @brief Invoke a port callback, if one has been registered + * @param uart + * @param code + */ +void notify(smg_uart_t* uart, smg_uart_notify_code_t code) +{ + auto callback = uartInstances[uart->uart_nr].callback; + if(callback != nullptr) { + callback(uart, code); + } +} + +__forceinline void uart_disable_isr(uint8_t nr) +{ + irq_set_enabled(UART0_IRQ + nr, false); +} + +__forceinline void uart_enable_isr(uint8_t nr) +{ + irq_set_enabled(UART0_IRQ + nr, true); +} + +bool realloc_buffer(SerialBuffer*& buffer, size_t new_size) +{ + if(buffer != nullptr) { + if(new_size == 0) { + delete buffer; + buffer = nullptr; + return true; + } + + return buffer->resize(new_size) == new_size; + } + + if(new_size == 0) { + return true; + } + + auto new_buf = new SerialBuffer; + if(new_buf != nullptr && new_buf->resize(new_size) == new_size) { + buffer = new_buf; + return true; + } + + delete new_buf; + return false; +} + +void IRAM_ATTR handleInterrupt(smg_uart_t* uart, uart_dev_t* dev) +{ + auto mis = dev->mis; + + // If status is clear there's no interrupt to service on this UART + if(mis == 0) { + return; + } + + // Value to be passed to callback + auto user_is = mis; + + // Deal with the event, unless we're in raw mode + if(!bitRead(uart->options, UART_OPT_CALLBACK_RAW)) { + // Rx FIFO full, timeout or receive overflow + auto rxfifo_full = mis & UART_UARTMIS_RXMIS_BITS; + auto rxfifo_tout = mis & UART_UARTMIS_RTMIS_BITS; + auto rxfifo_ovf = mis & UART_UARTMIS_OEMIS_BITS; + if(rxfifo_full || rxfifo_tout || rxfifo_ovf) { + bool read{false}; + + // Read as much data as possible from the RX FIFO into buffer + if(uart->rx_buffer != nullptr) { + size_t space = uart->rx_buffer->getFreeSpace(); + while(space != 0 && !uart_rxfifo_empty(dev)) { + uint8_t c = dev->dr; + uart->rx_buffer->writeChar(c); + --space; + read = true; + } + + // Don't call back until buffer is (almost) full + if(space > uart->rx_headroom) { + user_is &= ~UART_UARTMIS_RXMIS_BITS; + } + } + + /* + * If the FIFO is full and we didn't read any of the data then need to mask the interrupt out or it'll recur. + * The interrupt gets re-enabled by a call to uart_read() or uart_flush() + */ + if(rxfifo_ovf) { + hw_clear_bits(&dev->imsc, UART_UARTIMSC_OEIM_BITS); + } else if(read == 0) { + hw_set_bits(&dev->imsc, UART_UARTIMSC_RXIM_BITS | UART_UARTIMSC_RTIM_BITS); + } + } + + // Unless we replenish TX FIFO, disable after handling interrupt + auto txfifo_empty = mis & UART_UARTMIS_TXMIS_BITS; + if(txfifo_empty) { + // Dump as much data as we can from buffer into the TX FIFO + if(uart->tx_buffer != nullptr) { + size_t avail = uart->tx_buffer->available(); + while(avail-- && !uart_txfifo_full(dev)) { + uint8_t c = uart->tx_buffer->readChar(); + dev->dr = c; + } + } + + // If TX FIFO remains empty then we must disable TX FIFO EMPTY interrupt to stop it recurring. + if(uart_txfifo_empty(dev)) { + // The interrupt gets re-enabled by uart_write() + hw_clear_bits(&dev->imsc, UART_UARTIMSC_TXIM_BITS); + } else { + // We've topped up TX FIFO so defer callback until next time + user_is &= ~UART_UARTMIS_TXMIS_BITS; + } + } + } + + // Translate interrupt status flag bits into API values + uint32_t status{0}; + if(user_is & UART_UARTMIS_RXMIS_BITS) { + status |= UART_STATUS_RXFIFO_FULL; + } + if(user_is & UART_UARTMIS_RTMIS_BITS) { + status |= UART_STATUS_RXFIFO_TOUT; + } + if(user_is & UART_UARTMIS_OEMIS_BITS) { + status |= UART_STATUS_RXFIFO_OVF; + } + if(user_is & UART_UARTMIS_TXMIS_BITS) { + status |= UART_STATUS_TXFIFO_EMPTY; + } + if(user_is & UART_UARTMIS_BEMIS_BITS) { + status |= UART_STATUS_BRK_DET; + } + if(user_is & UART_UARTMIS_FEMIS_BITS) { + status |= UART_STATUS_FRM_ERR; + } + + // Keep a note of persistent flags - cleared via uart_get_status() + uart->status |= status; + + if(status != 0 && uart->callback != nullptr) { + uart->callback(uart, status); + } + + // Final step is to clear status flags + dev->icr = mis; +} + +void IRAM_ATTR uart0_isr() +{ + handleInterrupt(uartInstances[0].uart, uart0_hw); +} + +void IRAM_ATTR uart1_isr() +{ + handleInterrupt(uartInstances[1].uart, uart1_hw); +} + +} // namespace + +smg_uart_t* smg_uart_get_uart(uint8_t uart_nr) +{ + return (uart_nr < UART_COUNT) ? uartInstances[uart_nr].uart : nullptr; +} + +uint8_t smg_uart_disable_interrupts() +{ + if(isrMask & BIT(0)) { + irq_set_enabled(UART0_IRQ, false); + } + if(isrMask & BIT(1)) { + irq_set_enabled(UART1_IRQ, false); + } + return isrMask; +} + +void smg_uart_restore_interrupts() +{ + if(isrMask & BIT(0)) { + irq_set_enabled(UART0_IRQ, true); + } + if(isrMask & BIT(1)) { + irq_set_enabled(UART1_IRQ, true); + } +} + +bool smg_uart_set_notify(unsigned uart_nr, smg_uart_notify_callback_t callback) +{ + if(uart_nr >= UART_COUNT) { + return false; + } + + uartInstances[uart_nr].callback = callback; + return true; +} + +void smg_uart_set_callback(smg_uart_t* uart, smg_uart_callback_t callback, void* param) +{ + if(uart != nullptr) { + uart->callback = nullptr; // In case interrupt fires between setting param and callback + uart->param = param; + uart->callback = callback; + } +} + +size_t smg_uart_resize_rx_buffer(smg_uart_t* uart, size_t new_size) +{ + if(smg_uart_rx_enabled(uart)) { + uart_disable_isr(uart->uart_nr); + realloc_buffer(uart->rx_buffer, new_size); + uart_enable_isr(uart->uart_nr); + } + return smg_uart_rx_buffer_size(uart); +} + +size_t smg_uart_rx_buffer_size(smg_uart_t* uart) +{ + return uart != nullptr && uart->rx_buffer != nullptr ? uart->rx_buffer->getSize() : 0; +} + +size_t smg_uart_resize_tx_buffer(smg_uart_t* uart, size_t new_size) +{ + if(smg_uart_tx_enabled(uart)) { + uart_disable_isr(uart->uart_nr); + realloc_buffer(uart->tx_buffer, new_size); + uart_enable_isr(uart->uart_nr); + } + return smg_uart_tx_buffer_size(uart); +} + +size_t smg_uart_tx_buffer_size(smg_uart_t* uart) +{ + return uart != nullptr && uart->tx_buffer != nullptr ? uart->tx_buffer->getSize() : 0; +} + +int smg_uart_peek_char(smg_uart_t* uart) +{ + return uart != nullptr && uart->rx_buffer ? uart->rx_buffer->peekChar() : -1; +} + +int smg_uart_rx_find(smg_uart_t* uart, char c) +{ + if(uart == nullptr || uart->rx_buffer == nullptr) { + return -1; + } + + return uart->rx_buffer->find(c); +} + +int smg_uart_peek_last_char(smg_uart_t* uart) +{ + return uart != nullptr && uart->rx_buffer != nullptr ? uart->rx_buffer->peekLastChar() : -1; +} + +size_t smg_uart_read(smg_uart_t* uart, void* buffer, size_t size) +{ + if(!smg_uart_rx_enabled(uart) || buffer == nullptr || size == 0) { + return 0; + } + + notify(uart, UART_NOTIFY_BEFORE_READ); + + size_t read = 0; + + auto buf = static_cast(buffer); + + // First read data from RX buffer if in use + if(uart->rx_buffer != nullptr) { + while(read < size && !uart->rx_buffer->isEmpty()) + buf[read++] = uart->rx_buffer->readChar(); + } + + // Top up from hardware FIFO + auto dev = getDevice(uart->uart_nr); + while(read < size && !uart_rxfifo_empty(dev)) { + uint8_t c = dev->dr; + buf[read++] = c; + } + + // FIFO full may have been disabled if buffer overflowed, re-enabled it now + dev->icr = UART_UARTMIS_RXMIS_BITS | UART_UARTMIS_RTMIS_BITS | UART_UARTMIS_OEMIS_BITS; + hw_set_bits(&dev->imsc, UART_UARTIMSC_RXIM_BITS | UART_UARTIMSC_RTIM_BITS | UART_UARTIMSC_OEIM_BITS); + + return read; +} + +size_t smg_uart_rx_available(smg_uart_t* uart) +{ + return uart->rx_buffer ? uart->rx_buffer->available() : 0; +} + +void smg_uart_start_isr(smg_uart_t* uart) +{ + uint32_t int_ena{0}; + uint32_t fifo_level_select{0}; + + auto dev = getDevice(uart->uart_nr); + + if(smg_uart_rx_enabled(uart)) { + // Trigger at >= 7/8 full + fifo_level_select |= 5 << UART_UARTIFLS_RXIFLSEL_LSB; + + /* + * There is little benefit in generating interrupts on errors, instead these + * should be cleared at the start of a transaction and checked at the end. + * See uart_get_status(). + */ + int_ena |= UART_UARTIMSC_RXIM_BITS // rxfifo_full + | UART_UARTIMSC_RTIM_BITS // rxfifo_tout + | UART_UARTIMSC_OEIM_BITS // rxfifo_ovf + | UART_UARTIMSC_BEIM_BITS // brk_det + | UART_UARTIMSC_PEIM_BITS // parity error + | UART_UARTIMSC_FEIM_BITS // framing error + ; + } + + if(smg_uart_tx_enabled(uart)) { + /* + * We can interrupt when TX FIFO is empty; at 1Mbit that gives us 800 CPU + * cycles before the last character has actually gone over the wire. Even if + * a gap occurs it is unlike to cause any problems. It also makes the callback + * more useful, for example if using it for RS485 we'd then want to reverse + * transfer direction and begin waiting for a response. + */ + + // TX FIFO empty interrupt only gets enabled via uart_write function() + + // Trigger at <= 1/8 full + fifo_level_select |= 0 << UART_UARTIFLS_TXIFLSEL_LSB; + } + + dev->ifls = fifo_level_select; + + // Clear and enable required UART interrupts + dev->icr = UART_UARTICR_BITS; + dev->imsc = int_ena; + + // Enable interrupt handler + auto irq = UART0_IRQ + uart->uart_nr; + irq_set_exclusive_handler(irq, (uart->uart_nr == 0) ? uart0_isr : uart1_isr); + irq_set_enabled(irq, true); + bitSet(isrMask, uart->uart_nr); +} + +size_t smg_uart_write(smg_uart_t* uart, const void* buffer, size_t size) +{ + if(!smg_uart_tx_enabled(uart) || buffer == nullptr || size == 0) { + return 0; + } + + size_t written = 0; + + auto buf = static_cast(buffer); + + while(written < size) { + // If TX buffer not in use or it's empty then write directly to hardware FIFO + if(uart->tx_buffer == nullptr || uart->tx_buffer->isEmpty()) { + auto dev = getDevice(uart->uart_nr); + while(written < size && !uart_txfifo_full(dev)) { + dev->dr = buf[written++]; + } + + // Enable TX FIFO EMPTY interrupt + dev->icr = UART_UARTMIS_TXMIS_BITS; + hw_set_bits(&dev->imsc, UART_UARTIMSC_TXIM_BITS); + } + + // Write any remaining data into transmit buffer + if(uart->tx_buffer != nullptr) { + while(written < size && uart->tx_buffer->writeChar(buf[written])) { + ++written; + } + } + + notify(uart, UART_NOTIFY_AFTER_WRITE); + + if(!bitRead(uart->options, UART_OPT_TXWAIT)) { + break; + } + } + + return written; +} + +size_t smg_uart_tx_free(smg_uart_t* uart) +{ + return uart->tx_buffer ? uart->tx_buffer->getFreeSpace() : 0; +} + +void smg_uart_wait_tx_empty(smg_uart_t* uart) +{ + if(!smg_uart_tx_enabled(uart)) { + return; + } + + notify(uart, UART_NOTIFY_WAIT_TX); + + system_soft_wdt_feed(); + + if(uart->tx_buffer != nullptr) { + while(!uart->tx_buffer->isEmpty()) { + } + } + + auto dev = getDevice(uart->uart_nr); + while((dev->fr & UART_UARTFR_TXFE_BITS) != 0) { + } +} + +void smg_uart_set_break(smg_uart_t* uart, bool state) +{ + if(uart == nullptr) { + return; + } + + auto dev = getDevice(uart->uart_nr); + if(state) { + hw_set_bits(&dev->lcr_h, UART_UARTLCR_H_BRK_BITS); + } else { + hw_clear_bits(&dev->lcr_h, UART_UARTLCR_H_BRK_BITS); + } +} + +uint8_t smg_uart_get_status(smg_uart_t* uart) +{ + if(uart == nullptr) { + return 0; + } + + uart_disable_isr(uart->uart_nr); + auto dev = getDevice(uart->uart_nr); + // Get and clear break/overflow flags from uart + auto rsr = dev->rsr & (UART_UARTRSR_BE_BITS | UART_UARTRSR_OE_BITS | UART_UARTRSR_PE_BITS); + dev->rsr = rsr; // Clear errors + // Read raw status directly from uart, masking out non-error bits + auto raw = dev->ris & (UART_UARTRIS_OERIS_BITS | UART_UARTRIS_BERIS_BITS | UART_UARTRIS_FERIS_BITS); + dev->icr = raw; // clear errors + // Fetch latched status and clear it + auto status = uart->status; + uart->status = 0; + uart_enable_isr(uart->uart_nr); + + // Now translate hardware flags into API values + if(rsr & UART_UARTRSR_BE_BITS) { + status |= UART_STATUS_BRK_DET; + } + if(rsr & UART_UARTRSR_OE_BITS) { + status |= UART_STATUS_RXFIFO_OVF; + } + if(rsr & UART_UARTRSR_PE_BITS) { + status |= UART_STATUS_PARITY_ERR; + } + if(raw & UART_UARTRIS_OERIS_BITS) { + status |= UART_STATUS_RXFIFO_OVF; + } + if(raw & UART_UARTRIS_BERIS_BITS) { + status |= UART_STATUS_BRK_DET; + } + if(raw & UART_UARTRIS_FERIS_BITS) { + status |= UART_STATUS_FRM_ERR; + } + + return status; +} + +void smg_uart_flush(smg_uart_t* uart, smg_uart_mode_t mode) +{ + if(uart == nullptr) { + return; + } + + bool flushRx = mode == UART_FULL || mode == UART_RX_ONLY; + bool flushTx = mode == UART_FULL || mode == UART_TX_ONLY; + + uart_disable_isr(uart->uart_nr); + if(flushRx && uart->rx_buffer != nullptr) { + uart->rx_buffer->clear(); + } + + if(flushTx && uart->tx_buffer != nullptr) { + uart->tx_buffer->clear(); + } + + auto dev = getDevice(uart->uart_nr); + + if(flushTx) { + // Prevent TX FIFO EMPTY interrupts - don't need them until uart_write is called again + hw_clear_bits(&dev->imsc, UART_UARTIMSC_TXIM_BITS); + while(!uart_txfifo_empty(dev)) { + // + } + } + + // If receive overflow occurred then these interrupts will be masked + if(flushRx) { + while(!uart_rxfifo_empty(dev)) { + uint8_t c = dev->dr; + (void)c; + } + + // Clear all receive flag bits + dev->icr = UART_UARTICR_RXIC_BITS // rxfifo_full + | UART_UARTICR_RTIC_BITS // rxfifo_tout + | UART_UARTICR_OEIC_BITS // rxfifo_ovf + ; + + hw_set_bits(&dev->imsc, UART_UARTIMSC_RXIM_BITS // rxfifo_full + | UART_UARTIMSC_RTIM_BITS // rxfifo_tout + | UART_UARTIMSC_OEIM_BITS // rxfifo_ovf + ); + } + + uart_enable_isr(uart->uart_nr); +} + +uint32_t smg_uart_set_baudrate_reg(int uart_nr, uint32_t baud_rate) +{ + if(baud_rate == 0) { + return 0; + } + + auto clock_hz = clock_get_hz(clk_peri); + uint32_t baud_rate_div = 8 * clock_hz / baud_rate; + uint32_t baud_ibrd = baud_rate_div >> 7; + uint32_t baud_fbrd{0}; + + if(baud_ibrd == 0) { + baud_ibrd = 1; + } else if(baud_ibrd >= 0xffff) { + baud_ibrd = 0xffff; + } else { + baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2; + } + + // Load PL011's baud divisor registers + auto dev = getDevice(uart_nr); + dev->ibrd = baud_ibrd; + dev->fbrd = baud_fbrd; + + // PL011 needs a (dummy) line control register write to latch in the + // divisors. We don't want to actually change LCR contents here. + hw_set_bits(&dev->lcr_h, 0); + + // Return the actual baud rate in use + return 4 * clock_hz / ((64 * baud_ibrd) + baud_fbrd); +} + +uint32_t smg_uart_set_baudrate(smg_uart_t* uart, uint32_t baud_rate) +{ + baud_rate = smg_uart_set_baudrate_reg(uart->uart_nr, baud_rate); + // Store the actual baud rate in use + uart->baud_rate = baud_rate; + return baud_rate; +} + +uint32_t smg_uart_get_baudrate(smg_uart_t* uart) +{ + return uart ? uart->baud_rate : 0; +} + +smg_uart_t* smg_uart_init_ex(const smg_uart_config_t& cfg) +{ + // Already initialised? + if(cfg.uart_nr >= UART_COUNT || uartInstances[cfg.uart_nr].uart != nullptr) { + return nullptr; + } + + auto uart = new smg_uart_t{}; + if(uart == nullptr) { + return nullptr; + } + + uart->uart_nr = cfg.uart_nr; + uart->mode = cfg.mode; + uart->options = cfg.options; + uart->tx_pin = uart->rx_pin = UART_PIN_DEFAULT; + uart->rx_headroom = 16; + + int tx_pin = cfg.tx_pin; + int rx_pin = cfg.rx_pin; + + auto rxBufferSize = cfg.rx_size; + auto txBufferSize = cfg.tx_size; + + if(smg_uart_rx_enabled(uart)) { + if(!realloc_buffer(uart->rx_buffer, rxBufferSize)) { + delete uart; + return nullptr; + } + rx_pin = (cfg.rx_pin == UART_PIN_DEFAULT) ? defaultPins[cfg.uart_nr].rx : cfg.rx_pin; + } else { + rx_pin = UART_PIN_NO_CHANGE; + } + + if(smg_uart_tx_enabled(uart)) { + if(!realloc_buffer(uart->tx_buffer, txBufferSize)) { + delete uart->rx_buffer; + delete uart; + return nullptr; + } + tx_pin = (tx_pin == UART_PIN_DEFAULT) ? defaultPins[cfg.uart_nr].tx : cfg.tx_pin; + } else { + tx_pin = UART_PIN_NO_CHANGE; + } + + // OK, buffers allocated so setup hardware + auto reset_bits = (uart->uart_nr == 1) ? RESETS_RESET_UART1_BITS : RESETS_RESET_UART0_BITS; + reset_block(reset_bits); + unreset_block_wait(reset_bits); + + smg_uart_detach(cfg.uart_nr); + smg_uart_set_baudrate(uart, cfg.baudrate); + + auto dev = getDevice(cfg.uart_nr); + + // Setup line control register + smg_uart_config_format_t fmt; + fmt.val = cfg.config; + uint32_t lcr{0}; + lcr |= fmt.bits << UART_UARTLCR_H_WLEN_LSB; // data bits + if(fmt.stop_bits != UART_NB_STOP_BIT_1) { // stop bits + lcr |= UART_UARTLCR_H_STP2_BITS; + } + lcr |= fmt.parity << UART_UARTLCR_H_PEN_LSB; // parity + lcr |= UART_UARTLCR_H_FEN_BITS; // Enable FIFOs + dev->lcr_h = lcr; + + // Enable the UART + if(uart->mode == UART_TX_ONLY) { + dev->cr = UART_UARTCR_UARTEN_BITS | UART_UARTCR_TXE_BITS; + } else if(uart->mode == UART_RX_ONLY) { + dev->cr = UART_UARTCR_UARTEN_BITS | UART_UARTCR_RXE_BITS; + } else { + dev->cr = UART_UARTCR_UARTEN_BITS | UART_UARTCR_TXE_BITS | UART_UARTCR_RXE_BITS; + } + + // Always enable DREQ signals -- no harm in this if DMA is not listening + dev->dmacr = UART_UARTDMACR_TXDMAE_BITS | UART_UARTDMACR_RXDMAE_BITS; + + smg_uart_set_pins(uart, tx_pin, rx_pin); + + smg_uart_flush(uart); + uartInstances[cfg.uart_nr].uart = uart; + smg_uart_start_isr(uart); + + notify(uart, UART_NOTIFY_AFTER_OPEN); + + return uart; +} + +void smg_uart_uninit(smg_uart_t* uart) +{ + if(uart == nullptr) { + return; + } + + notify(uart, UART_NOTIFY_BEFORE_CLOSE); + + smg_uart_stop_isr(uart); + // If debug output being sent to this UART, disable it + if(uart->uart_nr == s_uart_debug_nr) { + smg_uart_set_debug(UART_NO); + } + + auto reset_bits = (uart->uart_nr == 1) ? RESETS_RESET_UART1_BITS : RESETS_RESET_UART0_BITS; + reset_block(reset_bits); + + uartInstances[uart->uart_nr].uart = nullptr; + delete uart->rx_buffer; + delete uart->tx_buffer; + delete uart; +} + +smg_uart_t* smg_uart_init(uint8_t uart_nr, uint32_t baudrate, uint32_t config, smg_uart_mode_t mode, uint8_t tx_pin, + size_t rx_size, size_t tx_size) +{ + smg_uart_config_t cfg = {.uart_nr = uart_nr, + .tx_pin = tx_pin, + .rx_pin = UART_PIN_DEFAULT, + .mode = mode, + .options = _BV(UART_OPT_TXWAIT), + .baudrate = baudrate, + .config = config, + .rx_size = rx_size, + .tx_size = tx_size}; + return smg_uart_init_ex(cfg); +} + +void smg_uart_swap(smg_uart_t* uart, int tx_pin) +{ + // Not implemented +} + +bool smg_uart_set_tx(smg_uart_t* uart, int tx_pin) +{ + return uart == nullptr ? false : smg_uart_set_pins(uart, tx_pin, -1); +} + +bool smg_uart_set_pins(smg_uart_t* uart, int tx_pin, int rx_pin) +{ + if(uart == nullptr) { + return false; + } + + // Valid GPIO for UART TX function + auto is_tx_pin = [&](uint8_t pin) -> bool { + const uint8_t uart_tx_pins[2][4] = { + {0, 12, 16, 28}, + {4, 8, 20, 24}, + }; + auto& txpins = uart_tx_pins[uart->uart_nr]; + return pin == txpins[0] || pin == txpins[1] || pin == txpins[2] || pin == txpins[3]; + }; + // UART pins are in groups, RX pins follow TX + auto is_rx_pin = [&](uint8_t pin) -> bool { return is_tx_pin(pin - 1); }; + + if(tx_pin != UART_PIN_NO_CHANGE && !is_tx_pin(tx_pin)) { + return false; + } + if(rx_pin != UART_PIN_NO_CHANGE && !is_rx_pin(rx_pin)) { + return false; + } + + if(tx_pin != UART_PIN_NO_CHANGE) { + if(uart->tx_pin != UART_PIN_DEFAULT) { + gpio_set_function(uart->tx_pin, GPIO_FUNC_NULL); + } + gpio_set_function(tx_pin, GPIO_FUNC_UART); + uart->tx_pin = tx_pin; + } + + if(rx_pin != UART_PIN_NO_CHANGE) { + if(uart->rx_pin != UART_PIN_DEFAULT) { + gpio_set_function(uart->rx_pin, GPIO_FUNC_NULL); + } + gpio_set_function(rx_pin, GPIO_FUNC_UART); + uart->rx_pin = rx_pin; + } + + return true; +} + +void smg_uart_debug_putc(char c) +{ + smg_uart_t* uart = smg_uart_get_uart(s_uart_debug_nr); + if(uart != nullptr) { + smg_uart_write_char(uart, c); + } +} + +void smg_uart_set_debug(int uart_nr) +{ + s_uart_debug_nr = uart_nr; + system_set_os_print(uart_nr >= 0); +} + +int smg_uart_get_debug() +{ + return s_uart_debug_nr; +} + +void smg_uart_detach(int uart_nr) +{ + if(!bitRead(isrMask, uart_nr)) { + return; + } + + uart_disable_isr(uart_nr); + auto dev = getDevice(uart_nr); + dev->imsc = 0; + + bitClear(isrMask, uart_nr); +} + +void smg_uart_detach_all() +{ + smg_uart_detach(0); + smg_uart_detach(1); +} diff --git a/Sming/Arch/Rp2040/Components/driver/uart.rst b/Sming/Arch/Rp2040/Components/driver/uart.rst new file mode 100644 index 0000000000..665a30ec31 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/driver/uart.rst @@ -0,0 +1,8 @@ +UART: Universal Asynchronous Receive/Transmit +============================================= + +Custom asynchronous driver. + +.. doxygengroup:: uart_driver + :content-only: + :members: diff --git a/Sming/Arch/Rp2040/Components/gdbstub/README.rst b/Sming/Arch/Rp2040/Components/gdbstub/README.rst new file mode 100644 index 0000000000..e13e526cee --- /dev/null +++ b/Sming/Arch/Rp2040/Components/gdbstub/README.rst @@ -0,0 +1,6 @@ +GDB Stub for RP2040 +=================== + +This defines the command line to use when ``make gdb`` is run. + +TODO: Implement stub code to allow serial debugging. diff --git a/Sming/Arch/Rp2040/Components/gdbstub/component.mk b/Sming/Arch/Rp2040/Components/gdbstub/component.mk new file mode 100644 index 0000000000..46fd3ee841 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/gdbstub/component.mk @@ -0,0 +1,6 @@ +# Full GDB command line +DEBUG_VARS += GDBSTUB_DIR +GDBSTUB_DIR := $(COMPONENT_PATH) + +CACHE_VARS += GDB_CMDLINE +GDB_CMDLINE = trap '' INT; $(GDB) -x $(GDBSTUB_DIR)/gdbcmds --args $(TARGET_OUT_0) $(CLI_TARGET_OPTIONS) --pause -- $(HOST_PARAMETERS) diff --git a/Sming/Arch/Rp2040/Components/gdbstub/gdb_syscall.cpp b/Sming/Arch/Rp2040/Components/gdbstub/gdb_syscall.cpp new file mode 100644 index 0000000000..52c57f1c0d --- /dev/null +++ b/Sming/Arch/Rp2040/Components/gdbstub/gdb_syscall.cpp @@ -0,0 +1,8 @@ +#include +#include + +int gdb_syscall(const GdbSyscallInfo& info) +{ + errno = ENODEV; + return -1; +} diff --git a/Sming/Arch/Rp2040/Components/gdbstub/gdbcmds b/Sming/Arch/Rp2040/Components/gdbstub/gdbcmds new file mode 100644 index 0000000000..ce3dfcdbb9 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/gdbstub/gdbcmds @@ -0,0 +1,10 @@ +handle SIGUSR1 nostop noprint + +# Enable this if you want to log all traffic between GDB and the stub +#set remotelogfile gdb_rsp_logfile.txt + +# Useful for lower-level debugging to see the result of stepping through assembler code +#set disassemble-next-line on + +# Display a welcome prompt +echo \nWelcome to SMING!\nType 'r' to run application\n\n diff --git a/Sming/Arch/Rp2040/Components/gdbstub/gdbstub.c b/Sming/Arch/Rp2040/Components/gdbstub/gdbstub.c new file mode 100644 index 0000000000..4b22298076 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/gdbstub/gdbstub.c @@ -0,0 +1,29 @@ +#include + +void gdb_enable(bool state) +{ +} + +GdbState gdb_present(void) +{ + return eGDB_NotPresent; +} + +void __attribute__((weak)) gdb_on_attach(bool attached) +{ +} + +void gdb_detach(void) +{ +} + + + +unsigned __gdb_no_op(void) +{ + return 0; +} + +//#define NOOP __attribute__((weak, alias("__gdb_no_op"))) +// +//void gdb_on_attach(bool attached) NOOP; diff --git a/Sming/Arch/Rp2040/Components/libc/README.rst b/Sming/Arch/Rp2040/Components/libc/README.rst new file mode 100644 index 0000000000..fd5cfaf62a --- /dev/null +++ b/Sming/Arch/Rp2040/Components/libc/README.rst @@ -0,0 +1,4 @@ +RP2040 LIBC Component +===================== + +This Component accommodates the differences in runtime libraries for the various supported toolchains. diff --git a/Sming/Arch/Rp2040/Components/libc/component.mk b/Sming/Arch/Rp2040/Components/libc/component.mk new file mode 100644 index 0000000000..e316951715 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/libc/component.mk @@ -0,0 +1,11 @@ +COMPONENT_INCDIRS := src/include +COMPONENT_SRCDIRS := src + +# pico_printf +LIBC_DEFSYMS := \ + __wrap_putchar=m_putc \ + __wrap_puts=m_puts \ + __wrap_vprintf=m_vprintf \ + __wrap_printf=m_printf + +EXTRA_LDFLAGS := $(call DefSym,$(LIBC_DEFSYMS)) diff --git a/Sming/Arch/Rp2040/Components/libc/src/heap.c b/Sming/Arch/Rp2040/Components/libc/src/heap.c new file mode 100644 index 0000000000..de274e3b4e --- /dev/null +++ b/Sming/Arch/Rp2040/Components/libc/src/heap.c @@ -0,0 +1,16 @@ +/* + * heap.c + */ + +#include "include/heap.h" +#include + +uint32_t system_get_free_heap_size(void) +{ + // These are set by linker + extern char __end__; + extern char __StackLimit; + uint32_t maxHeap = (uint32_t)&__StackLimit - (uint32_t)&__end__; + struct mallinfo m = mallinfo(); + return maxHeap - m.uordblks; +} diff --git a/Sming/Arch/Rp2040/Components/libc/src/include/esp_attr.h b/Sming/Arch/Rp2040/Components/libc/src/include/esp_attr.h new file mode 100644 index 0000000000..34a29d9f7e --- /dev/null +++ b/Sming/Arch/Rp2040/Components/libc/src/include/esp_attr.h @@ -0,0 +1,32 @@ +// ESP8266 attribute definitions (previously in c_types.h) +#pragma once + +// http://stackoverflow.com/a/35441900 +#define MACROQUOT(x) #x +#define MACROQUOTE(x) MACROQUOT(x) + +#define STORE_TYPEDEF_ATTR __attribute__((aligned(4), packed)) +#define STORE_ATTR __attribute__((aligned(4))) + +#define DMEM_ATTR __attribute__((section(".bss"))) + +#define ICACHE_FLASH_SECTION ".rodata" + +#define ICACHE_FLASH_ATTR \ + __attribute__((section(ICACHE_FLASH_SECTION "." __FILE__ "." MACROQUOTE(__LINE__) "." MACROQUOTE(__COUNTER__)))) + +#define ICACHE_RODATA_SECTION ICACHE_FLASH_SECTION + +#define ICACHE_RODATA_ATTR ICACHE_FLASH_ATTR + +#define ICACHE_RAM_SECTION ".time_critical." + +#define IRAM_ATTR __attribute__((section(ICACHE_RAM_SECTION "." MACROQUOTE(__LINE__) "." MACROQUOTE(__COUNTER__)))) + +#define STORE_ATTR __attribute__((aligned(4))) + +#ifdef ENABLE_GDB +#define GDB_IRAM_ATTR IRAM_ATTR +#else +#define GDB_IRAM_ATTR +#endif diff --git a/Sming/Arch/Rp2040/Components/libc/src/include/heap.h b/Sming/Arch/Rp2040/Components/libc/src/include/heap.h new file mode 100644 index 0000000000..7c80854cba --- /dev/null +++ b/Sming/Arch/Rp2040/Components/libc/src/include/heap.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +uint32_t system_get_free_heap_size(void); + +#ifdef __cplusplus +} +#endif diff --git a/Sming/Arch/Rp2040/Components/libc/src/include/sys/pgmspace.h b/Sming/Arch/Rp2040/Components/libc/src/include/sys/pgmspace.h new file mode 100644 index 0000000000..676e70fec6 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/libc/src/include/sys/pgmspace.h @@ -0,0 +1,80 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * pgmspace.h - Support for reading flash memory + * + ****/ + +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Simple check to determine if a pointer refers to flash memory + */ +#define isFlashPtr(ptr) ((uint32_t)(ptr) >= XIP_MAIN_BASE && (uint32_t)(ptr) < XIP_NOALLOC_BASE) + +#define PROGMEM STORE_ATTR ICACHE_RODATA_ATTR +#define PROGMEM_PSTR PROGMEM +#define PSTR(str) (str) + +#define PGM_P const char* +#define PGM_VOID_P const void* + +/** + * @name Macros to safely read PROGMEM locations + * @{ + */ + +#define pgm_read_byte(addr) (*(const unsigned char*)(addr)) +#define pgm_read_word(addr) \ + ({ \ + uint32_t tmp_addr = (uint32_t)(addr); \ + *(const uint16_t*)tmp_addr; \ + }) +#define pgm_read_dword(addr) \ + ({ \ + uint32_t tmp_addr = (uint32_t)(addr); \ + *(const uint32_t*)tmp_addr; \ + }) +#define pgm_read_float(addr) \ + ({ \ + uint32_t tmp_addr = (uint32_t)(addr); \ + *(const float*)tmp_addr; \ + }) + +/** @} */ + +#define pgm_read_byte_near(addr) pgm_read_byte(addr) +#define pgm_read_word_near(addr) pgm_read_word(addr) +#define pgm_read_dword_near(addr) pgm_read_dword(addr) +#define pgm_read_float_near(addr) pgm_read_float(addr) +#define pgm_read_byte_far(addr) pgm_read_byte(addr) +#define pgm_read_word_far(addr) pgm_read_word(addr) +#define pgm_read_dword_far(addr) pgm_read_dword(addr) +#define pgm_read_float_far(addr) pgm_read_float(addr) + +#define memcpy_P(dest, src, num) memcpy(dest, src, num) +#define memcmp_P(a1, b1, len) memcmp(a1, b1, len) +#define strlen_P(a) strlen(a) +#define strcpy_P(dest, src) strcpy(dest, src) +#define strncpy_P(dest, src, size) strncpy(dest, src, size) +#define strcmp_P(a, b) strcmp(a, b) +#define strncmp_P(str1, str2_P, size) strncmp(str1, str2_P, size) +#define strcasecmp_P(a, b) strcasecmp(a, b) +#define strcat_P(dest, src) strcat(dest, src) +#define strstr_P(a, b) strstr(a, b) +#define sprintf_P(s, f, ...) m_snprintf(s, 1024, f, ##__VA_ARGS__) + +#ifdef __cplusplus +} +#endif diff --git a/Sming/Arch/Rp2040/Components/picotool/README.rst b/Sming/Arch/Rp2040/Components/picotool/README.rst new file mode 100644 index 0000000000..e887b5d13e --- /dev/null +++ b/Sming/Arch/Rp2040/Components/picotool/README.rst @@ -0,0 +1,6 @@ +Picotool +======== + +Picotool is a tool for inspecting RP2040 binaries, and interacting with RP2040 devices when they are in BOOTSEL mode. + +Note for full documentation see https://rptl.io/pico-get-started Appendix B. diff --git a/Sming/Arch/Rp2040/Components/picotool/component.mk b/Sming/Arch/Rp2040/Components/picotool/component.mk new file mode 100644 index 0000000000..6a6611b74c --- /dev/null +++ b/Sming/Arch/Rp2040/Components/picotool/component.mk @@ -0,0 +1,35 @@ +COMPONENT_SUBMODULES := picotool +PICOTOOL_SRC := $(COMPONENT_PATH)/picotool +LIBUSB_PATH := $(COMPONENT_PATH)/libusb +LIBUSB_DLL := libusb-1.0.dll +PICOTOOL := $(TOOLS_BASE)/picotool$(TOOL_EXT) + +DEBUG_VARS += PICOTOOL + +CMAKE_OPTIONS := + +ifeq ($(UNAME),Windows) +PICOTOOL_CMAKE_OPTIONS += \ + -DLIBUSB_INCLUDE_DIR=$(LIBUSB_PATH) \ + -DLIBUSB_LIBRARIES=$(LIBUSB_PATH)/$(LIBUSB_DLL) \ + -G "MSYS Makefiles" + +COMPONENT_TARGETS += $(TOOLS_BASE)/$(LIBUSB_DLL) + +$(COMPONENT_RULE)$(TOOLS_BASE)/$(LIBUSB_DLL): $(LIBUSB_PATH)/$(LIBUSB_DLL) + cp $< $@ +endif + +COMPONENT_TARGETS += $(PICOTOOL) + +$(COMPONENT_RULE)$(PICOTOOL): + $(Q) mkdir -p $(@D) + $(Q) cd $(@D) && $(CMAKE) $(PICOTOOL_CMAKE_OPTIONS) $(PICOTOOL_SRC) && $(MAKE) + + +# Read flash manufacturer ID and determine actual size +define ReadFlashID + $(info ReadFlashID) + $(Q) $(PICOTOOL) info -a $(TARGET_BIN) + # $(PICOTOOL) help info + endef diff --git a/Sming/Arch/Rp2040/Components/picotool/libusb/libusb-1.0.dll b/Sming/Arch/Rp2040/Components/picotool/libusb/libusb-1.0.dll new file mode 100644 index 0000000000000000000000000000000000000000..fd7a9414f250c677bc46a7961c7e518c848f915e GIT binary patch literal 262758 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

mr+T#rG~op0#;fIi$!uzM+ot|4 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 literal 0 HcmV?d00001 diff --git a/Sming/Arch/Rp2040/Components/picotool/libusb/libusb.h b/Sming/Arch/Rp2040/Components/picotool/libusb/libusb.h new file mode 100644 index 0000000000..1308571cd6 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/picotool/libusb/libusb.h @@ -0,0 +1,2113 @@ +/* + * Public libusb header file + * Copyright © 2001 Johannes Erdfelt + * Copyright © 2007-2008 Daniel Drake + * Copyright © 2012 Pete Batard + * Copyright © 2012-2018 Nathan Hjelm + * Copyright © 2014-2020 Chris Dickens + * For more information, please visit: http://libusb.info + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_H +#define LIBUSB_H + +#if defined(_MSC_VER) +/* on MS environments, the inline keyword is available in C++ only */ +#if !defined(__cplusplus) +#define inline __inline +#endif +/* ssize_t is also not available */ +#include +typedef SSIZE_T ssize_t; +#endif /* _MSC_VER */ + +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#include + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) +#define ZERO_SIZED_ARRAY /* [] - valid C99 code */ +#else +#define ZERO_SIZED_ARRAY 0 /* [0] - non-standard, but usually working code */ +#endif /* __STDC_VERSION__ */ + +/* 'interface' might be defined as a macro on Windows, so we need to + * undefine it so as not to break the current libusb API, because + * libusb_config_descriptor has an 'interface' member + * As this can be problematic if you include windows.h after libusb.h + * in your sources, we force windows.h to be included first. */ +#if defined(_WIN32) || defined(__CYGWIN__) +#include +#if defined(interface) +#undef interface +#endif +#if !defined(__CYGWIN__) +#include +#endif +#endif /* _WIN32 || __CYGWIN__ */ + +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated ("Use " #f " instead"))) +#elif defined(__GNUC__) && (__GNUC__ >= 3) +#define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated)) +#else +#define LIBUSB_DEPRECATED_FOR(f) +#endif /* __GNUC__ */ + +#if defined(__GNUC__) +#define LIBUSB_PACKED __attribute__ ((packed)) +#else +#define LIBUSB_PACKED +#endif /* __GNUC__ */ + +/** \def LIBUSB_CALL + * \ingroup libusb_misc + * libusb's Windows calling convention. + * + * Under Windows, the selection of available compilers and configurations + * means that, unlike other platforms, there is not one true calling + * convention (calling convention: the manner in which parameters are + * passed to functions in the generated assembly code). + * + * Matching the Windows API itself, libusb uses the WINAPI convention (which + * translates to the stdcall convention) and guarantees that the + * library is compiled in this way. The public header file also includes + * appropriate annotations so that your own software will use the right + * convention, even if another convention is being used by default within + * your codebase. + * + * The one consideration that you must apply in your software is to mark + * all functions which you use as libusb callbacks with this LIBUSB_CALL + * annotation, so that they too get compiled for the correct calling + * convention. + * + * On non-Windows operating systems, this macro is defined as nothing. This + * means that you can apply it to your code without worrying about + * cross-platform compatibility. + */ +/* LIBUSB_CALL must be defined on both definition and declaration of libusb + * functions. You'd think that declaration would be enough, but cygwin will + * complain about conflicting types unless both are marked this way. + * The placement of this macro is important too; it must appear after the + * return type, before the function name. See internal documentation for + * API_EXPORTED. + */ +#if defined(_WIN32) || defined(__CYGWIN__) +#define LIBUSB_CALL WINAPI +#else +#define LIBUSB_CALL +#endif /* _WIN32 || __CYGWIN__ */ + +/** \def LIBUSB_API_VERSION + * \ingroup libusb_misc + * libusb's API version. + * + * Since version 1.0.13, to help with feature detection, libusb defines + * a LIBUSB_API_VERSION macro that gets increased every time there is a + * significant change to the API, such as the introduction of a new call, + * the definition of a new macro/enum member, or any other element that + * libusb applications may want to detect at compilation time. + * + * The macro is typically used in an application as follows: + * \code + * #if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01001234) + * // Use one of the newer features from the libusb API + * #endif + * \endcode + * + * Internally, LIBUSB_API_VERSION is defined as follows: + * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental) + */ +#define LIBUSB_API_VERSION 0x01000108 + +/* The following is kept for compatibility, but will be deprecated in the future */ +#define LIBUSBX_API_VERSION LIBUSB_API_VERSION + +#if defined(__cplusplus) +extern "C" { +#endif + +/** + * \ingroup libusb_misc + * Convert a 16-bit value from host-endian to little-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the host-endian value to convert + * \returns the value in little-endian byte order + */ +static inline uint16_t libusb_cpu_to_le16(const uint16_t x) +{ + union { + uint8_t b8[2]; + uint16_t b16; + } _tmp; + _tmp.b8[1] = (uint8_t) (x >> 8); + _tmp.b8[0] = (uint8_t) (x & 0xff); + return _tmp.b16; +} + +/** \def libusb_le16_to_cpu + * \ingroup libusb_misc + * Convert a 16-bit value from little-endian to host-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the little-endian value to convert + * \returns the value in host-endian byte order + */ +#define libusb_le16_to_cpu libusb_cpu_to_le16 + +/* standard USB stuff */ + +/** \ingroup libusb_desc + * Device and/or Interface Class codes */ +enum libusb_class_code { + /** In the context of a \ref libusb_device_descriptor "device descriptor", + * this bDeviceClass value indicates that each interface specifies its + * own class information and all interfaces operate independently. + */ + LIBUSB_CLASS_PER_INTERFACE = 0x00, + + /** Audio class */ + LIBUSB_CLASS_AUDIO = 0x01, + + /** Communications class */ + LIBUSB_CLASS_COMM = 0x02, + + /** Human Interface Device class */ + LIBUSB_CLASS_HID = 0x03, + + /** Physical */ + LIBUSB_CLASS_PHYSICAL = 0x05, + + /** Image class */ + LIBUSB_CLASS_IMAGE = 0x06, + LIBUSB_CLASS_PTP = 0x06, /* legacy name from libusb-0.1 usb.h */ + + /** Printer class */ + LIBUSB_CLASS_PRINTER = 0x07, + + /** Mass storage class */ + LIBUSB_CLASS_MASS_STORAGE = 0x08, + + /** Hub class */ + LIBUSB_CLASS_HUB = 0x09, + + /** Data class */ + LIBUSB_CLASS_DATA = 0x0a, + + /** Smart Card */ + LIBUSB_CLASS_SMART_CARD = 0x0b, + + /** Content Security */ + LIBUSB_CLASS_CONTENT_SECURITY = 0x0d, + + /** Video */ + LIBUSB_CLASS_VIDEO = 0x0e, + + /** Personal Healthcare */ + LIBUSB_CLASS_PERSONAL_HEALTHCARE = 0x0f, + + /** Diagnostic Device */ + LIBUSB_CLASS_DIAGNOSTIC_DEVICE = 0xdc, + + /** Wireless class */ + LIBUSB_CLASS_WIRELESS = 0xe0, + + /** Miscellaneous class */ + LIBUSB_CLASS_MISCELLANEOUS = 0xef, + + /** Application class */ + LIBUSB_CLASS_APPLICATION = 0xfe, + + /** Class is vendor-specific */ + LIBUSB_CLASS_VENDOR_SPEC = 0xff +}; + +/** \ingroup libusb_desc + * Descriptor types as defined by the USB specification. */ +enum libusb_descriptor_type { + /** Device descriptor. See libusb_device_descriptor. */ + LIBUSB_DT_DEVICE = 0x01, + + /** Configuration descriptor. See libusb_config_descriptor. */ + LIBUSB_DT_CONFIG = 0x02, + + /** String descriptor */ + LIBUSB_DT_STRING = 0x03, + + /** Interface descriptor. See libusb_interface_descriptor. */ + LIBUSB_DT_INTERFACE = 0x04, + + /** Endpoint descriptor. See libusb_endpoint_descriptor. */ + LIBUSB_DT_ENDPOINT = 0x05, + + /** BOS descriptor */ + LIBUSB_DT_BOS = 0x0f, + + /** Device Capability descriptor */ + LIBUSB_DT_DEVICE_CAPABILITY = 0x10, + + /** HID descriptor */ + LIBUSB_DT_HID = 0x21, + + /** HID report descriptor */ + LIBUSB_DT_REPORT = 0x22, + + /** Physical descriptor */ + LIBUSB_DT_PHYSICAL = 0x23, + + /** Hub descriptor */ + LIBUSB_DT_HUB = 0x29, + + /** SuperSpeed Hub descriptor */ + LIBUSB_DT_SUPERSPEED_HUB = 0x2a, + + /** SuperSpeed Endpoint Companion descriptor */ + LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30 +}; + +/* Descriptor sizes per descriptor type */ +#define LIBUSB_DT_DEVICE_SIZE 18 +#define LIBUSB_DT_CONFIG_SIZE 9 +#define LIBUSB_DT_INTERFACE_SIZE 9 +#define LIBUSB_DT_ENDPOINT_SIZE 7 +#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define LIBUSB_DT_HUB_NONVAR_SIZE 7 +#define LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE 6 +#define LIBUSB_DT_BOS_SIZE 5 +#define LIBUSB_DT_DEVICE_CAPABILITY_SIZE 3 + +/* BOS descriptor sizes */ +#define LIBUSB_BT_USB_2_0_EXTENSION_SIZE 7 +#define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE 10 +#define LIBUSB_BT_CONTAINER_ID_SIZE 20 + +/* We unwrap the BOS => define its max size */ +#define LIBUSB_DT_BOS_MAX_SIZE \ + (LIBUSB_DT_BOS_SIZE + \ + LIBUSB_BT_USB_2_0_EXTENSION_SIZE + \ + LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE + \ + LIBUSB_BT_CONTAINER_ID_SIZE) + +#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define LIBUSB_ENDPOINT_DIR_MASK 0x80 + +/** \ingroup libusb_desc + * Endpoint direction. Values for bit 7 of the + * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme. + */ +enum libusb_endpoint_direction { + /** Out: host-to-device */ + LIBUSB_ENDPOINT_OUT = 0x00, + + /** In: device-to-host */ + LIBUSB_ENDPOINT_IN = 0x80 +}; + +#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ + +/** \ingroup libusb_desc + * Endpoint transfer type. Values for bits 0:1 of the + * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field. + */ +enum libusb_endpoint_transfer_type { + /** Control endpoint */ + LIBUSB_ENDPOINT_TRANSFER_TYPE_CONTROL = 0x0, + + /** Isochronous endpoint */ + LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS = 0x1, + + /** Bulk endpoint */ + LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK = 0x2, + + /** Interrupt endpoint */ + LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT = 0x3 +}; + +/** \ingroup libusb_misc + * Standard requests, as defined in table 9-5 of the USB 3.0 specifications */ +enum libusb_standard_request { + /** Request status of the specific recipient */ + LIBUSB_REQUEST_GET_STATUS = 0x00, + + /** Clear or disable a specific feature */ + LIBUSB_REQUEST_CLEAR_FEATURE = 0x01, + + /* 0x02 is reserved */ + + /** Set or enable a specific feature */ + LIBUSB_REQUEST_SET_FEATURE = 0x03, + + /* 0x04 is reserved */ + + /** Set device address for all future accesses */ + LIBUSB_REQUEST_SET_ADDRESS = 0x05, + + /** Get the specified descriptor */ + LIBUSB_REQUEST_GET_DESCRIPTOR = 0x06, + + /** Used to update existing descriptors or add new descriptors */ + LIBUSB_REQUEST_SET_DESCRIPTOR = 0x07, + + /** Get the current device configuration value */ + LIBUSB_REQUEST_GET_CONFIGURATION = 0x08, + + /** Set device configuration */ + LIBUSB_REQUEST_SET_CONFIGURATION = 0x09, + + /** Return the selected alternate setting for the specified interface */ + LIBUSB_REQUEST_GET_INTERFACE = 0x0a, + + /** Select an alternate interface for the specified interface */ + LIBUSB_REQUEST_SET_INTERFACE = 0x0b, + + /** Set then report an endpoint's synchronization frame */ + LIBUSB_REQUEST_SYNCH_FRAME = 0x0c, + + /** Sets both the U1 and U2 Exit Latency */ + LIBUSB_REQUEST_SET_SEL = 0x30, + + /** Delay from the time a host transmits a packet to the time it is + * received by the device. */ + LIBUSB_SET_ISOCH_DELAY = 0x31 +}; + +/** \ingroup libusb_misc + * Request type bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. */ +enum libusb_request_type { + /** Standard */ + LIBUSB_REQUEST_TYPE_STANDARD = (0x00 << 5), + + /** Class */ + LIBUSB_REQUEST_TYPE_CLASS = (0x01 << 5), + + /** Vendor */ + LIBUSB_REQUEST_TYPE_VENDOR = (0x02 << 5), + + /** Reserved */ + LIBUSB_REQUEST_TYPE_RESERVED = (0x03 << 5) +}; + +/** \ingroup libusb_misc + * Recipient bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. Values 4 through 31 are reserved. */ +enum libusb_request_recipient { + /** Device */ + LIBUSB_RECIPIENT_DEVICE = 0x00, + + /** Interface */ + LIBUSB_RECIPIENT_INTERFACE = 0x01, + + /** Endpoint */ + LIBUSB_RECIPIENT_ENDPOINT = 0x02, + + /** Other */ + LIBUSB_RECIPIENT_OTHER = 0x03 +}; + +#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0c + +/** \ingroup libusb_desc + * Synchronization type for isochronous endpoints. Values for bits 2:3 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_sync_type { + /** No synchronization */ + LIBUSB_ISO_SYNC_TYPE_NONE = 0x0, + + /** Asynchronous */ + LIBUSB_ISO_SYNC_TYPE_ASYNC = 0x1, + + /** Adaptive */ + LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 0x2, + + /** Synchronous */ + LIBUSB_ISO_SYNC_TYPE_SYNC = 0x3 +}; + +#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 + +/** \ingroup libusb_desc + * Usage type for isochronous endpoints. Values for bits 4:5 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_usage_type { + /** Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_DATA = 0x0, + + /** Feedback endpoint */ + LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 0x1, + + /** Implicit feedback Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 0x2 +}; + +/** \ingroup libusb_desc + * Supported speeds (wSpeedSupported) bitfield. Indicates what + * speeds the device supports. + */ +enum libusb_supported_speed { + /** Low speed operation supported (1.5MBit/s). */ + LIBUSB_LOW_SPEED_OPERATION = (1 << 0), + + /** Full speed operation supported (12MBit/s). */ + LIBUSB_FULL_SPEED_OPERATION = (1 << 1), + + /** High speed operation supported (480MBit/s). */ + LIBUSB_HIGH_SPEED_OPERATION = (1 << 2), + + /** Superspeed operation supported (5000MBit/s). */ + LIBUSB_SUPER_SPEED_OPERATION = (1 << 3) +}; + +/** \ingroup libusb_desc + * Masks for the bits of the + * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field + * of the USB 2.0 Extension descriptor. + */ +enum libusb_usb_2_0_extension_attributes { + /** Supports Link Power Management (LPM) */ + LIBUSB_BM_LPM_SUPPORT = (1 << 1) +}; + +/** \ingroup libusb_desc + * Masks for the bits of the + * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field + * field of the SuperSpeed USB Device Capability descriptor. + */ +enum libusb_ss_usb_device_capability_attributes { + /** Supports Latency Tolerance Messages (LTM) */ + LIBUSB_BM_LTM_SUPPORT = (1 << 1) +}; + +/** \ingroup libusb_desc + * USB capability types + */ +enum libusb_bos_type { + /** Wireless USB device capability */ + LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 0x01, + + /** USB 2.0 extensions */ + LIBUSB_BT_USB_2_0_EXTENSION = 0x02, + + /** SuperSpeed USB device capability */ + LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 0x03, + + /** Container ID type */ + LIBUSB_BT_CONTAINER_ID = 0x04 +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB device descriptor. This + * descriptor is documented in section 9.6.1 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_device_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE LIBUSB_DT_DEVICE in this + * context. */ + uint8_t bDescriptorType; + + /** USB specification release number in binary-coded decimal. A value of + * 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc. */ + uint16_t bcdUSB; + + /** USB-IF class code for the device. See \ref libusb_class_code. */ + uint8_t bDeviceClass; + + /** USB-IF subclass code for the device, qualified by the bDeviceClass + * value */ + uint8_t bDeviceSubClass; + + /** USB-IF protocol code for the device, qualified by the bDeviceClass and + * bDeviceSubClass values */ + uint8_t bDeviceProtocol; + + /** Maximum packet size for endpoint 0 */ + uint8_t bMaxPacketSize0; + + /** USB-IF vendor ID */ + uint16_t idVendor; + + /** USB-IF product ID */ + uint16_t idProduct; + + /** Device release number in binary-coded decimal */ + uint16_t bcdDevice; + + /** Index of string descriptor describing manufacturer */ + uint8_t iManufacturer; + + /** Index of string descriptor describing product */ + uint8_t iProduct; + + /** Index of string descriptor containing device serial number */ + uint8_t iSerialNumber; + + /** Number of possible configurations */ + uint8_t bNumConfigurations; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB endpoint descriptor. This + * descriptor is documented in section 9.6.6 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_endpoint_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_ENDPOINT LIBUSB_DT_ENDPOINT in + * this context. */ + uint8_t bDescriptorType; + + /** The address of the endpoint described by this descriptor. Bits 0:3 are + * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction, + * see \ref libusb_endpoint_direction. */ + uint8_t bEndpointAddress; + + /** Attributes which apply to the endpoint when it is configured using + * the bConfigurationValue. Bits 0:1 determine the transfer type and + * correspond to \ref libusb_endpoint_transfer_type. Bits 2:3 are only used + * for isochronous endpoints and correspond to \ref libusb_iso_sync_type. + * Bits 4:5 are also only used for isochronous endpoints and correspond to + * \ref libusb_iso_usage_type. Bits 6:7 are reserved. */ + uint8_t bmAttributes; + + /** Maximum packet size this endpoint is capable of sending/receiving. */ + uint16_t wMaxPacketSize; + + /** Interval for polling endpoint for data transfers. */ + uint8_t bInterval; + + /** For audio devices only: the rate at which synchronization feedback + * is provided. */ + uint8_t bRefresh; + + /** For audio devices only: the address if the synch endpoint */ + uint8_t bSynchAddress; + + /** Extra descriptors. If libusb encounters unknown endpoint descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. Must be non-negative. */ + int extra_length; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB interface descriptor. This + * descriptor is documented in section 9.6.5 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_interface_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE LIBUSB_DT_INTERFACE + * in this context. */ + uint8_t bDescriptorType; + + /** Number of this interface */ + uint8_t bInterfaceNumber; + + /** Value used to select this alternate setting for this interface */ + uint8_t bAlternateSetting; + + /** Number of endpoints used by this interface (excluding the control + * endpoint). */ + uint8_t bNumEndpoints; + + /** USB-IF class code for this interface. See \ref libusb_class_code. */ + uint8_t bInterfaceClass; + + /** USB-IF subclass code for this interface, qualified by the + * bInterfaceClass value */ + uint8_t bInterfaceSubClass; + + /** USB-IF protocol code for this interface, qualified by the + * bInterfaceClass and bInterfaceSubClass values */ + uint8_t bInterfaceProtocol; + + /** Index of string descriptor describing this interface */ + uint8_t iInterface; + + /** Array of endpoint descriptors. This length of this array is determined + * by the bNumEndpoints field. */ + const struct libusb_endpoint_descriptor *endpoint; + + /** Extra descriptors. If libusb encounters unknown interface descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. Must be non-negative. */ + int extra_length; +}; + +/** \ingroup libusb_desc + * A collection of alternate settings for a particular USB interface. + */ +struct libusb_interface { + /** Array of interface descriptors. The length of this array is determined + * by the num_altsetting field. */ + const struct libusb_interface_descriptor *altsetting; + + /** The number of alternate settings that belong to this interface. + * Must be non-negative. */ + int num_altsetting; +}; + +/** \ingroup libusb_desc + * A structure representing the standard USB configuration descriptor. This + * descriptor is documented in section 9.6.3 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_config_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_CONFIG LIBUSB_DT_CONFIG + * in this context. */ + uint8_t bDescriptorType; + + /** Total length of data returned for this configuration */ + uint16_t wTotalLength; + + /** Number of interfaces supported by this configuration */ + uint8_t bNumInterfaces; + + /** Identifier value for this configuration */ + uint8_t bConfigurationValue; + + /** Index of string descriptor describing this configuration */ + uint8_t iConfiguration; + + /** Configuration characteristics */ + uint8_t bmAttributes; + + /** Maximum power consumption of the USB device from this bus in this + * configuration when the device is fully operation. Expressed in units + * of 2 mA when the device is operating in high-speed mode and in units + * of 8 mA when the device is operating in super-speed mode. */ + uint8_t MaxPower; + + /** Array of interfaces supported by this configuration. The length of + * this array is determined by the bNumInterfaces field. */ + const struct libusb_interface *interface; + + /** Extra descriptors. If libusb encounters unknown configuration + * descriptors, it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. Must be non-negative. */ + int extra_length; +}; + +/** \ingroup libusb_desc + * A structure representing the superspeed endpoint companion + * descriptor. This descriptor is documented in section 9.6.7 of + * the USB 3.0 specification. All multiple-byte fields are represented in + * host-endian format. + */ +struct libusb_ss_endpoint_companion_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_SS_ENDPOINT_COMPANION in + * this context. */ + uint8_t bDescriptorType; + + /** The maximum number of packets the endpoint can send or + * receive as part of a burst. */ + uint8_t bMaxBurst; + + /** In bulk EP: bits 4:0 represents the maximum number of + * streams the EP supports. In isochronous EP: bits 1:0 + * represents the Mult - a zero based value that determines + * the maximum number of packets within a service interval */ + uint8_t bmAttributes; + + /** The total number of bytes this EP will transfer every + * service interval. Valid only for periodic EPs. */ + uint16_t wBytesPerInterval; +}; + +/** \ingroup libusb_desc + * A generic representation of a BOS Device Capability descriptor. It is + * advised to check bDevCapabilityType and call the matching + * libusb_get_*_descriptor function to get a structure fully matching the type. + */ +struct libusb_bos_dev_capability_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Device Capability type */ + uint8_t bDevCapabilityType; + + /** Device Capability data (bLength - 3 bytes) */ + uint8_t dev_capability_data[ZERO_SIZED_ARRAY]; +}; + +/** \ingroup libusb_desc + * A structure representing the Binary Device Object Store (BOS) descriptor. + * This descriptor is documented in section 9.6.2 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_bos_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_BOS LIBUSB_DT_BOS + * in this context. */ + uint8_t bDescriptorType; + + /** Length of this descriptor and all of its sub descriptors */ + uint16_t wTotalLength; + + /** The number of separate device capability descriptors in + * the BOS */ + uint8_t bNumDeviceCaps; + + /** bNumDeviceCap Device Capability Descriptors */ + struct libusb_bos_dev_capability_descriptor *dev_capability[ZERO_SIZED_ARRAY]; +}; + +/** \ingroup libusb_desc + * A structure representing the USB 2.0 Extension descriptor + * This descriptor is documented in section 9.6.2.1 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_usb_2_0_extension_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION + * LIBUSB_BT_USB_2_0_EXTENSION in this context. */ + uint8_t bDevCapabilityType; + + /** Bitmap encoding of supported device level features. + * A value of one in a bit location indicates a feature is + * supported; a value of zero indicates it is not supported. + * See \ref libusb_usb_2_0_extension_attributes. */ + uint32_t bmAttributes; +}; + +/** \ingroup libusb_desc + * A structure representing the SuperSpeed USB Device Capability descriptor + * This descriptor is documented in section 9.6.2.2 of the USB 3.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_ss_usb_device_capability_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEVICE_CAPABILITY + * LIBUSB_BT_SS_USB_DEVICE_CAPABILITY in this context. */ + uint8_t bDevCapabilityType; + + /** Bitmap encoding of supported device level features. + * A value of one in a bit location indicates a feature is + * supported; a value of zero indicates it is not supported. + * See \ref libusb_ss_usb_device_capability_attributes. */ + uint8_t bmAttributes; + + /** Bitmap encoding of the speed supported by this device when + * operating in SuperSpeed mode. See \ref libusb_supported_speed. */ + uint16_t wSpeedSupported; + + /** The lowest speed at which all the functionality supported + * by the device is available to the user. For example if the + * device supports all its functionality when connected at + * full speed and above then it sets this value to 1. */ + uint8_t bFunctionalitySupport; + + /** U1 Device Exit Latency. */ + uint8_t bU1DevExitLat; + + /** U2 Device Exit Latency. */ + uint16_t bU2DevExitLat; +}; + +/** \ingroup libusb_desc + * A structure representing the Container ID descriptor. + * This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification. + * All multiple-byte fields, except UUIDs, are represented in host-endian format. + */ +struct libusb_container_id_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY + * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ + uint8_t bDescriptorType; + + /** Capability type. Will have value + * \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID + * LIBUSB_BT_CONTAINER_ID in this context. */ + uint8_t bDevCapabilityType; + + /** Reserved field */ + uint8_t bReserved; + + /** 128 bit UUID */ + uint8_t ContainerID[16]; +}; + +/** \ingroup libusb_asyncio + * Setup packet for control transfers. */ +#if defined(_MSC_VER) +#pragma pack(push, 1) +#endif +struct libusb_control_setup { + /** Request type. Bits 0:4 determine recipient, see + * \ref libusb_request_recipient. Bits 5:6 determine type, see + * \ref libusb_request_type. Bit 7 determines data transfer direction, see + * \ref libusb_endpoint_direction. + */ + uint8_t bmRequestType; + + /** Request. If the type bits of bmRequestType are equal to + * \ref libusb_request_type::LIBUSB_REQUEST_TYPE_STANDARD + * "LIBUSB_REQUEST_TYPE_STANDARD" then this field refers to + * \ref libusb_standard_request. For other cases, use of this field is + * application-specific. */ + uint8_t bRequest; + + /** Value. Varies according to request */ + uint16_t wValue; + + /** Index. Varies according to request, typically used to pass an index + * or offset */ + uint16_t wIndex; + + /** Number of bytes to transfer */ + uint16_t wLength; +} LIBUSB_PACKED; +#if defined(_MSC_VER) +#pragma pack(pop) +#endif + +#define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) + +/* libusb */ + +struct libusb_context; +struct libusb_device; +struct libusb_device_handle; + +/** \ingroup libusb_lib + * Structure providing the version of the libusb runtime + */ +struct libusb_version { + /** Library major version. */ + const uint16_t major; + + /** Library minor version. */ + const uint16_t minor; + + /** Library micro version. */ + const uint16_t micro; + + /** Library nano version. */ + const uint16_t nano; + + /** Library release candidate suffix string, e.g. "-rc4". */ + const char *rc; + + /** For ABI compatibility only. */ + const char *describe; +}; + +/** \ingroup libusb_lib + * Structure representing a libusb session. The concept of individual libusb + * sessions allows for your program to use two libraries (or dynamically + * load two modules) which both independently use libusb. This will prevent + * interference between the individual libusb users - for example + * libusb_set_option() will not affect the other user of the library, and + * libusb_exit() will not destroy resources that the other user is still + * using. + * + * Sessions are created by libusb_init() and destroyed through libusb_exit(). + * If your application is guaranteed to only ever include a single libusb + * user (i.e. you), you do not have to worry about contexts: pass NULL in + * every function call where a context is required. The default context + * will be used. + * + * For more information, see \ref libusb_contexts. + */ +typedef struct libusb_context libusb_context; + +/** \ingroup libusb_dev + * Structure representing a USB device detected on the system. This is an + * opaque type for which you are only ever provided with a pointer, usually + * originating from libusb_get_device_list(). + * + * Certain operations can be performed on a device, but in order to do any + * I/O you will have to first obtain a device handle using libusb_open(). + * + * Devices are reference counted with libusb_ref_device() and + * libusb_unref_device(), and are freed when the reference count reaches 0. + * New devices presented by libusb_get_device_list() have a reference count of + * 1, and libusb_free_device_list() can optionally decrease the reference count + * on all devices in the list. libusb_open() adds another reference which is + * later destroyed by libusb_close(). + */ +typedef struct libusb_device libusb_device; + + +/** \ingroup libusb_dev + * Structure representing a handle on a USB device. This is an opaque type for + * which you are only ever provided with a pointer, usually originating from + * libusb_open(). + * + * A device handle is used to perform I/O and other operations. When finished + * with a device handle, you should call libusb_close(). + */ +typedef struct libusb_device_handle libusb_device_handle; + +/** \ingroup libusb_dev + * Speed codes. Indicates the speed at which the device is operating. + */ +enum libusb_speed { + /** The OS doesn't report or know the device speed. */ + LIBUSB_SPEED_UNKNOWN = 0, + + /** The device is operating at low speed (1.5MBit/s). */ + LIBUSB_SPEED_LOW = 1, + + /** The device is operating at full speed (12MBit/s). */ + LIBUSB_SPEED_FULL = 2, + + /** The device is operating at high speed (480MBit/s). */ + LIBUSB_SPEED_HIGH = 3, + + /** The device is operating at super speed (5000MBit/s). */ + LIBUSB_SPEED_SUPER = 4, + + /** The device is operating at super speed plus (10000MBit/s). */ + LIBUSB_SPEED_SUPER_PLUS = 5 +}; + +/** \ingroup libusb_misc + * Error codes. Most libusb functions return 0 on success or one of these + * codes on failure. + * You can call libusb_error_name() to retrieve a string representation of an + * error code or libusb_strerror() to get an end-user suitable description of + * an error code. + */ +enum libusb_error { + /** Success (no error) */ + LIBUSB_SUCCESS = 0, + + /** Input/output error */ + LIBUSB_ERROR_IO = -1, + + /** Invalid parameter */ + LIBUSB_ERROR_INVALID_PARAM = -2, + + /** Access denied (insufficient permissions) */ + LIBUSB_ERROR_ACCESS = -3, + + /** No such device (it may have been disconnected) */ + LIBUSB_ERROR_NO_DEVICE = -4, + + /** Entity not found */ + LIBUSB_ERROR_NOT_FOUND = -5, + + /** Resource busy */ + LIBUSB_ERROR_BUSY = -6, + + /** Operation timed out */ + LIBUSB_ERROR_TIMEOUT = -7, + + /** Overflow */ + LIBUSB_ERROR_OVERFLOW = -8, + + /** Pipe error */ + LIBUSB_ERROR_PIPE = -9, + + /** System call interrupted (perhaps due to signal) */ + LIBUSB_ERROR_INTERRUPTED = -10, + + /** Insufficient memory */ + LIBUSB_ERROR_NO_MEM = -11, + + /** Operation not supported or unimplemented on this platform */ + LIBUSB_ERROR_NOT_SUPPORTED = -12, + + /* NB: Remember to update LIBUSB_ERROR_COUNT below as well as the + message strings in strerror.c when adding new error codes here. */ + + /** Other error */ + LIBUSB_ERROR_OTHER = -99 +}; + +/* Total number of error codes in enum libusb_error */ +#define LIBUSB_ERROR_COUNT 14 + +/** \ingroup libusb_asyncio + * Transfer type */ +enum libusb_transfer_type { + /** Control transfer */ + LIBUSB_TRANSFER_TYPE_CONTROL = 0U, + + /** Isochronous transfer */ + LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1U, + + /** Bulk transfer */ + LIBUSB_TRANSFER_TYPE_BULK = 2U, + + /** Interrupt transfer */ + LIBUSB_TRANSFER_TYPE_INTERRUPT = 3U, + + /** Bulk stream transfer */ + LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4U +}; + +/** \ingroup libusb_asyncio + * Transfer status codes */ +enum libusb_transfer_status { + /** Transfer completed without error. Note that this does not indicate + * that the entire amount of requested data was transferred. */ + LIBUSB_TRANSFER_COMPLETED, + + /** Transfer failed */ + LIBUSB_TRANSFER_ERROR, + + /** Transfer timed out */ + LIBUSB_TRANSFER_TIMED_OUT, + + /** Transfer was cancelled */ + LIBUSB_TRANSFER_CANCELLED, + + /** For bulk/interrupt endpoints: halt condition detected (endpoint + * stalled). For control endpoints: control request not supported. */ + LIBUSB_TRANSFER_STALL, + + /** Device was disconnected */ + LIBUSB_TRANSFER_NO_DEVICE, + + /** Device sent more data than requested */ + LIBUSB_TRANSFER_OVERFLOW + + /* NB! Remember to update libusb_error_name() + when adding new status codes here. */ +}; + +/** \ingroup libusb_asyncio + * libusb_transfer.flags values */ +enum libusb_transfer_flags { + /** Report short frames as errors */ + LIBUSB_TRANSFER_SHORT_NOT_OK = (1U << 0), + + /** Automatically free() transfer buffer during libusb_free_transfer(). + * Note that buffers allocated with libusb_dev_mem_alloc() should not + * be attempted freed in this way, since free() is not an appropriate + * way to release such memory. */ + LIBUSB_TRANSFER_FREE_BUFFER = (1U << 1), + + /** Automatically call libusb_free_transfer() after callback returns. + * If this flag is set, it is illegal to call libusb_free_transfer() + * from your transfer callback, as this will result in a double-free + * when this flag is acted upon. */ + LIBUSB_TRANSFER_FREE_TRANSFER = (1U << 2), + + /** Terminate transfers that are a multiple of the endpoint's + * wMaxPacketSize with an extra zero length packet. This is useful + * when a device protocol mandates that each logical request is + * terminated by an incomplete packet (i.e. the logical requests are + * not separated by other means). + * + * This flag only affects host-to-device transfers to bulk and interrupt + * endpoints. In other situations, it is ignored. + * + * This flag only affects transfers with a length that is a multiple of + * the endpoint's wMaxPacketSize. On transfers of other lengths, this + * flag has no effect. Therefore, if you are working with a device that + * needs a ZLP whenever the end of the logical request falls on a packet + * boundary, then it is sensible to set this flag on every + * transfer (you do not have to worry about only setting it on transfers + * that end on the boundary). + * + * This flag is currently only supported on Linux. + * On other systems, libusb_submit_transfer() will return + * LIBUSB_ERROR_NOT_SUPPORTED for every transfer where this flag is set. + * + * Available since libusb-1.0.9. + */ + LIBUSB_TRANSFER_ADD_ZERO_PACKET = (1U << 3) +}; + +/** \ingroup libusb_asyncio + * Isochronous packet descriptor. */ +struct libusb_iso_packet_descriptor { + /** Length of data to request in this packet */ + unsigned int length; + + /** Amount of data that was actually transferred */ + unsigned int actual_length; + + /** Status code for this packet */ + enum libusb_transfer_status status; +}; + +struct libusb_transfer; + +/** \ingroup libusb_asyncio + * Asynchronous transfer callback function type. When submitting asynchronous + * transfers, you pass a pointer to a callback function of this type via the + * \ref libusb_transfer::callback "callback" member of the libusb_transfer + * structure. libusb will call this function later, when the transfer has + * completed or failed. See \ref libusb_asyncio for more information. + * \param transfer The libusb_transfer struct the callback function is being + * notified about. + */ +typedef void (LIBUSB_CALL *libusb_transfer_cb_fn)(struct libusb_transfer *transfer); + +/** \ingroup libusb_asyncio + * The generic USB transfer structure. The user populates this structure and + * then submits it in order to request a transfer. After the transfer has + * completed, the library populates the transfer with the results and passes + * it back to the user. + */ +struct libusb_transfer { + /** Handle of the device that this transfer will be submitted to */ + libusb_device_handle *dev_handle; + + /** A bitwise OR combination of \ref libusb_transfer_flags. */ + uint8_t flags; + + /** Address of the endpoint where this transfer will be sent. */ + unsigned char endpoint; + + /** Type of the transfer from \ref libusb_transfer_type */ + unsigned char type; + + /** Timeout for this transfer in milliseconds. A value of 0 indicates no + * timeout. */ + unsigned int timeout; + + /** The status of the transfer. Read-only, and only for use within + * transfer callback function. + * + * If this is an isochronous transfer, this field may read COMPLETED even + * if there were errors in the frames. Use the + * \ref libusb_iso_packet_descriptor::status "status" field in each packet + * to determine if errors occurred. */ + enum libusb_transfer_status status; + + /** Length of the data buffer. Must be non-negative. */ + int length; + + /** Actual length of data that was transferred. Read-only, and only for + * use within transfer callback function. Not valid for isochronous + * endpoint transfers. */ + int actual_length; + + /** Callback function. This will be invoked when the transfer completes, + * fails, or is cancelled. */ + libusb_transfer_cb_fn callback; + + /** User context data. Useful for associating specific data to a transfer + * that can be accessed from within the callback function. + * + * This field may be set manually or is taken as the `user_data` parameter + * of the following functions: + * - libusb_fill_bulk_transfer() + * - libusb_fill_bulk_stream_transfer() + * - libusb_fill_control_transfer() + * - libusb_fill_interrupt_transfer() + * - libusb_fill_iso_transfer() */ + void *user_data; + + /** Data buffer */ + unsigned char *buffer; + + /** Number of isochronous packets. Only used for I/O with isochronous + * endpoints. Must be non-negative. */ + int num_iso_packets; + + /** Isochronous packet descriptors, for isochronous transfers only. */ + struct libusb_iso_packet_descriptor iso_packet_desc[ZERO_SIZED_ARRAY]; +}; + +/** \ingroup libusb_misc + * Capabilities supported by an instance of libusb on the current running + * platform. Test if the loaded library supports a given capability by calling + * \ref libusb_has_capability(). + */ +enum libusb_capability { + /** The libusb_has_capability() API is available. */ + LIBUSB_CAP_HAS_CAPABILITY = 0x0000U, + + /** Hotplug support is available on this platform. */ + LIBUSB_CAP_HAS_HOTPLUG = 0x0001U, + + /** The library can access HID devices without requiring user intervention. + * Note that before being able to actually access an HID device, you may + * still have to call additional libusb functions such as + * \ref libusb_detach_kernel_driver(). */ + LIBUSB_CAP_HAS_HID_ACCESS = 0x0100U, + + /** The library supports detaching of the default USB driver, using + * \ref libusb_detach_kernel_driver(), if one is set by the OS kernel */ + LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101U +}; + +/** \ingroup libusb_lib + * Log message levels. + */ +enum libusb_log_level { + /** (0) : No messages ever emitted by the library (default) */ + LIBUSB_LOG_LEVEL_NONE = 0, + + /** (1) : Error messages are emitted */ + LIBUSB_LOG_LEVEL_ERROR = 1, + + /** (2) : Warning and error messages are emitted */ + LIBUSB_LOG_LEVEL_WARNING = 2, + + /** (3) : Informational, warning and error messages are emitted */ + LIBUSB_LOG_LEVEL_INFO = 3, + + /** (4) : All messages are emitted */ + LIBUSB_LOG_LEVEL_DEBUG = 4 +}; + +/** \ingroup libusb_lib + * Log callback mode. + * \see libusb_set_log_cb() + */ +enum libusb_log_cb_mode { + /** Callback function handling all log messages. */ + LIBUSB_LOG_CB_GLOBAL = (1 << 0), + + /** Callback function handling context related log messages. */ + LIBUSB_LOG_CB_CONTEXT = (1 << 1) +}; + +/** \ingroup libusb_lib + * Callback function for handling log messages. + * \param ctx the context which is related to the log message, or NULL if it + * is a global log message + * \param level the log level, see \ref libusb_log_level for a description + * \param str the log message + * \see libusb_set_log_cb() + */ +typedef void (LIBUSB_CALL *libusb_log_cb)(libusb_context *ctx, + enum libusb_log_level level, const char *str); + +int LIBUSB_CALL libusb_init(libusb_context **ctx); +void LIBUSB_CALL libusb_exit(libusb_context *ctx); +LIBUSB_DEPRECATED_FOR(libusb_set_option) +void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); +void LIBUSB_CALL libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, int mode); +const struct libusb_version * LIBUSB_CALL libusb_get_version(void); +int LIBUSB_CALL libusb_has_capability(uint32_t capability); +const char * LIBUSB_CALL libusb_error_name(int errcode); +int LIBUSB_CALL libusb_setlocale(const char *locale); +const char * LIBUSB_CALL libusb_strerror(int errcode); + +ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx, + libusb_device ***list); +void LIBUSB_CALL libusb_free_device_list(libusb_device **list, + int unref_devices); +libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev); +void LIBUSB_CALL libusb_unref_device(libusb_device *dev); + +int LIBUSB_CALL libusb_get_configuration(libusb_device_handle *dev, + int *config); +int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *dev, + struct libusb_device_descriptor *desc); +int LIBUSB_CALL libusb_get_active_config_descriptor(libusb_device *dev, + struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor(libusb_device *dev, + uint8_t config_index, struct libusb_config_descriptor **config); +int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config); +void LIBUSB_CALL libusb_free_config_descriptor( + struct libusb_config_descriptor *config); +int LIBUSB_CALL libusb_get_ss_endpoint_companion_descriptor( + libusb_context *ctx, + const struct libusb_endpoint_descriptor *endpoint, + struct libusb_ss_endpoint_companion_descriptor **ep_comp); +void LIBUSB_CALL libusb_free_ss_endpoint_companion_descriptor( + struct libusb_ss_endpoint_companion_descriptor *ep_comp); +int LIBUSB_CALL libusb_get_bos_descriptor(libusb_device_handle *dev_handle, + struct libusb_bos_descriptor **bos); +void LIBUSB_CALL libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos); +int LIBUSB_CALL libusb_get_usb_2_0_extension_descriptor( + libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension); +void LIBUSB_CALL libusb_free_usb_2_0_extension_descriptor( + struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension); +int LIBUSB_CALL libusb_get_ss_usb_device_capability_descriptor( + libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap); +void LIBUSB_CALL libusb_free_ss_usb_device_capability_descriptor( + struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap); +int LIBUSB_CALL libusb_get_container_id_descriptor(libusb_context *ctx, + struct libusb_bos_dev_capability_descriptor *dev_cap, + struct libusb_container_id_descriptor **container_id); +void LIBUSB_CALL libusb_free_container_id_descriptor( + struct libusb_container_id_descriptor *container_id); +uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev); +int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t *port_numbers, int port_numbers_len); +LIBUSB_DEPRECATED_FOR(libusb_get_port_numbers) +int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t *path, uint8_t path_length); +libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev); +uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev); +int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev); +int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev, + unsigned char endpoint); +int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev, + unsigned char endpoint); + +int LIBUSB_CALL libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, libusb_device_handle **dev_handle); +int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **dev_handle); +void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle); +libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle); + +int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev_handle, + int configuration); +int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev_handle, + int interface_number); + +libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid( + libusb_context *ctx, uint16_t vendor_id, uint16_t product_id); + +int LIBUSB_CALL libusb_set_interface_alt_setting(libusb_device_handle *dev_handle, + int interface_number, int alternate_setting); +int LIBUSB_CALL libusb_clear_halt(libusb_device_handle *dev_handle, + unsigned char endpoint); +int LIBUSB_CALL libusb_reset_device(libusb_device_handle *dev_handle); + +int LIBUSB_CALL libusb_alloc_streams(libusb_device_handle *dev_handle, + uint32_t num_streams, unsigned char *endpoints, int num_endpoints); +int LIBUSB_CALL libusb_free_streams(libusb_device_handle *dev_handle, + unsigned char *endpoints, int num_endpoints); + +unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle, + size_t length); +int LIBUSB_CALL libusb_dev_mem_free(libusb_device_handle *dev_handle, + unsigned char *buffer, size_t length); + +int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle *dev_handle, + int interface_number); +int LIBUSB_CALL libusb_set_auto_detach_kernel_driver( + libusb_device_handle *dev_handle, int enable); + +/* async I/O */ + +/** \ingroup libusb_asyncio + * Get the data section of a control transfer. This convenience function is here + * to remind you that the data does not start until 8 bytes into the actual + * buffer, as the setup packet comes first. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns pointer to the first byte of the data section + */ +static inline unsigned char *libusb_control_transfer_get_data( + struct libusb_transfer *transfer) +{ + return transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; +} + +/** \ingroup libusb_asyncio + * Get the control setup packet of a control transfer. This convenience + * function is here to remind you that the control setup occupies the first + * 8 bytes of the transfer data buffer. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns a casted pointer to the start of the transfer data buffer + */ +static inline struct libusb_control_setup *libusb_control_transfer_get_setup( + struct libusb_transfer *transfer) +{ + return (struct libusb_control_setup *)(void *)transfer->buffer; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the setup packet (first 8 bytes of the data + * buffer) for a control transfer. The wIndex, wValue and wLength values should + * be given in host-endian byte order. + * + * \param buffer buffer to output the setup packet into + * This pointer must be aligned to at least 2 bytes boundary. + * \param bmRequestType see the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field of + * \ref libusb_control_setup + * \param bRequest see the + * \ref libusb_control_setup::bRequest "bRequest" field of + * \ref libusb_control_setup + * \param wValue see the + * \ref libusb_control_setup::wValue "wValue" field of + * \ref libusb_control_setup + * \param wIndex see the + * \ref libusb_control_setup::wIndex "wIndex" field of + * \ref libusb_control_setup + * \param wLength see the + * \ref libusb_control_setup::wLength "wLength" field of + * \ref libusb_control_setup + */ +static inline void libusb_fill_control_setup(unsigned char *buffer, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + uint16_t wLength) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *)buffer; + setup->bmRequestType = bmRequestType; + setup->bRequest = bRequest; + setup->wValue = libusb_cpu_to_le16(wValue); + setup->wIndex = libusb_cpu_to_le16(wIndex); + setup->wLength = libusb_cpu_to_le16(wLength); +} + +struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(int iso_packets); +int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer); +int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer); +void LIBUSB_CALL libusb_free_transfer(struct libusb_transfer *transfer); +void LIBUSB_CALL libusb_transfer_set_stream_id( + struct libusb_transfer *transfer, uint32_t stream_id); +uint32_t LIBUSB_CALL libusb_transfer_get_stream_id( + struct libusb_transfer *transfer); + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a control transfer. + * + * If you pass a transfer buffer to this function, the first 8 bytes will + * be interpreted as a control setup packet, and the wLength field will be + * used to automatically populate the \ref libusb_transfer::length "length" + * field of the transfer. Therefore the recommended approach is: + * -# Allocate a suitably sized data buffer (including space for control setup) + * -# Call libusb_fill_control_setup() + * -# If this is a host-to-device transfer with a data stage, put the data + * in place after the setup packet + * -# Call this function + * -# Call libusb_submit_transfer() + * + * It is also legal to pass a NULL buffer to this function, in which case this + * function will not attempt to populate the length field. Remember that you + * must then populate the buffer and length fields later. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param buffer data buffer. If provided, this function will interpret the + * first 8 bytes as a setup packet and infer the transfer length from that. + * This pointer must be aligned to at least 2 bytes boundary. + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_control_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data, + unsigned int timeout) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *)buffer; + transfer->dev_handle = dev_handle; + transfer->endpoint = 0; + transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; + transfer->timeout = timeout; + transfer->buffer = buffer; + if (setup) + transfer->length = (int) (LIBUSB_CONTROL_SETUP_SIZE + + libusb_le16_to_cpu(setup->wLength)); + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a bulk transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_BULK; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a bulk transfer using bulk streams. + * + * Since version 1.0.19, \ref LIBUSB_API_VERSION >= 0x01000103 + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param stream_id bulk stream id for this transfer + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_bulk_stream_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char endpoint, uint32_t stream_id, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, buffer, + length, callback, user_data, timeout); + transfer->type = LIBUSB_TRANSFER_TYPE_BULK_STREAM; + libusb_transfer_set_stream_id(transfer, stream_id); +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an interrupt transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_interrupt_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an isochronous transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param num_iso_packets the number of isochronous packets + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, int num_iso_packets, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->num_iso_packets = num_iso_packets; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup libusb_asyncio + * Convenience function to set the length of all packets in an isochronous + * transfer, based on the num_iso_packets field in the transfer structure. + * + * \param transfer a transfer + * \param length the length to set in each isochronous packet descriptor + * \see libusb_get_max_packet_size() + */ +static inline void libusb_set_iso_packet_lengths( + struct libusb_transfer *transfer, unsigned int length) +{ + int i; + + for (i = 0; i < transfer->num_iso_packets; i++) + transfer->iso_packet_desc[i].length = length; +} + +/** \ingroup libusb_asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer. + * + * This is a thorough function which loops through all preceding packets, + * accumulating their lengths to find the position of the specified packet. + * Typically you will assign equal lengths to each packet in the transfer, + * and hence the above method is sub-optimal. You may wish to use + * libusb_get_iso_packet_buffer_simple() instead. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer_simple() + */ +static inline unsigned char *libusb_get_iso_packet_buffer( + struct libusb_transfer *transfer, unsigned int packet) +{ + int i; + size_t offset = 0; + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = (int) packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + for (i = 0; i < _packet; i++) + offset += transfer->iso_packet_desc[i].length; + + return transfer->buffer + offset; +} + +/** \ingroup libusb_asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer, for transfers where each + * packet is of identical size. + * + * This function relies on the assumption that every packet within the transfer + * is of identical size to the first packet. Calculating the location of + * the packet buffer is then just a simple calculation: + * buffer + (packet_size * packet) + * + * Do not use this function on transfers other than those that have identical + * packet lengths for each packet. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer() + */ +static inline unsigned char *libusb_get_iso_packet_buffer_simple( + struct libusb_transfer *transfer, unsigned int packet) +{ + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = (int) packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + return transfer->buffer + ((int) transfer->iso_packet_desc[0].length * _packet); +} + +/* sync I/O */ + +int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle, + uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout); + +int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +/** \ingroup libusb_desc + * Retrieve a descriptor from the default control pipe. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. + * + * \param dev_handle a device handle + * \param desc_type the descriptor type, see \ref libusb_descriptor_type + * \param desc_index the index of the descriptor to retrieve + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + */ +static inline int libusb_get_descriptor(libusb_device_handle *dev_handle, + uint8_t desc_type, uint8_t desc_index, unsigned char *data, int length) +{ + return libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t) ((desc_type << 8) | desc_index), + 0, data, (uint16_t) length, 1000); +} + +/** \ingroup libusb_desc + * Retrieve a descriptor from a device. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. The string returned is Unicode, as + * detailed in the USB specifications. + * + * \param dev_handle a device handle + * \param desc_index the index of the descriptor to retrieve + * \param langid the language ID for the string descriptor + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + * \see libusb_get_string_descriptor_ascii() + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev_handle, + uint8_t desc_index, uint16_t langid, unsigned char *data, int length) +{ + return libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (uint16_t)((LIBUSB_DT_STRING << 8) | desc_index), + langid, data, (uint16_t) length, 1000); +} + +int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle, + uint8_t desc_index, unsigned char *data, int length); + +/* polling and timeouts */ + +int LIBUSB_CALL libusb_try_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_events(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_events(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handling_ok(libusb_context *ctx); +int LIBUSB_CALL libusb_event_handler_active(libusb_context *ctx); +void LIBUSB_CALL libusb_interrupt_event_handler(libusb_context *ctx); +void LIBUSB_CALL libusb_lock_event_waiters(libusb_context *ctx); +void LIBUSB_CALL libusb_unlock_event_waiters(libusb_context *ctx); +int LIBUSB_CALL libusb_wait_for_event(libusb_context *ctx, struct timeval *tv); + +int LIBUSB_CALL libusb_handle_events_timeout(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_handle_events_timeout_completed(libusb_context *ctx, + struct timeval *tv, int *completed); +int LIBUSB_CALL libusb_handle_events(libusb_context *ctx); +int LIBUSB_CALL libusb_handle_events_completed(libusb_context *ctx, int *completed); +int LIBUSB_CALL libusb_handle_events_locked(libusb_context *ctx, + struct timeval *tv); +int LIBUSB_CALL libusb_pollfds_handle_timeouts(libusb_context *ctx); +int LIBUSB_CALL libusb_get_next_timeout(libusb_context *ctx, + struct timeval *tv); + +/** \ingroup libusb_poll + * File descriptor for polling + */ +struct libusb_pollfd { + /** Numeric file descriptor */ + int fd; + + /** Event flags to poll for from . POLLIN indicates that you + * should monitor this file descriptor for becoming ready to read from, + * and POLLOUT indicates that you should monitor this file descriptor for + * nonblocking write readiness. */ + short events; +}; + +/** \ingroup libusb_poll + * Callback function, invoked when a new file descriptor should be added + * to the set of file descriptors monitored for events. + * \param fd the new file descriptor + * \param events events to monitor for, see \ref libusb_pollfd for a + * description + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_added_cb)(int fd, short events, + void *user_data); + +/** \ingroup libusb_poll + * Callback function, invoked when a file descriptor should be removed from + * the set of file descriptors being monitored for events. After returning + * from this callback, do not use that file descriptor again. + * \param fd the file descriptor to stop monitoring + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (LIBUSB_CALL *libusb_pollfd_removed_cb)(int fd, void *user_data); + +const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds( + libusb_context *ctx); +void LIBUSB_CALL libusb_free_pollfds(const struct libusb_pollfd **pollfds); +void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx, + libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, + void *user_data); + +/** \ingroup libusb_hotplug + * Callback handle. + * + * Callbacks handles are generated by libusb_hotplug_register_callback() + * and can be used to deregister callbacks. Callback handles are unique + * per libusb_context and it is safe to call libusb_hotplug_deregister_callback() + * on an already deregistered callback. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * For more information, see \ref libusb_hotplug. + */ +typedef int libusb_hotplug_callback_handle; + +/** \ingroup libusb_hotplug + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * Hotplug events */ +typedef enum { + /** A device has been plugged in and is ready to use */ + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = (1 << 0), + + /** A device has left and is no longer available. + * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device. + * It is safe to call libusb_get_device_descriptor on a device that has left */ + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = (1 << 1) +} libusb_hotplug_event; + +/** \ingroup libusb_hotplug + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * Hotplug flags */ +typedef enum { + /** Arm the callback and fire it for all matching currently attached devices. */ + LIBUSB_HOTPLUG_ENUMERATE = (1 << 0) +} libusb_hotplug_flag; + +/** \ingroup libusb_hotplug + * Convenience macro when not using any flags */ +#define LIBUSB_HOTPLUG_NO_FLAGS 0 + +/** \ingroup libusb_hotplug + * Wildcard matching for hotplug events */ +#define LIBUSB_HOTPLUG_MATCH_ANY -1 + +/** \ingroup libusb_hotplug + * Hotplug callback function type. When requesting hotplug event notifications, + * you pass a pointer to a callback function of this type. + * + * This callback may be called by an internal event thread and as such it is + * recommended the callback do minimal processing before returning. + * + * libusb will call this function later, when a matching event had happened on + * a matching device. See \ref libusb_hotplug for more information. + * + * It is safe to call either libusb_hotplug_register_callback() or + * libusb_hotplug_deregister_callback() from within a callback function. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * \param ctx context of this notification + * \param device libusb_device this event occurred on + * \param event event that occurred + * \param user_data user data provided when this callback was registered + * \returns bool whether this callback is finished processing events. + * returning 1 will cause this callback to be deregistered + */ +typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx, + libusb_device *device, libusb_hotplug_event event, void *user_data); + +/** \ingroup libusb_hotplug + * Register a hotplug callback function + * + * Register a callback with the libusb_context. The callback will fire + * when a matching event occurs on a matching device. The callback is + * armed until either it is deregistered with libusb_hotplug_deregister_callback() + * or the supplied callback returns 1 to indicate it is finished processing events. + * + * If the \ref LIBUSB_HOTPLUG_ENUMERATE is passed the callback will be + * called with a \ref LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED for all devices + * already plugged into the machine. Note that libusb modifies its internal + * device list from a separate thread, while calling hotplug callbacks from + * libusb_handle_events(), so it is possible for a device to already be present + * on, or removed from, its internal device list, while the hotplug callbacks + * still need to be dispatched. This means that when using \ref + * LIBUSB_HOTPLUG_ENUMERATE, your callback may be called twice for the arrival + * of the same device, once from libusb_hotplug_register_callback() and once + * from libusb_handle_events(); and/or your callback may be called for the + * removal of a device for which an arrived call was never made. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * \param[in] ctx context to register this callback with + * \param[in] events bitwise or of hotplug events that will trigger this callback. + * See \ref libusb_hotplug_event + * \param[in] flags bitwise or of hotplug flags that affect registration. + * See \ref libusb_hotplug_flag + * \param[in] vendor_id the vendor id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] product_id the product id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] dev_class the device class to match or \ref LIBUSB_HOTPLUG_MATCH_ANY + * \param[in] cb_fn the function to be invoked on a matching event/device + * \param[in] user_data user data to pass to the callback function + * \param[out] callback_handle pointer to store the handle of the allocated callback (can be NULL) + * \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure + */ +int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx, + int events, int flags, + int vendor_id, int product_id, int dev_class, + libusb_hotplug_callback_fn cb_fn, void *user_data, + libusb_hotplug_callback_handle *callback_handle); + +/** \ingroup libusb_hotplug + * Deregisters a hotplug callback. + * + * Deregister a callback from a libusb_context. This function is safe to call from within + * a hotplug callback. + * + * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 + * + * \param[in] ctx context this callback is registered with + * \param[in] callback_handle the handle of the callback to deregister + */ +void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx, + libusb_hotplug_callback_handle callback_handle); + +/** \ingroup libusb_hotplug + * Gets the user_data associated with a hotplug callback. + * + * Since version v1.0.24 \ref LIBUSB_API_VERSION >= 0x01000108 + * + * \param[in] ctx context this callback is registered with + * \param[in] callback_handle the handle of the callback to get the user_data of + */ +void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx, + libusb_hotplug_callback_handle callback_handle); + +/** \ingroup libusb_lib + * Available option values for libusb_set_option(). + */ +enum libusb_option { + /** Set the log message verbosity. + * + * The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever + * printed. If you choose to increase the message verbosity level, ensure + * that your application does not close the stderr file descriptor. + * + * You are advised to use level LIBUSB_LOG_LEVEL_WARNING. libusb is conservative + * with its message logging and most of the time, will only log messages that + * explain error conditions and other oddities. This will help you debug + * your software. + * + * If the LIBUSB_DEBUG environment variable was set when libusb was + * initialized, this function does nothing: the message verbosity is fixed + * to the value in the environment variable. + * + * If libusb was compiled without any message logging, this function does + * nothing: you'll never get any messages. + * + * If libusb was compiled with verbose debug message logging, this function + * does nothing: you'll always get messages from all levels. + */ + LIBUSB_OPTION_LOG_LEVEL = 0, + + /** Use the UsbDk backend for a specific context, if available. + * + * This option should be set immediately after calling libusb_init(), otherwise + * unspecified behavior may occur. + * + * Only valid on Windows. + */ + LIBUSB_OPTION_USE_USBDK = 1, + + /** Set libusb has weak authority. With this option, libusb will skip + * scan devices in libusb_init. + * + * This option should be set before calling libusb_init(), otherwise + * libusb_init will failed. Normally libusb_wrap_sys_device need set + * this option. + * + * Only valid on Linux-based operating system, such as Android. + */ + LIBUSB_OPTION_WEAK_AUTHORITY = 2 +}; + +int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/Sming/Arch/Rp2040/Components/picotool/picotool b/Sming/Arch/Rp2040/Components/picotool/picotool new file mode 160000 index 0000000000..5d6df39033 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/picotool/picotool @@ -0,0 +1 @@ +Subproject commit 5d6df39033c7166ea6e76cbde1ff0c0240a4bba4 diff --git a/Sming/Arch/Rp2040/Components/rp2040/Kconfig b/Sming/Arch/Rp2040/Components/rp2040/Kconfig new file mode 100644 index 0000000000..3c7a01b401 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/Kconfig @@ -0,0 +1,15 @@ +menu "RP2040 Core" + config PICO_BOARD + bool "Development board in use" + default pico + help + If using custom hardware, select ``none``. + List available boards with ``make board-list``. + + config ENABLE_BOOTSEL + bool "Enable BOOTSEL" + default y if SMING_RELEASE + help + Monitor BOOTSEL button and restart in boot mode if pressed. + Avoids need to power-cycle the board to re-program. +endmenu diff --git a/Sming/Arch/Rp2040/Components/rp2040/README.rst b/Sming/Arch/Rp2040/Components/rp2040/README.rst new file mode 100644 index 0000000000..d42399f685 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/README.rst @@ -0,0 +1,30 @@ +RP2040 Core Component +===================== + +.. highlight:: bash + +Contains startup code, crash handling and additional RP2040-specific support code. + +Configuration variables +----------------------- + +.. envvar:: PICO_BOARD + + default: pico + + Select development board in use. + List availlable boards with ``make list-boards``. + + The SDK defines various useful bits of information in a board header file, + such as the default LED pin, how much flash memory it has, etc. + Use ``make board-info`` to list these values. + + If using custom hardware, select ``none`` and provide definitions as required. + + +.. envvar:: ENABLE_BOOTSEL + + default: 1 for debug, 0 for release builds + + This setting is provided to make it easy to re-program RP2040 boards during development. + When enabled, Sming monitors the BOOTSEL button and restartS in boot mode if pressed. diff --git a/Sming/Arch/Rp2040/Components/rp2040/board-info.awk b/Sming/Arch/Rp2040/Components/rp2040/board-info.awk new file mode 100644 index 0000000000..6965e8113e --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/board-info.awk @@ -0,0 +1,27 @@ +# Scan RP2040 SDK board header file and extract variables for use in makefiles +BEGIN { + FS = "#define " +} +/^#define +[^_]+/ { + n = index($2, " ") + if (n == 0) { + vars[$2] = "" + } else { + name = substr($2, 1, n-1) + value = substr($2, n + 1) + if (index(value, " ") == 0) { + vars[name] = value + } else { + vars[name] = "$$((" value "))" + } + } +} +END { + for (v in vars) { + printf "%s=%s\\n", v, vars[v] + } + printf "RP2040_BOARD_VARS := " + for (v in vars) { + printf v " " + } +} diff --git a/Sming/Arch/Rp2040/Components/rp2040/component.mk b/Sming/Arch/Rp2040/Components/rp2040/component.mk new file mode 100644 index 0000000000..26afa42f40 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/component.mk @@ -0,0 +1,162 @@ +COMPONENT_SUBMODULES := pico-sdk + +ifdef PICO_SDK_PATH +export PICO_SDK_PATH := $(call FixPath,$(PICO_SDK_PATH)) +else +export PICO_SDK_PATH := $(COMPONENT_PATH)/pico-sdk +endif + +COMPONENT_RELINK_VARS += PICO_BOARD +ifndef PICO_BOARD +export PICO_BOARD=pico +endif + +# Press BOOTSEL to reboot into programming mode +COMPONENT_RELINK_VARS := ENABLE_BOOTSEL +ifndef SMING_RELEASE +ENABLE_BOOTSEL ?= 1 +endif +ifeq ($(ENABLE_BOOTSEL),1) +COMPONENT_CXXFLAGS += -DENABLE_BOOTSEL=1 +endif + +WRAPPED_FUNCTIONS := + +$(foreach c,$(wildcard $(COMPONENT_PATH)/sdk/*.mk),$(eval include $c)) + +EXTRA_LDFLAGS := \ + $(call Wrap,$(WRAPPED_FUNCTIONS)) \ + -T memmap_default.ld + +SDK_INTERFACES := \ + boards \ + common/pico_base \ + common/pico_binary_info \ + common/pico_bit_ops \ + common/pico_divider \ + common/pico_sync \ + common/pico_util \ + rp2040/hardware_regs \ + rp2040/hardware_structs \ + rp2_common/hardware_gpio \ + rp2_common/pico_platform \ + rp2_common/hardware_base \ + rp2_common/hardware_sync \ + rp2_common/hardware_divider \ + rp2_common/hardware_timer \ + rp2_common/hardware_clocks \ + rp2_common/hardware_dma \ + rp2_common/hardware_exception \ + rp2_common/hardware_flash \ + rp2_common/hardware_irq \ + rp2_common/hardware_resets \ + rp2_common/hardware_rosc \ + rp2_common/hardware_rtc \ + rp2_common/hardware_pll \ + rp2_common/hardware_vreg \ + rp2_common/hardware_watchdog \ + rp2_common/hardware_xosc \ + rp2_common/pico_bootrom \ + rp2_common/pico_double \ + rp2_common/pico_int64_ops \ + rp2_common/pico_float \ + rp2_common/pico_runtime \ + rp2_common/pico_unique_id + +COMPONENT_INCDIRS := \ + src/include \ + $(foreach p,$(SDK_INTERFACES),$(PICO_SDK_PATH)/src/$p/include) + + +COMPONENT_SRCDIRS := src + +RP2040_COMPONENT_DIR := $(COMPONENT_PATH) +PICO_BUILD_DIR := $(COMPONENT_BUILD_BASE)/sdk +PICO_BASE_DIR := $(PICO_BUILD_DIR)/generated/pico_base +PICO_CONFIG := $(PICO_BASE_DIR)/pico/config_autogen.h +PICO_LIB := $(PICO_BUILD_DIR)/libpico.a + +COMPONENT_INCDIRS += $(PICO_BASE_DIR) + +LIBDIRS += \ + $(PICO_SDK_PATH)/src/rp2_common/pico_standard_link \ + $(PICO_BUILD_DIR) + +EXTRA_LIBS += \ + pico + +ifdef NINJA +NINJA := $(call FixPath,$(NINJA)) +else +NINJA := ninja +endif +RP2040_CMAKE_OPTIONS := \ + -G Ninja \ + -DCMAKE_MAKE_PROGRAM=$(NINJA) + +COMPONENT_PREREQUISITES := $(PICO_CONFIG) + +BOOTLOADER := $(PICO_BUILD_DIR)/pico-sdk/src/rp2_common/boot_stage2/bs2_default_padded_checksummed.S + +COMPONENT_TARGETS := \ + $(PICO_LIB) + +$(PICO_CONFIG): $(PICO_BUILD_DIR) + $(Q) cd $(PICO_BUILD_DIR) && $(CMAKE) $(RP2040_CMAKE_OPTIONS) $(RP2040_COMPONENT_DIR)/sdk + +$(COMPONENT_RULE)$(PICO_LIB): + $(Q) cd $(@D) && $(NINJA) + +ifdef COMPONENT_RULE +$(PICO_BUILD_DIR): + $(Q) mkdir -p $@ +endif + + +# Pull in board configuration, specifically PICO_FLASH_SIZE_BYTES +# It's only available in a .h file so needs some parsing +RP2040_BOARD_PATH := $(PICO_SDK_PATH)/src/boards/include/boards +RP2040_BOARD_HEADERS = $(notdir $(wildcard $(RP2040_BOARD_PATH)/*.h)) +RP2040_BOARDS = $(RP2040_BOARD_HEADERS:.h=) + +# Used to un-escape newlines from AWK script +define NewLine + + +endef + +BOARD_HEADER_PATH := $(PICO_SDK_PATH)/src/boards/include/boards/$(PICO_BOARD).h + +ifneq (,$(wildcard $(BOARD_HEADER_PATH))) +$(eval $(subst \n,$(NewLine),$(shell $(AWK) -f $(RP2040_COMPONENT_DIR)/board-info.awk $(BOARD_HEADER_PATH)))) +endif + +DEBUG_VARS += $(RP2040_BOARD_VARS) + + +CUSTOM_TARGETS += check-flash-size + +.PHONY: check-flash-size +check-flash-size: + @if [ $$(( $(STORAGE_DEVICE_spiFlash_SIZE_BYTES) - $(PICO_FLASH_SIZE_BYTES) )) -gt 0 ]; then \ + echo; \ + echo "ERROR: Hardware config spiFlash size exceeds value board definition"; \ + echo "PICO_FLASH_SIZE_BYTES = $(PICO_FLASH_SIZE_BYTES)"; \ + echo "spiFlash.size = $(STORAGE_DEVICE_spiFlash_SIZE_BYTES)"; \ + echo; \ + echo "Please fix your hardware configuration!"; \ + echo; \ + exit 1; \ + fi + + +##@Help + +.PHONY: list-boards +list-boards: ##List development boards defined in SDK + $(call PrintVariable,RP2040_BOARDS) + + +.PHONY: board-info +board-info: ##List RP2040 board configuration + $(call PrintVariableRefs,RP2040_BOARD_VARS) diff --git a/Sming/Arch/Rp2040/Components/rp2040/pico-sdk b/Sming/Arch/Rp2040/Components/rp2040/pico-sdk new file mode 160000 index 0000000000..bfcbefafc5 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/pico-sdk @@ -0,0 +1 @@ +Subproject commit bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7 diff --git a/Sming/Arch/Host/Components/fatfs/ff.h b/Sming/Arch/Rp2040/Components/rp2040/pico-sdk.no-recursive similarity index 100% rename from Sming/Arch/Host/Components/fatfs/ff.h rename to Sming/Arch/Rp2040/Components/rp2040/pico-sdk.no-recursive diff --git a/Sming/Arch/Rp2040/Components/rp2040/pico-sdk.patch b/Sming/Arch/Rp2040/Components/rp2040/pico-sdk.patch new file mode 100644 index 0000000000..405be571b1 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/pico-sdk.patch @@ -0,0 +1,13 @@ +diff --git a/src/rp2_common/pico_platform/include/pico/platform.h b/src/rp2_common/pico_platform/include/pico/platform.h +index c008b95..bf5a690 100644 +--- a/src/rp2_common/pico_platform/include/pico/platform.h ++++ b/src/rp2_common/pico_platform/include/pico/platform.h +@@ -146,7 +146,7 @@ static inline void tight_loop_contents(void) {} + * \return a * b + */ + __force_inline static int32_t __mul_instruction(int32_t a, int32_t b) { +- asm ("mul %0, %1" : "+l" (a) : "l" (b) : ); ++ __asm__ ("mul %0, %1" : "+l" (a) : "l" (b) : ); + return a; + } + diff --git a/Sming/Arch/Rp2040/Components/rp2040/sdk/CMakeLists.txt b/Sming/Arch/Rp2040/Components/rp2040/sdk/CMakeLists.txt new file mode 100644 index 0000000000..739b172780 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/sdk/CMakeLists.txt @@ -0,0 +1,76 @@ +cmake_minimum_required(VERSION 3.12) + +set(PICO_PLATFORM "rp2040") +set(PICO_COMPILER "pico_arm_gcc") + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_BUILD_TYPE RelWithDebInfo) + +include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake) +# include(pico_extras_import.cmake) + +project(pico_lib C CXX ASM) + +# Don't want these interfaces +set(SKIP_PICO_MALLOC 1) +set(SKIP_PICO_STDIO 1) +set(SKIP_PICO_STDIO_SEMIHOSTING 1) +set(SKIP_PICO_STDIO_UART 1) +set(SKIP_PICO_STDIO_USB 1) +set(SKIP_TINYUSB 1) + +# Initialize the SDK +pico_sdk_init() + +add_library(pico STATIC) + +# Use a longer XOSC startup time, to accommodate Adafruit and other boards that may need it. +target_compile_definitions(pico PUBLIC + PICO_PRINTF_ALWAYS_INCLUDED=1 + PICO_FLASH_SIZE_BYTES=16777216 + PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64 +) + +pico_set_program_name(pico "Sming") +pico_set_program_url(pico "https://github.com/SmingHub/Sming") + +include_directories(BEFORE ${pico_lib_SOURCE_DIR}) + +target_link_libraries(pico + hardware_adc + hardware_base + hardware_claim + hardware_clocks + hardware_divider + hardware_dma + hardware_exception + hardware_flash + hardware_gpio + hardware_i2c + hardware_interp + hardware_irq + hardware_pio + hardware_pll + hardware_pwm + hardware_resets + hardware_rtc + hardware_spi + hardware_sync + hardware_timer + hardware_uart + hardware_vreg + hardware_watchdog + hardware_xosc + pico_bit_ops + pico_divider + pico_double + pico_fix + pico_float + pico_int64_ops + pico_mem_ops + pico_runtime + pico_standard_link + pico_unique_id + pico_audio_i2s +) diff --git a/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_bit_ops.mk b/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_bit_ops.mk new file mode 100644 index 0000000000..a0ec0d43df --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_bit_ops.mk @@ -0,0 +1,9 @@ +# pico_bit_ops + +WRAPPED_FUNCTIONS += \ + __clzsi2 \ + __clzdi2 \ + __ctzsi2 \ + __ctzdi2 \ + __popcountsi2 \ + __popcountdi2 diff --git a/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_divider.mk b/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_divider.mk new file mode 100644 index 0000000000..8578a4ccf2 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_divider.mk @@ -0,0 +1,9 @@ +# pico_divider + +WRAPPED_FUNCTIONS += \ + __aeabi_idiv \ + __aeabi_idivmod \ + __aeabi_ldivmod \ + __aeabi_uidiv \ + __aeabi_uidivmod \ + __aeabi_uldivmod diff --git a/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_double.mk b/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_double.mk new file mode 100644 index 0000000000..765ee5f469 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_double.mk @@ -0,0 +1,64 @@ +# pico_double + +WRAPPED_FUNCTIONS += \ + __aeabi_dadd \ + __aeabi_ddiv \ + __aeabi_dmul \ + __aeabi_drsub \ + __aeabi_dsub \ + __aeabi_cdcmpeq \ + __aeabi_cdrcmple \ + __aeabi_cdcmple \ + __aeabi_dcmpeq \ + __aeabi_dcmplt \ + __aeabi_dcmple \ + __aeabi_dcmpge \ + __aeabi_dcmpgt \ + __aeabi_dcmpun \ + __aeabi_i2d \ + __aeabi_l2d \ + __aeabi_ui2d \ + __aeabi_ul2d \ + __aeabi_d2iz \ + __aeabi_d2lz \ + __aeabi_d2uiz \ + __aeabi_d2ulz \ + __aeabi_d2f \ + sqrt \ + cos \ + sin \ + tan \ + atan2 \ + exp \ + log \ + ldexp \ + copysign \ + trunc \ + floor \ + ceil \ + round \ + sincos \ + asin \ + acos \ + atan \ + sinh \ + cosh \ + tanh \ + asinh \ + acosh \ + atanh \ + exp2 \ + log2 \ + exp10 \ + log10 \ + pow \ + powint \ + hypot \ + cbrt \ + fmod \ + drem \ + remainder \ + remquo \ + expm1 \ + log1p \ + fma diff --git a/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_float.mk b/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_float.mk new file mode 100644 index 0000000000..e45345f639 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_float.mk @@ -0,0 +1,64 @@ +# pico_float + +WRAPPED_FUNCTIONS += \ + __aeabi_fadd \ + __aeabi_fdiv \ + __aeabi_fmul \ + __aeabi_frsub \ + __aeabi_fsub \ + __aeabi_cfcmpeq \ + __aeabi_cfrcmple \ + __aeabi_cfcmple \ + __aeabi_fcmpeq \ + __aeabi_fcmplt \ + __aeabi_fcmple \ + __aeabi_fcmpge \ + __aeabi_fcmpgt \ + __aeabi_fcmpun \ + __aeabi_i2f \ + __aeabi_l2f \ + __aeabi_ui2f \ + __aeabi_ul2f \ + __aeabi_f2iz \ + __aeabi_f2lz \ + __aeabi_f2uiz \ + __aeabi_f2ulz \ + __aeabi_f2d \ + sqrtf \ + cosf \ + sinf \ + tanf \ + atan2f \ + expf \ + logf \ + ldexpf \ + copysignf \ + truncf \ + floorf \ + ceilf \ + roundf \ + sincosf \ + asinf \ + acosf \ + atanf \ + sinhf \ + coshf \ + tanhf \ + asinhf \ + acoshf \ + atanhf \ + exp2f \ + log2f \ + exp10f \ + log10f \ + powf \ + powintf \ + hypotf \ + cbrtf \ + fmodf \ + dremf \ + remainderf \ + remquof \ + expm1f \ + log1pf \ + fmaf diff --git a/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_int64_ops.mk b/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_int64_ops.mk new file mode 100644 index 0000000000..83f39e1492 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_int64_ops.mk @@ -0,0 +1,4 @@ +# pico_int64_ops + +WRAPPED_FUNCTIONS += \ + __aeabi_lmul diff --git a/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_mem_ops.mk b/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_mem_ops.mk new file mode 100644 index 0000000000..6244c18b5d --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_mem_ops.mk @@ -0,0 +1,11 @@ +# pico_mem_ops + +WRAPPED_FUNCTIONS += \ + memcpy \ + memset \ + __aeabi_memcpy \ + __aeabi_memset \ + __aeabi_memcpy4 \ + __aeabi_memset4 \ + __aeabi_memcpy8 \ + __aeabi_memset8 diff --git a/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_stdio.mk b/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_stdio.mk new file mode 100644 index 0000000000..aaa395e7bc --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/sdk/pico_stdio.mk @@ -0,0 +1,8 @@ +# pico_stdio + +WRAPPED_FUNCTIONS += \ + printf \ + vprintf \ + puts \ + putchar \ + getchar diff --git a/Sming/Arch/Rp2040/Components/rp2040/sdk/tusb_config.h b/Sming/Arch/Rp2040/Components/rp2040/sdk/tusb_config.h new file mode 100644 index 0000000000..0c98598d40 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/sdk/tusb_config.h @@ -0,0 +1,92 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * 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 the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUSB_MCU + #define CFG_TUSB_MCU OPT_MCU_RP2040 +#endif + +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE +#define CFG_TUSB_OS OPT_OS_PICO + +// CFG_TUSB_DEBUG is defined by compiler in DEBUG build +#ifndef CFG_TUSB_DEBUG +#define CFG_TUSB_DEBUG 0 +#endif + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_HID (2) +#define CFG_TUD_CDC (1) +#define CFG_TUD_MSC (0) +#define CFG_TUD_MIDI (1) +#define CFG_TUD_VENDOR (0) + +#define CFG_TUD_CDC_RX_BUFSIZE (256) +#define CFG_TUD_CDC_TX_BUFSIZE (256) + +#define CFG_TUD_MIDI_RX_BUFSIZE (64) +#define CFG_TUD_MIDI_TX_BUFSIZE (64) + +// HID buffer size Should be sufficient to hold ID (if any) + Data +#define CFG_TUD_HID_EP_BUFSIZE (64) + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/Sming/Arch/Rp2040/Components/rp2040/src/clk.c b/Sming/Arch/Rp2040/Components/rp2040/src/clk.c new file mode 100644 index 0000000000..f992cf989b --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/src/clk.c @@ -0,0 +1,116 @@ +#include "include/esp_clk.h" +#include "include/esp_system.h" +#include +#include +#include +#include + +#define DEFAULT_CPU_FREQ (125 * MHZ) + +static volatile uint32_t systick_overflow; + +static void IRAM_ATTR systick_overflow_isr() +{ + ++systick_overflow; +} + +uint32_t IRAM_ATTR esp_get_ccount() +{ + extern volatile uint32_t systick_overflow; + uint32_t ovf = systick_overflow; + if(ovf != systick_overflow) { + ovf = systick_overflow; + } + return systick_hw->cvr | (ovf << 24); +} + +/*! \brief Check if a given system clock frequency is valid/attainable + * \ingroup pico_stdlib + * + * \param freq_khz Requested frequency + * \param vco_freq_out On success, the voltage controller oscillator frequeucny to be used by the SYS PLL + * \param post_div1_out On success, The first post divider for the SYS PLL + * \param post_div2_out On success, The second post divider for the SYS PLL. + * @return true if the frequency is possible and the output parameters have been written. + */ +static bool check_sys_clock_khz(uint32_t freq_khz, uint* vco_out, uint* postdiv1_out, uint* postdiv_out) +{ + uint crystal_freq_khz = clock_get_hz(clk_ref) / 1000; + for(uint fbdiv = 320; fbdiv >= 16; fbdiv--) { + uint vco = fbdiv * crystal_freq_khz; + if(vco < 400000 || vco > 1600000) { + continue; + } + for(uint postdiv1 = 7; postdiv1 >= 1; postdiv1--) { + for(uint postdiv2 = postdiv1; postdiv2 >= 1; postdiv2--) { + uint out = vco / (postdiv1 * postdiv2); + if(out == freq_khz && (vco % (postdiv1 * postdiv2)) == 0) { + *vco_out = vco * 1000; + *postdiv1_out = postdiv1; + *postdiv_out = postdiv2; + return true; + } + } + } + } + return false; +} + +// Fix the peripheral clocks but allow system (CPU) to be varied independently using PLL +void system_init_clocks() +{ + uint vco_freq, post_div1, post_div2; + check_sys_clock_khz(DEFAULT_CPU_FREQ / 1000U, &vco_freq, &post_div1, &post_div2); + + // Switch to stable system clock before enabling PLL + clock_configure(clk_sys, CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX, + CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, 48 * MHZ, 48 * MHZ); + + pll_init(pll_sys, 1, vco_freq, post_div1, post_div2); + uint32_t freq = vco_freq / (post_div1 * post_div2); + + // CLK_REF = XOSC (12MHz) / 1 = 12MHz + clock_configure(clk_ref, CLOCKS_CLK_REF_CTRL_SRC_VALUE_XOSC_CLKSRC, + 0, // No aux mux + 12 * MHZ, 12 * MHZ); + + // CLK SYS = PLL SYS (125MHz) / 1 = 125MHz + clock_configure(clk_sys, CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX, + CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, freq, freq); + + clock_configure(clk_peri, + 0, // Only AUX mux on ADC + CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, 48 * MHZ, 48 * MHZ); + + // Initialise systick for use by esp_get_ccount() + exception_set_exclusive_handler(SYSTICK_EXCEPTION, systick_overflow_isr); + systick_hw->csr = (1 << M0PLUS_SYST_CSR_CLKSOURCE_LSB) // Processor CLK source + | M0PLUS_SYST_CSR_TICKINT_BITS // Enable overflow ISR + | M0PLUS_SYST_CSR_ENABLE_BITS; // ENABLE + systick_hw->rvr = M0PLUS_SYST_RVR_BITS; // Reload value when counter hits 0 +} + +bool system_update_cpu_freq(uint8_t mhz) +{ + uint vco_freq, post_div1, post_div2; + if(!check_sys_clock_khz(mhz * 1000U, &vco_freq, &post_div1, &post_div2)) { + return false; + } + + // Switch to stable system clock before messing with PLL + clock_configure(clk_sys, CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX, + CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB, 48 * MHZ, 48 * MHZ); + + pll_init(pll_sys, 1, vco_freq, post_div1, post_div2); + uint32_t freq = vco_freq / (post_div1 * post_div2); + + // CLK SYS = PLL SYS (125MHz) / 1 = 125MHz + clock_configure(clk_sys, CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX, + CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, freq, freq); + return true; +} + +uint8_t system_get_cpu_freq(void) +{ + return clock_get_hz(clk_sys) / MHZ; +} diff --git a/Sming/Arch/Rp2040/Components/rp2040/src/esp_cplusplus.cpp b/Sming/Arch/Rp2040/Components/rp2040/src/esp_cplusplus.cpp new file mode 100644 index 0000000000..add05b64be --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/src/esp_cplusplus.cpp @@ -0,0 +1,29 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * esp_cplusplus.cpp + * + ****/ + +#include +#include + +namespace std +{ +const nothrow_t nothrow; +} + +extern "C" void __cxa_pure_virtual(void) +{ + SYSTEM_ERROR("Bad pure_virtual_call"); + abort(); +} + +extern "C" void __cxa_deleted_virtual(void) +{ + SYSTEM_ERROR("Bad deleted_virtual_call"); + abort(); +} diff --git a/Sming/Arch/Rp2040/Components/rp2040/src/functexcept.cpp b/Sming/Arch/Rp2040/Components/rp2040/src/functexcept.cpp new file mode 100644 index 0000000000..7394848d03 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/src/functexcept.cpp @@ -0,0 +1,93 @@ +// Function-Based Exception Support -*- C++ -*- + +// Copyright (C) 2001-2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file bits/functexcept.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{exception} + * + * This header provides support for -fno-exceptions. + */ + +// +// ISO C++ 14882: 19.1 Exception classes +// + +#include + +#define THROW_HANDLER(name, ...) \ + void WEAK_ATTR __throw_##name(__VA_ARGS__) \ + { \ + while(1) { \ + } \ + } + +namespace std +{ +// Helper for exception objects in +THROW_HANDLER(bad_exception, void) + +// Helper for exception objects in +THROW_HANDLER(bad_alloc, void) + +// Helper for exception objects in +THROW_HANDLER(bad_cast, void) + +THROW_HANDLER(bad_typeid, void) + +// Helpers for exception objects in +THROW_HANDLER(logic_error, const char*) + +THROW_HANDLER(domain_error, const char*) + +THROW_HANDLER(invalid_argument, const char*) + +THROW_HANDLER(length_error, const char*) + +THROW_HANDLER(out_of_range, const char*) + +THROW_HANDLER(out_of_range_fmt, const char*, ...) + +THROW_HANDLER(runtime_error, const char*) + +THROW_HANDLER(range_error, const char*) + +THROW_HANDLER(overflow_error, const char*) + +THROW_HANDLER(underflow_error, const char*) + +// Helpers for exception objects in +THROW_HANDLER(ios_failure, const char*) + +THROW_HANDLER(ios_failure, const char*, int) + +// Helpers for exception objects in +THROW_HANDLER(system_error, int) + +// Helpers for exception objects in +THROW_HANDLER(future_error, int) + +// Helpers for exception objects in +THROW_HANDLER(bad_function_call) + +} // namespace std diff --git a/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_clk.h b/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_clk.h new file mode 100644 index 0000000000..7a775c0303 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_clk.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool system_update_cpu_freq(uint8_t freq); + +uint8_t system_get_cpu_freq(void); + +uint32_t esp_get_ccount(); + +#ifdef __cplusplus +} +#endif diff --git a/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_libc.h b/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_libc.h new file mode 100644 index 0000000000..26560f986c --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_libc.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +uint32_t os_random(void); +int os_get_random(uint8_t* buf, size_t len); + +void ets_install_putc1(void (*p)(char c)); +void system_set_os_print(bool onoff); + +#ifdef __cplusplus +} +#endif diff --git a/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_sleep.h b/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_sleep.h new file mode 100644 index 0000000000..f8df75a78f --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_sleep.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum sleep_type { + NONE_SLEEP_T = 0, + LIGHT_SLEEP_T, + MODEM_SLEEP_T, +}; + +void system_deep_sleep(uint32_t time_in_us); +bool system_deep_sleep_set_option(uint8_t option); + +#ifdef __cplusplus +} +#endif diff --git a/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_system.h b/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_system.h new file mode 100644 index 0000000000..f878937a48 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_system.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +uint32_t system_get_time(void); +void system_restart(void); + +void system_soft_wdt_stop(void); +void system_soft_wdt_restart(void); +void system_soft_wdt_feed(void); + +enum rst_reason { + REASON_DEFAULT_RST, + REASON_WDT_RST, + REASON_EXCEPTION_RST, + REASON_SOFT_WDT_RST, + REASON_SOFT_RESTART, + REASON_DEEP_SLEEP_AWAKE, + REASON_EXT_SYS_RST, +}; + +struct rst_info { + uint32_t reason; + uint32_t exccause; + uint32_t epc1; + uint32_t epc2; + uint32_t epc3; + uint32_t excvaddr; + uint32_t depc; +}; + +struct rst_info* system_get_rst_info(void); + +void os_delay_us(uint32_t us); + +const char* system_get_sdk_version(void); + +uint32_t system_get_chip_id(void); + +#ifdef __cplusplus +} +#endif diff --git a/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_systemapi.h b/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_systemapi.h new file mode 100644 index 0000000000..6478e19862 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_systemapi.h @@ -0,0 +1,51 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * esp_systemapi.h + * + ****/ + +#pragma once + +// Default types +#include +#include + +#include +#include +#include +#include +#include +#include +#include "esp_system.h" +#include + +#include "m_printf.h" +#include "debug_progmem.h" +#include "stringutil.h" + +#define BIT(nr) (1UL << (nr)) + +#define SYSTEM_ERROR(fmt, ...) debug_e("ERROR: " fmt "\r\n", ##__VA_ARGS__) + +extern void ets_wdt_enable(void); +extern void ets_wdt_disable(void); +extern void wdt_feed(void); + + +/** @brief Disable interrupts + * @retval Current interrupt level + * @note Hardware timer is unaffected if operating in non-maskable mode + */ +uint32_t noInterrupts(); + +/** @brief Enable interrupts +*/ +void interrupts(); + +/** @brief Restore interrupts to level saved from previous noInterrupts() call + */ +void restoreInterrupts(uint32_t level); diff --git a/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_tasks.h b/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_tasks.h new file mode 100644 index 0000000000..247aa9bf7a --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_tasks.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint32_t os_signal_t; +typedef uint32_t os_param_t; + +typedef struct { + os_signal_t sig; + os_param_t par; +} os_event_t; + +typedef enum { + USER_TASK_PRIO_0, + USER_TASK_PRIO_1, + USER_TASK_PRIO_2, + USER_TASK_PRIO_MAX, +} os_task_priority_t; + +typedef void (*os_task_t)(os_event_t* e); + +bool system_os_task(os_task_t callback, os_task_priority_t prio, os_event_t* events, uint8_t qlen); +bool system_os_post(os_task_priority_t prio, os_signal_t sig, os_param_t par); + +#ifdef __cplusplus +} +#endif + +#ifdef DRIVER_CODE_INIT +// Setup default task queues +void system_init_tasks(); + +// Hook function to process task queues +void system_service_tasks(); + +typedef void (*system_task_callback_t)(os_param_t param); + +bool system_queue_callback(system_task_callback_t callback, os_param_t param); +#endif diff --git a/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_tasks_ll.h b/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_tasks_ll.h new file mode 100644 index 0000000000..2d7eac0a4f --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/src/include/esp_tasks_ll.h @@ -0,0 +1,16 @@ +/* + * Used by drivers and system code + */ +#pragma once + +#include "esp_tasks.h" + +// Setup default task queues +void system_init_tasks(); + +// Hook function to process task queues +void system_service_tasks(); + +typedef void (*system_task_callback_t)(os_param_t param); + +bool system_queue_callback(system_task_callback_t callback, os_param_t param); diff --git a/Sming/Arch/Rp2040/Components/rp2040/src/libc.c b/Sming/Arch/Rp2040/Components/rp2040/src/libc.c new file mode 100644 index 0000000000..63ebf5ba47 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/src/libc.c @@ -0,0 +1,38 @@ + +#include "include/esp_libc.h" +#include + +/* Misc */ + +uint32_t os_random(void) +{ + uint32_t res = 0; + for(unsigned i = 0; i < 32; ++i) { + res <<= 1; + res |= rosc_hw->randombit; + } + return res; +} + +int os_get_random(uint8_t* buf, size_t len) +{ + while(len-- != 0) { + uint8_t res = 0; + for(unsigned i = 0; i < 8; ++i) { + res <<= 1; + res |= rosc_hw->randombit; + } + *buf++ = res; + } + return 0; +} + +void ets_install_putc1(void (*p)(char c)) +{ + // Not implemented +} + +void system_set_os_print(bool onoff) +{ + // Not implemented +} diff --git a/Sming/Arch/Rp2040/Components/rp2040/src/sleep.c b/Sming/Arch/Rp2040/Components/rp2040/src/sleep.c new file mode 100644 index 0000000000..6b6b38c998 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/src/sleep.c @@ -0,0 +1,10 @@ +#include "include/esp_sleep.h" + +void system_deep_sleep(uint32_t time_in_us) +{ +} + +bool system_deep_sleep_set_option(uint8_t option) +{ + return false; +} diff --git a/Sming/Arch/Rp2040/Components/rp2040/src/startup.cpp b/Sming/Arch/Rp2040/Components/rp2040/src/startup.cpp new file mode 100644 index 0000000000..12826ba80e --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/src/startup.cpp @@ -0,0 +1,130 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * user_main.cpp + * + */ + +#include +#include "Platform/System.h" +#include +#include +#include "include/esp_tasks_ll.h" +#include +#include +#include +#include +#include + +extern void init(); +extern void hw_timer_init(); +extern void system_init_timers(); +extern void system_service_timers(); + +namespace +{ +#ifdef ENABLE_BOOTSEL +uint32_t last_bootsel_check; + +/* + * Read state of BOOTSEL switch on GPIO 1 (QSPI CS #0). + * + * Simplified version of + * https://github.com/raspberrypi/pico-examples/blob/master/picoboard/button/button.c + * + * We want this to run as fast as possible, so other than placing code in RAM we make + * no concessions for interrupts (which must all be in RAM anyway) or the second core + * (which is not yet implemented). + */ +bool __noinline IRAM_ATTR get_bootsel_button() +{ + const unsigned CS_PIN_INDEX{1}; + + noInterrupts(); + + // Set chip select to Hi-Z + hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl, GPIO_OVERRIDE_LOW << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB, + IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS); + + // Note we can't call into any sleep functions in flash right now + for(volatile int i = 0; i < 1000; ++i) { + // + } + + // Read input (low when BOOTSEL pressed) + bool button_state = !(sio_hw->gpio_hi_in & BIT(CS_PIN_INDEX)); + + // Re-enable chip select + hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl, GPIO_OVERRIDE_NORMAL << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB, + IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS); + + interrupts(); + + return button_state; +} + +void check_bootsel() +{ + constexpr uint32_t sampleInterval{HW_TIMER2_CLK / 2}; + auto ticks = hw_timer2_read(); + auto elapsed = ticks - last_bootsel_check; + if(elapsed < sampleInterval) { + return; + } + last_bootsel_check = ticks; + + if(get_bootsel_button()) { + reset_usb_boot(BIT(PICO_DEFAULT_LED_PIN), 0); + } +} + +#endif // ENABLE_BOOTSEL + +} // namespace + +extern "C" int main(void) +{ + extern void system_init_clocks(void); + system_init_clocks(); + + system_soft_wdt_restart(); + + // Initialise hardware timers + hw_timer_init(); + + system_init_tasks(); + system_init_timers(); + + // Initialise UARTs to a known state + smg_uart_detach_all(); + + /* Note: System is a static class so it's safe to call initialize() before cpp_core_initialize() + * We need to do this so that class constructors can use the task queue or onReady() + */ + System.initialize(); + +#ifdef SMING_RELEASE + // disable all debug output for release builds + smg_uart_set_debug(UART_NO); +#endif + + // gdb_init(); + + Storage::initialize(); + + init(); // User code init + + while(true) { + system_soft_wdt_feed(); + system_service_tasks(); + system_service_timers(); +#ifdef ENABLE_BOOTSEL + check_bootsel(); +#endif + } + + return 0; +} diff --git a/Sming/Arch/Rp2040/Components/rp2040/src/system.cpp b/Sming/Arch/Rp2040/Components/rp2040/src/system.cpp new file mode 100644 index 0000000000..77a91f7a2d --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/src/system.cpp @@ -0,0 +1,97 @@ +#include "include/esp_system.h" +#include +#include +#include +#include +#include + +namespace +{ +constexpr uint32_t WATCHDOG_PERIOD_MS{8000}; +constexpr uint32_t SYSTEM_RESTART_DELAY{250}; + +uint32_t savedIrqLevels[2]; + +}; // namespace + +void system_soft_wdt_stop() +{ + hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS); +} + +void system_soft_wdt_restart() +{ + constexpr uint32_t ticks = WATCHDOG_PERIOD_MS * 1000 * 2; + static_assert(ticks <= 0xffffff); + + watchdog_enable(WATCHDOG_PERIOD_MS, true); +} + +void system_soft_wdt_feed() +{ + watchdog_update(); +} + +void interrupts() +{ + restore_interrupts(savedIrqLevels[get_core_num()]); +} + +uint32_t noInterrupts() +{ + auto level = save_and_disable_interrupts(); + savedIrqLevels[get_core_num()] = level; + return level; +} + +void restoreInterrupts(uint32_t level) +{ + restore_interrupts(level); +} + +uint32_t system_get_time() +{ + return hw_timer2_read(); +} + +void system_restart() +{ + // Force a restart + watchdog_hw->ctrl = WATCHDOG_CTRL_TRIGGER_BITS; +} + +struct rst_info* system_get_rst_info() +{ + static struct rst_info info; + auto reason = watchdog_hw->reason; + if(reason & WATCHDOG_REASON_FORCE_BITS) { + info.reason = REASON_SOFT_RESTART; + } else if(reason != 0) { + info.reason = REASON_SOFT_WDT_RST; + } else { + info.reason = REASON_DEFAULT_RST; + } + return &info; +} + +void os_delay_us(uint32_t us) +{ + auto start = hw_timer2_read(); + while(hw_timer2_read() - start < us) { + // Wait + } +} + +const char* system_get_sdk_version() +{ + return "PICO_SDK_" PICO_SDK_VERSION_STRING; +} + +uint32_t system_get_chip_id() +{ + pico_unique_board_id_t id; + pico_get_unique_board_id(&id); + uint32_t res; + memcpy(&res, &id, sizeof(res)); + return res; +} diff --git a/Sming/Arch/Rp2040/Components/rp2040/src/tasks.cpp b/Sming/Arch/Rp2040/Components/rp2040/src/tasks.cpp new file mode 100644 index 0000000000..df1ddaa000 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/rp2040/src/tasks.cpp @@ -0,0 +1,110 @@ +#include "include/esp_tasks_ll.h" +#include +#include +#include + +class TaskQueue +{ +public: + TaskQueue(os_task_t callback, os_event_t* events, uint8_t length) + { + this->callback = callback; + this->events = events; + this->length = length; + read = count = 0; + } + + bool IRAM_ATTR post(os_signal_t sig, os_param_t par) + { + auto level = save_and_disable_interrupts(); + + bool full = (count == length); + if(!full) { + events[(read + count) % length] = os_event_t{sig, par}; + ++count; + } + + restore_interrupts(level); + return !full; + } + + void process() + { + // Don't service any newly queued events + for(unsigned n = count; n != 0; --n) { + auto evt = events[read]; + read = (read + 1) % length; + --count; + callback(&evt); + } + } + +private: + os_task_t callback; + os_event_t* events; + uint8_t read; + uint8_t count; + uint8_t length; +}; + +static TaskQueue* task_queues[USER_TASK_PRIO_MAX + 1]; + +const uint8_t SYSTEM_TASK_PRIO = USER_TASK_PRIO_MAX; + +bool system_os_task(os_task_t callback, os_task_priority_t prio, os_event_t* events, uint8_t qlen) +{ + if(prio >= USER_TASK_PRIO_MAX) { + debug_e("[TQ] Invalid priority %u", prio); + return false; + } + auto& queue = task_queues[prio]; + if(queue != nullptr) { + debug_e("[TQ] Queue %u already initialised", prio); + return false; + } + + queue = new TaskQueue(callback, events, qlen); + return queue != nullptr; +} + +bool IRAM_ATTR system_os_post(os_task_priority_t prio, os_signal_t sig, os_param_t par) +{ + if(prio >= USER_TASK_PRIO_MAX) { + return false; + } + auto& queue = task_queues[prio]; + if(queue == nullptr) { + return false; + } + + return task_queues[prio]->post(sig, par); +} + +void system_init_tasks() +{ + static os_event_t events[8]; + + auto systemTaskCallback = [](os_event_t* event) { + auto callback = system_task_callback_t(event->sig); + if(callback != nullptr) { + callback(event->par); + } + }; + + task_queues[SYSTEM_TASK_PRIO] = new TaskQueue(systemTaskCallback, events, ARRAY_SIZE(events)); +} + +void system_service_tasks() +{ + for(int prio = SYSTEM_TASK_PRIO; prio >= 0; --prio) { + auto queue = task_queues[prio]; + if(queue != nullptr) { + queue->process(); + } + } +} + +bool system_queue_callback(system_task_callback_t callback, uint32_t param) +{ + return task_queues[SYSTEM_TASK_PRIO]->post(os_signal_t(callback), param); +} diff --git a/Sming/Arch/Rp2040/Components/sming-arch/README.rst b/Sming/Arch/Rp2040/Components/sming-arch/README.rst new file mode 100644 index 0000000000..50e8ae1279 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/sming-arch/README.rst @@ -0,0 +1,5 @@ +Sming (Rp2040) +============== + +This Component builds a library containing architecture-specific code, and defines dependencies +for Sming to build for Raspberry Pi 2040 microcontroller-based boards. diff --git a/Sming/Arch/Rp2040/Components/sming-arch/component.mk b/Sming/Arch/Rp2040/Components/sming-arch/component.mk new file mode 100644 index 0000000000..8f532b460b --- /dev/null +++ b/Sming/Arch/Rp2040/Components/sming-arch/component.mk @@ -0,0 +1,25 @@ +COMPONENT_SRCDIRS := \ + $(ARCH_CORE) $(call ListAllSubDirs,$(ARCH_CORE)) \ + $(ARCH_SYS) \ + $(ARCH_BASE)/Platform \ + $(ARCH_BASE)/Services/Profiling + +COMPONENT_INCDIRS := \ + $(ARCH_BASE) \ + $(ARCH_CORE) \ + $(ARCH_SYS)/include \ + $(ARCH_COMPONENTS) + +COMPONENT_DEPENDS := \ + libc \ + rp2040 \ + uf2 \ + driver \ + gdbstub \ + spi_flash + +# ELF and BIN files +DEBUG_VARS += TARGET_BIN +TARGET_OUT = $(BUILD_BASE)/$(APP_NAME).out +TARGET_BIN = $(FW_BASE)/$(APP_NAME).bin +TARGET_OUT_0 = $(TARGET_OUT) diff --git a/Sming/Arch/Rp2040/Components/spi_flash/README.rst b/Sming/Arch/Rp2040/Components/spi_flash/README.rst new file mode 100644 index 0000000000..193687eea2 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/spi_flash/README.rst @@ -0,0 +1,10 @@ +Rp2040 SPI Flash Support +======================== + +Provides functions for access to flash memory. + +.. toctree:: + :glob: + :maxdepth: 1 + + * diff --git a/Sming/Arch/Rp2040/Components/spi_flash/api.rst b/Sming/Arch/Rp2040/Components/spi_flash/api.rst new file mode 100644 index 0000000000..8034002df6 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/spi_flash/api.rst @@ -0,0 +1,6 @@ +API Documentation +================= + +.. doxygengroup:: spi_flash + :content-only: + :members: diff --git a/Sming/Arch/Rp2040/Components/spi_flash/component.mk b/Sming/Arch/Rp2040/Components/spi_flash/component.mk new file mode 100644 index 0000000000..d0e64b93f1 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/spi_flash/component.mk @@ -0,0 +1,2 @@ +COMPONENT_DEPENDS := rp2040 +COMPONENT_DOXYGEN_INPUT := include diff --git a/Sming/Arch/Rp2040/Components/spi_flash/flashmem.cpp b/Sming/Arch/Rp2040/Components/spi_flash/flashmem.cpp new file mode 100644 index 0000000000..7c1080a383 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/spi_flash/flashmem.cpp @@ -0,0 +1,408 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * flashmem.cpp + * + ****/ + +#include +#include +#include +#include +#include +#include +#include + +#define FLASHCMD_READ_SFDP 0x5a +#define FLASHCMD_READ_JEDEC_ID 0x9f + +namespace +{ +/* + * To ensure memory alignment a temporary buffer is used by flashmem_read and flashmem_write functions. + * + * The buffer must be an integer multiple of INTERNAL_FLASH_WRITE_UNIT_SIZE. + */ +constexpr dma_channel_transfer_size dmaTransferSize{DMA_SIZE_32}; +constexpr size_t flashReadUnitSize{1 << dmaTransferSize}; +constexpr size_t flashBufferCount{FLASH_PAGE_SIZE / flashReadUnitSize}; + +// JEDEC flash ID read from chip +uint32_t flash_id; +// Size of flash chip read from device +uint32_t sfdp_flash_size_bytes; + +uint32_t sfdp_read_size() +{ + uint32_t hdr[4]; + flashmem_sfdp_read(0, hdr, sizeof(hdr)); + + // Check magic + constexpr uint32_t sfdp_magic{'S' | ('F' << 8) | ('D' << 16) | ('P' << 24)}; + if(sfdp_magic != hdr[0]) { + debug_e("[SFDP] Magic invalid: 0x%08x (expected 0x%08x)", hdr[0], sfdp_magic); + return 0; + } + + // Skip NPH -- we don't care about nonmandatory parameters. + // Check header byte for mandatory parameter table + // | ID | MinRev | MajRev | Length in words | ptr[2] | ptr[1] | ptr[0] | unused| + // ID must be 0 (JEDEC) for mandatory PTH + if((hdr[2] & 0xff) != 0) { + debug_e("[SFDP] PTH ID invalid"); + return 0; + } + + uint32_t param_table_ptr = hdr[3] & 0xffffffu; + uint32_t param[2]; + flashmem_sfdp_read(param_table_ptr, ¶m, sizeof(param)); + + // MSB set: array >= 2 Gbit, encoded as log2 of number of bits + auto array_size_word = param[1]; + if(array_size_word & BIT(31)) { + return 1UL << ((array_size_word & ~BIT(31)) - 3); + } + // MSB clear: array < 2 Gbit, encoded as direct bit count, minus 1 + return (array_size_word + 1) / 8; +} + +void initFlashInfo() +{ + if(flash_id != 0) { + return; + } + + // Read JEDEC ID: command, 3 data bytes (manf. ID, Memory Type ID, Capacity ID) + uint8_t buf[4] = {FLASHCMD_READ_JEDEC_ID}; + flash_do_cmd(buf, buf, 4); + flash_id = (buf[1] << 16) | (buf[2] << 8) | buf[3]; + + sfdp_flash_size_bytes = sfdp_read_size(); +} + +uint32_t writeAligned(const void* from, uint32_t toaddr, uint32_t size) +{ + auto flashaddr = XIP_BASE + toaddr; + if(!isFlashPtr(flashaddr)) { + debug_e("[FLSH] write toaddr not in flash 0x%08x", toaddr); + return 0; + } + + debug_d("[FLSH] write(%p, 0x%08x, 0x%08x)", from, toaddr, size); + + flash_range_program(toaddr, static_cast(from), size); + + return size; +} + +uint32_t readAligned(void* to, uint32_t fromaddr, uint32_t size) +{ + auto flashaddr = XIP_BASE + fromaddr; + if(!isFlashPtr(flashaddr)) { + debug_e("[FLSH] read fromaddr not in flash 0x%08x", fromaddr); + return 0; + } + + debug_d("[FLSH] read(%p, 0x%08x, 0x%08x)", to, fromaddr, size); + + auto transfer_count = size >> dmaTransferSize; + + /* + * https://github.com/raspberrypi/pico-examples/tree/master/flash/xip_stream + * + * The XIP has some internal hardware that can stream a linear access sequence to + * a DMAable FIFO, while the system is still doing random accesses on flash code + data. + */ + + /* + * Transfer started by writing nonzero value to stream_ctr. + * stream_ctr will count down as the transfer progresses. + * Can terminate early by writing 0 to stream_ctr. + * It's a good idea to drain the FIFO first! + */ + while(!(xip_ctrl_hw->stat & XIP_STAT_FIFO_EMPTY)) { + (void)xip_ctrl_hw->stream_fifo; + } + + xip_ctrl_hw->stream_addr = flashaddr; + xip_ctrl_hw->stream_ctr = transfer_count; + + /* + * Start DMA transfer from XIP stream FIFO to our buffer in memory. + * Use the auxiliary bus slave for the DMA<-FIFO accesses, to avoid stalling + * the DMA against general XIP traffic. + */ + constexpr unsigned dma_chan{0}; + dma_channel_config cfg = dma_channel_get_default_config(dma_chan); + channel_config_set_transfer_data_size(&cfg, dmaTransferSize); + channel_config_set_read_increment(&cfg, false); + channel_config_set_write_increment(&cfg, true); + channel_config_set_dreq(&cfg, DREQ_XIP_STREAM); + dma_channel_configure(dma_chan, &cfg, + to, // Write addr + reinterpret_cast(XIP_AUX_BASE), // Read addr + transfer_count, // Transfer count + true // Start immediately! + ); + + dma_channel_wait_for_finish_blocking(dma_chan); + + return size; +} + +} // namespace + +uint32_t flashmem_write(const void* from, uint32_t toaddr, uint32_t size) +{ + const uint32_t blksize = FLASH_PAGE_SIZE; + const uint32_t blkmask = FLASH_PAGE_SIZE - 1; + + if((toaddr & blkmask) == 0 && (size & blkmask) == 0) { + return writeAligned(from, toaddr, size); + } + + uint8_t tmpdata[FLASH_PAGE_SIZE]; + auto pfrom = static_cast(from); + uint32_t remain = size; + + // Align the start + uint32_t rest = toaddr & blkmask; + if(rest != 0) { + uint32_t addr_aligned = toaddr & ~blkmask; // this is the actual aligned address + + // Read existing unit and overlay with new data + if(readAligned(tmpdata, addr_aligned, blksize) != blksize) { + return 0; + } + + while(remain != 0 && rest < blksize) { + tmpdata[rest++] = *pfrom++; + --remain; + } + + // Write the unit + uint32_t written = writeAligned(tmpdata, addr_aligned, blksize); + if(written != blksize) { + return written; + } + + if(remain == 0) { + return size; + } + + toaddr = addr_aligned + blksize; + } + + // The start address is now a multiple of blksize + // Compute how many bytes we can write as multiples of blksize + rest = remain & blkmask; + remain &= ~blkmask; + // Program the blocks now + while(remain != 0) { + unsigned count = std::min(size_t(remain), sizeof(tmpdata)); + memcpy(tmpdata, pfrom, count); + uint32_t written = writeAligned(tmpdata, toaddr, count); + remain -= written; + if(written != count) { + return size - remain; + } + toaddr += count; + pfrom += count; + } + + // And the final part of a block if needed + if(rest != 0) { + if(readAligned(tmpdata, toaddr, blksize) != blksize) { + return size - remain; + } + for(unsigned i = 0; i < rest; ++i) { + tmpdata[i] = *pfrom++; + } + uint32_t written = writeAligned(tmpdata, toaddr, blksize); + remain -= written; + if(written != blksize) { + return size - remain; + } + } + + return size; +} + +uint32_t flashmem_read(void* to, uint32_t fromaddr, uint32_t size) +{ + if(IS_ALIGNED(fromaddr) && IS_ALIGNED(size)) { + return readAligned(to, fromaddr, size); + } + + const uint32_t blksize = flashReadUnitSize; + const uint32_t blkmask = flashReadUnitSize - 1; + + uint8_t tmpdata[flashBufferCount * blksize]; + auto pto = static_cast(to); + size_t remain = size; + + // Align the start + uint32_t rest = fromaddr & blkmask; + if(rest != 0) { + uint32_t addr_aligned = fromaddr & ~blkmask; // this is the actual aligned address + if(readAligned(tmpdata, addr_aligned, blksize) != blksize) { + return 0; + } + // memcpy(pto, &tmpdata[rest], std::min(blksize - rest, remain)) + while(remain != 0 && rest < blksize) { + *pto++ = tmpdata[rest++]; + --remain; + } + if(remain == 0) { + return size; + } + fromaddr = addr_aligned + blksize; + } + + // The start address is now a multiple of blksize + // Compute how many bytes we can read as multiples of blksize + rest = remain & blkmask; + remain &= ~blkmask; + // Read the blocks now + while(remain != 0) { + unsigned count = std::min(remain, sizeof(tmpdata)); + uint32_t read = readAligned(tmpdata, fromaddr, count); + memcpy(pto, tmpdata, read); + remain -= read; + if(read != count) { + return size - remain; + } + fromaddr += count; + pto += count; + } + + // And the final part of a block if needed + if(rest != 0) { + if(readAligned(tmpdata, fromaddr, blksize) != blksize) { + return size - remain; + } + for(unsigned i = 0; i < rest; ++i) { + *pto++ = tmpdata[i]; + } + } + + return size; +} + +bool flashmem_erase_sector(uint32_t sector_id) +{ + debug_d("flashmem_erase_sector(0x%08x)", sector_id); + flash_range_erase(sector_id * FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE); + return true; +} + +SPIFlashInfo flashmem_get_info() +{ + SPIFlashInfo info{}; + info.size = flashmem_get_size_type(); + + // Flash mode + uint32_t ctrlr0 = ssi_hw->ctrlr0; + auto ssi_frame_format = (ctrlr0 & SSI_CTRLR0_SPI_FRF_BITS) >> SSI_CTRLR0_SPI_FRF_LSB; + auto trans_type = (ctrlr0 & SSI_SPI_CTRLR0_TRANS_TYPE_BITS) >> SSI_SPI_CTRLR0_TRANS_TYPE_LSB; + + switch(ssi_frame_format) { + case SSI_CTRLR0_SPI_FRF_VALUE_DUAL: + info.mode = (trans_type == 0) ? MODE_DOUT : MODE_DIO; + break; + case SSI_CTRLR0_SPI_FRF_VALUE_QUAD: + info.mode = (trans_type == 0) ? MODE_QOUT : MODE_QIO; + break; + case SSI_CTRLR0_SPI_FRF_VALUE_STD: + default: + info.mode = MODE_SLOW_READ; + } + + return info; +} + +uint8_t flashmem_get_size_type() +{ + initFlashInfo(); + + switch(flashmem_get_size_bytes()) { + case 0x40000: + return SIZE_2MBIT; + case 0x80000: + return SIZE_4MBIT; + case 0x100000: + return SIZE_8MBIT; + case 0x200000: + return SIZE_16MBIT; + case 0x400000: + return SIZE_32MBIT; + default: + return SIZE_1MBIT; + } +} + +uint32_t flashmem_get_size_bytes() +{ + initFlashInfo(); + + // Use SFDP data if available + if(sfdp_flash_size_bytes != 0) { + return sfdp_flash_size_bytes; + } + + /* + * Determine flash size from the JEDEC device ID. + * The lower byte generally contains the device size in bytes as a power of 2. + * However, this isn't a hard standard so may not work in all situations. + */ + auto size_bytes = flash_id & 0xff; + return 1U << size_bytes; +} + +uint16_t flashmem_get_size_sectors() +{ + return flashmem_get_size_bytes() / FLASH_SECTOR_SIZE; +} + +uint32_t flashmem_find_sector(uint32_t address, uint32_t* pstart, uint32_t* pend) +{ + // All the sectors in the flash have the same size, so just align the address + uint32_t sect_id = address / FLASH_SECTOR_SIZE; + + if(pstart != nullptr) { + *pstart = sect_id * FLASH_SECTOR_SIZE; + } + if(pend != nullptr) { + *pend = (sect_id + 1) * FLASH_SECTOR_SIZE - 1; + } + return sect_id; +} + +uint32_t flashmem_get_sector_of_address(uint32_t addr) +{ + return flashmem_find_sector(addr, NULL, NULL); +} + +uint32_t spi_flash_get_id(void) +{ + initFlashInfo(); + return flash_id; +} + +void flashmem_sfdp_read(uint32_t addr, void* buffer, size_t count) +{ + size_t buflen = 5 + count; + uint8_t buf[buflen] = { + FLASHCMD_READ_SFDP, + uint8_t(addr >> 16), + uint8_t(addr >> 8), + uint8_t(addr & 0xff), + 0, // dummy + }; + + flash_do_cmd(buf, buf, buflen); + memcpy(buffer, &buf[5], count); +} diff --git a/Sming/Arch/Rp2040/Components/spi_flash/include/esp_spi_flash.h b/Sming/Arch/Rp2040/Components/spi_flash/include/esp_spi_flash.h new file mode 100644 index 0000000000..b4f4d06445 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/spi_flash/include/esp_spi_flash.h @@ -0,0 +1,175 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Based on NodeMCU platform_flash + * https://github.com/nodemcu/nodemcu-firmware + * + ****/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define SPI_FLASH_SEC_SIZE 4096 + +/** + * @defgroup flash Flash Memory Support + * @defgroup spi_flash SPI Flash API + * @ingroup flash + * @{ + */ + +#define FLASH_TOTAL_SEC_COUNT PICO_FLASH_SIZE_BYTES + +/* + * TODO: Revise layout to use custom 2nd-stage bootloader with partition + * table support. + */ +/// Number of flash sectors reserved for system parameters at start +#define SYS_PARAM_SEC_COUNT 4 +#define FLASH_WORK_SEC_COUNT (FLASH_TOTAL_SEC_COUNT - SYS_PARAM_SEC_COUNT) + +#define INTERNAL_FLASH_SECTOR_SIZE SPI_FLASH_SEC_SIZE +#define INTERNAL_FLASH_SIZE ((FLASH_WORK_SEC_COUNT)*INTERNAL_FLASH_SECTOR_SIZE) +#define INTERNAL_FLASH_START_ADDRESS FLASH_BASE + +typedef enum { + MODE_QIO = 0, + MODE_QOUT = 1, + MODE_DIO = 2, + MODE_DOUT = 15, + MODE_SLOW_READ = 0xFE, ///< Not supported + MODE_FAST_READ = 0xFF, ///< Not supported +} SPIFlashMode; + +typedef enum { + SPEED_40MHZ = 0, + SPEED_26MHZ = 1, + SPEED_20MHZ = 2, + SPEED_80MHZ = 15, +} SPIFlashSpeed; + +typedef enum { + SIZE_4MBIT = 0, + SIZE_2MBIT = 1, + SIZE_8MBIT = 2, + SIZE_16MBIT = 3, + SIZE_32MBIT = 4, + SIZE_1MBIT = 0xFF, ///< Not supported +} SPIFlashSize; + +/** @brief SPI Flash memory information block. + * Stored at the beginning of flash memory. + */ +typedef struct { + uint8_t mode : 8; ///< SPIFlashMode + uint8_t speed : 4; ///< SPIFlashSpeed + uint8_t size : 4; ///< SPIFlashSize +} SPIFlashInfo; + +/** @brief Obtain the flash memory address for a memory pointer + * @param memptr + * @retval uint32_t Offset from start of flash memory + * @note If memptr is not in valid flash memory it will return an offset which exceeds + * the internal flash memory size. + * @note The flash location is dependent on where rBoot has mapped the firmware. + */ +static inline uint32_t flashmem_get_address(const void* memptr) +{ + auto addr = uint32_t(memptr); + if(addr < XIP_BASE || addr >= XIP_NOALLOC_BASE) { + return 0; + } + return addr - XIP_BASE; +} + +/** @brief Write a block of data to flash + * @param from Buffer to obtain data from + * @param toaddr Flash location to start writing + * @param size Number of bytes to write + * @retval uint32_t Number of bytes written + * @note None of the parameters need to be aligned + */ +uint32_t flashmem_write(const void* from, uint32_t toaddr, uint32_t size); + +/** @brief Read a block of data from flash + * @param to Buffer to store data + * @param fromaddr Flash location to start reading + * @param size Number of bytes to read + * @retval uint32_t Number of bytes written + * @note none of the parameters need to be aligned + */ +uint32_t flashmem_read(void* to, uint32_t fromaddr, uint32_t size); + +/** @brief Erase a single flash sector + * @param sector_id the sector to erase + * @retval true on success + */ +bool flashmem_erase_sector(uint32_t sector_id); + +/** @brief Get flash memory information block + * @retval SPIFlashInfo Information block + */ +SPIFlashInfo flashmem_get_info(); + +/** @brief Returns a number indicating the size of flash memory chip + * @retval uint8_t See SpiFlashInfo.size field for possible values + */ +uint8_t flashmem_get_size_type(); + +/** @brief get the total flash memory size + * @retval uint32_t Size in bytes + */ +uint32_t flashmem_get_size_bytes(); + +/** @brief Get the total number of flash sectors + * @retval uint16_t Sector count + */ +uint16_t flashmem_get_size_sectors(); + +/** @brief Helper function: find the flash sector in which an address resides + * @param address + * @param pstart OUT/OPTIONAL: Start of sector containing the given address + * @param pend OUT/OPTIONAL: Last address in sector + * @retval uint32_t Sector number for the given address + * @note Optional parameters may be null + */ +uint32_t flashmem_find_sector(uint32_t address, uint32_t* pstart, uint32_t* pend); + +/** @brief Get sector number containing the given address + * @param addr + * @retval uint32_t sector number + */ +uint32_t flashmem_get_sector_of_address(uint32_t addr); + +/* + * @brief Returns the address of the first free block on flash + * @retval uint32_t The actual address on flash + */ +uint32_t flashmem_get_first_free_block_address(); + +/* + * @brief Get unique 32-bit flash identification code + */ +uint32_t spi_flash_get_id(void); + +/* + * @brief Read flash SFDP device information (standard for Serial Flash Discoverable Parameters) + * @param addr First memory location to read + * @param buffer Buffer for data + * @param count Number of bytes to read + */ +void flashmem_sfdp_read(uint32_t addr, void* buffer, size_t count); + +/** @} */ + +#ifdef __cplusplus +} +#endif diff --git a/Sming/Arch/Rp2040/Components/spi_flash/include/iram_precache.h b/Sming/Arch/Rp2040/Components/spi_flash/include/iram_precache.h new file mode 100644 index 0000000000..a18771a3d6 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/spi_flash/include/iram_precache.h @@ -0,0 +1,17 @@ +/* + * Dummy stub. RP2040 has tons of RAM. +*/ + +#pragma once + +/** + * @defgroup iram_precache IRAM Precache Support + * @ingroup flash + * @{ + */ + +#define IRAM_PRECACHE_ATTR +#define IRAM_PRECACHE_START(tag) +#define IRAM_PRECACHE_END(tag) + +/** @} */ diff --git a/Sming/Arch/Rp2040/Components/uf2/LICENSE.txt b/Sming/Arch/Rp2040/Components/uf2/LICENSE.txt new file mode 100644 index 0000000000..d3e3605f81 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/uf2/LICENSE.txt @@ -0,0 +1,25 @@ +Microsoft UF2 + +The MIT License (MIT) + +Copyright (c) Microsoft Corporation + +All rights reserved. + +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 the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Sming/Arch/Rp2040/Components/uf2/README.rst b/Sming/Arch/Rp2040/Components/uf2/README.rst new file mode 100644 index 0000000000..8c68b68e8e --- /dev/null +++ b/Sming/Arch/Rp2040/Components/uf2/README.rst @@ -0,0 +1,18 @@ +UF2 Support +=========== + +Provides support for converting binary code into UF2 format for uploading to RP2040 devices. + +.. toctree:: + :glob: + :maxdepth: 1 + + * + + +Configuration variables +----------------------- + +.. envvar:: UF2CONV_PY + + Path to the ``uf2conv`` utility should you wish to run it manually. diff --git a/Sming/Arch/Rp2040/Components/uf2/component.mk b/Sming/Arch/Rp2040/Components/uf2/component.mk new file mode 100644 index 0000000000..5ca96793cb --- /dev/null +++ b/Sming/Arch/Rp2040/Components/uf2/component.mk @@ -0,0 +1,61 @@ +COMPONENT_DOCFILES := \ + uf2.md \ + uf2conv.md + +# Path to python utility for manipulating files and uploading to target +DEBUG_VARS += UF2CONV_PY +UF2CONV_PY := $(COMPONENT_PATH)/uf2conv.py + + +# Invoke uf2conf utility +# $1 -> Parameters +ifdef WSL_ROOT +Uf2Conv = powershell.exe -Command "$(PYTHON) $(UF2CONV_PY) $(if $V,--verbose) $1" +else +Uf2Conv = $(PYTHON) $(UF2CONV_PY) $(if $V,--verbose) $1 +endif + + +# Read flash manufacturer ID and determine actual size +define ReadFlashID + $(info Reading Flash ID) + $(call Uf2Conv,--list --verbose) +endef + +# Write file contents to Flash +# $1 -> List of `Offset=File` chunks +define WriteFlash + $(if $1,\ + $(info WriteFlash $1) \ + $(call Uf2Conv,--upload $1 --output $(OUT_BASE)/flash.uf2) + ) +endef + +# Verify flash against file contents +# $1 -> List of `Offset=File` chunks +define VerifyFlash + $(if $1,\ + $(info VerifyFlash $1) + $(info ** NOT IMPLEMENTED **) + ) +endef + +# Read flash memory into file +# $1 -> `Offset,Size` chunk +# $2 -> Output filename +define ReadFlash + $(info ReadFlash $1,$2) + $(info ** NOT IMPLEMENTED **) +endef + +# Erase a region of Flash +# $1 -> Offset,Size +define EraseFlashRegion + $(info EraseFlashRegion $1) + $(info ** NOT IMPLEMENTED **) +endef + +# Erase flash memory contents +define EraseFlash + $(info ** NOT IMPLEMENTED **) +endef diff --git a/Sming/Arch/Rp2040/Components/uf2/uf2.h b/Sming/Arch/Rp2040/Components/uf2/uf2.h new file mode 100644 index 0000000000..04e7725379 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/uf2/uf2.h @@ -0,0 +1,63 @@ +/** + +Microsoft UF2 + +The MIT License (MIT) + +Copyright (c) Microsoft Corporation + +All rights reserved. + +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 the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ +#ifndef UF2FORMAT_H +#define UF2FORMAT_H 1 + +#include +#include + +// All entries are little endian. + +#define UF2_MAGIC_START0 0x0A324655UL // "UF2\n" +#define UF2_MAGIC_START1 0x9E5D5157UL // Randomly selected +#define UF2_MAGIC_END 0x0AB16F30UL // Ditto + +// If set, the block is "comment" and should not be flashed to the device +#define UF2_FLAG_NOFLASH 0x00000001 + +typedef struct { + // 32 byte header + uint32_t magicStart0; + uint32_t magicStart1; + uint32_t flags; + uint32_t targetAddr; + uint32_t payloadSize; + uint32_t blockNo; + uint32_t numBlocks; + uint32_t reserved; + + // raw data; + uint8_t data[476]; + + // store magic also at the end to limit damage from partial block reads + uint32_t magicEnd; +} UF2_Block; + +#endif diff --git a/Sming/Arch/Rp2040/Components/uf2/uf2.md b/Sming/Arch/Rp2040/Components/uf2/uf2.md new file mode 100644 index 0000000000..0333c08da4 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/uf2/uf2.md @@ -0,0 +1,356 @@ +# USB Flashing Format (UF2) + +UF2 is a file format, developed by Microsoft for [PXT](https://github.com/Microsoft/pxt) +(also known as [Microsoft MakeCode](https://makecode.com)), that is particularly suitable for +flashing microcontrollers over MSC (Mass Storage Class; aka removable flash drive). + +For a more friendly explanation, check out [this blog post](https://makecode.com/blog/one-chip-to-flash-them-all). +Also, take a look at the list of [implementations](#implementations) at the bottom of this document. + +## Overview + +The UF2 file consists of 512 byte blocks, each of which is self-contained and independent of others. +Each 512 byte block consists of (see below for details): + * magic numbers at the beginning and at the end + * address where the data should be flashed + * up to 476 bytes of data + +The data transfers over MSC always arrive in multiples of 512 bytes. +Together with the FAT file system structure, this means that blocks of the +UF2 file are always aligned with the MSC writes - the microcontroller +never gets a partial file. + +The magic numbers let the microcontroller distinguish an UF2 file block from +other data (eg., FAT table entry, or various book-keeping files stored by some +operating systems). When a UF2 block is recognized, it can be flashed +immediately (unless flash page size is more than 256 bytes; in that case a buffer +is needed). The actual handling of file format during writing is very simple +(~10 lines of C code in simplest version). + +## File format + +A UF2 file consists of 512 byte blocks. Each block starts with a 32 byte +header, followed by data, and a final magic number. +All fields, except for data, are 32 bit unsigned little endian integers. + +| Offset | Size | Value | +|--------|------|---------------------------------------------------| +| 0 | 4 | First magic number, `0x0A324655` (`"UF2\n"`) | +| 4 | 4 | Second magic number, `0x9E5D5157` | +| 8 | 4 | Flags | +| 12 | 4 | Address in flash where the data should be written | +| 16 | 4 | Number of bytes used in data (often 256) | +| 20 | 4 | Sequential block number; starts at 0 | +| 24 | 4 | Total number of blocks in file | +| 28 | 4 | File size or board family ID or zero | +| 32 | 476 | Data, padded with zeros | +| 508 | 4 | Final magic number, `0x0AB16F30` | + +The following C struct can be used: + +```C +struct UF2_Block { + // 32 byte header + uint32_t magicStart0; + uint32_t magicStart1; + uint32_t flags; + uint32_t targetAddr; + uint32_t payloadSize; + uint32_t blockNo; + uint32_t numBlocks; + uint32_t fileSize; // or familyID; + uint8_t data[476]; + uint32_t magicEnd; +} UF2_Block; +``` + +### Flags + +Currently, there are five flags defined: + + * `0x00000001` - **not main flash** - this block should be skipped when writing the + device flash; it can be used to store "comments" in the file, typically + embedded source code or debug info that does not fit on the device flash + + * `0x00001000` - **file container** - see below + + * `0x00002000` - **familyID present** - when set, the `fileSize/familyID` holds a value + identifying the board family (usually corresponds to an MCU) + + * `0x00004000` - **MD5 checksum present** - see below + + * `0x00008000` - **extension tags present** - see below + +### Family ID + +This field is optional, and should be set only when the corresponding +flag is set. It is recommended that new bootloaders require the field to +be set appropriately, and refuse to flash UF2 files without it. +If you're developing your own bootloader, and your +board family isn't listed here, pick a new family ID at random. It's good +to also send a PR here, so your family can be listed. + +If the `familyID` doesn't match, the bootloader should disregard the +entire block, including `blockNo` and `numBlocks` fields. +In particular, writing a full UF2 file with non-matching `familyID` +should not reset the board. +This also allows for several files with different `familyID` to be +simply concatenated together, and the whole resulting file to be copied +to the device with only one actually being written to flash. + +#### Picking numbers at random + +The reason to pick numbers at random is to minimize risk of collisions +in the wild. Do not pick random numbers by banging on keyboard, or by using +`0xdeadf00d`, `0x42424242` etc. A good way is to use the following +shell command: `printf "0x%04x%04x\n" $RANDOM $RANDOM` +Another good way is the link at the bottom of https://microsoft.github.io/uf2/patcher/ +This procedure was unfortunately not used for the SAMD51 and NRF52840 below. + +#### Family list + +The current master list of family IDs is maintained in a [JSON file](utils/uf2families.json). + +### Rationale + +The magic number at the end is meant to mitigate partial block writes. + +Second and final magic numbers were randomly selected, except for the last byte +of final magic number, which was forced to be `'\n'` (`0xA`). Together with the +first magic number being `"UF2\n"` this makes it easy to identify UF2 blocks in +a text editor. + +The header is padded to 32 bytes, as hex editors commonly use 16 or 32 bytes +as line length. This way, the data payload is aligned to line start. + +32 bit integers are used for all fields so that large flash sizes can be +supported in future, as well as for simplicity. Little endian is used, as most +microcontrollers are little endian. 8 bit microcontrollers can choose to just +use the first 16 bits of various header fields. + +The total number of blocks in the file and the sequential block number make it +easy for the bootloader to detect that all blocks have been transferred. It +requires one bit of memory per block (eg., on SAMD21G18A it's 128 bytes). +Alternatively, the bootloader might ignore that and just implement a reset +after say 1 second break in incoming UF2 blocks. + +### Payload sizes + +The number of data bytes is configurable and depends on the size of +the flash page (that is the smallest size that can be erased) on the +microcontroller. + + * if the page size is more than `476` bytes, the bootloader should support + any payload size, as it needs to buffer the entire page in memory anyway + + * if the page size is less than `476` bytes, the payload should be a multiple + of page size, so it can be written without buffering; the target address + should also be a multiple of page size + +In any event, payload size and target address should always be 4-byte aligned. + +Note that payload size of `256` is always correct, and makes it easy to convert +between flash addresses and UF2 file offsets. + +For example, on Atmel's SAMD21 chips the page size is `256` bytes, and this +also is the payload size. If the page size was `128` bytes, one could use +payload of `128*3`. Nordic nRF51 has page size of `1024` bytes, and thus +any payload size should be allowed. + +### Embedding sources + +Some IDEs will embed program sources in the UF2 file. This allows a UF2 files to be +loaded by the IDE and serve as a natural backup and transfer format. +This can be done in two ways: + + * using the "not main flash" flag + * using normal blocks that are flashed to the device + +If the bootloader can expose `CURRENT.UF2` file (see below) and there is enough +flash available, than the second option is more desirable, as it allows sharing +programs directly from the board. + +See https://makecode.com/source-embedding for more info. + +### Robustness + +The file format is designed specifically to deal with the following problems: + + * operating system (OS) writing blocks in different order than occurs in a file + * OS writing blocks multiple times + * OS writing data that is not UF2 blocks + * OS writing first/final part of a block, possibly for metadata detection or search indexing + +The only file system assumption we make is that blocks of file are aligned with +blocks on the hard drive. It's likely true of many file systems besides FAT. + +We also assume that USB MSC device reports its block size to be a multiple of `512` +bytes. In the wild these devices always almost report exactly `512`, and some +operating systems do not support other values. + +## Files exposed by bootloaders + +Bootloaders may expose virtual files in their MSC devices. These are +standardized here, so that flashing tools can automatically detect the +bootloaders. + + * `INFO_UF2.TXT` - contains information about the bootloader build and the board on which it is running + + * `INDEX.HTM` - redirects to a page that contains an IDE or other information + + * `CURRENT.UF2` - the contents of the entire flash of the device, starting at `0x00000000`, with `256` payload size; + thus, the size of this file will report as twice the size of flash + +Flashing tools can use the presence of `INFO_UF2.TXT` (in upper or lower case, +as FAT is case-insensitive) file as an indication that a given directory is +actually a connected UF2 board. The other files should not be used for +detection. + +Typical `INFO_UF2.TXT` file looks like this: + +``` +UF2 Bootloader v1.1.3 SFA +Model: Arduino Zero +Board-ID: SAMD21G18A-Zero-v0 +``` + +The `Board-ID` field is machine-readable and consists of a number of dash-separated tokens. +The first token is the CPU type, second is the board type, and third is the board revision. +More tokens can be also added. + +The bootloader should contain its info file as a static string somewhere in its code. +If possible, the last word of the bootloader code should point to this string. +This way, the info file can be found in the initial section of the `CURRENT.UF2` +file as well. Thus, a board type can be determined from the contents of `CURRENT.UF2`. +This is particularly useful with the source embedding (see above). + +## File containers + +It is also possible to use the UF2 format as a container for one or more +regular files (akin to a TAR file, or ZIP archive without compression). This +is useful when the embedded device being flashed sports a file system. + +The program to run may reside in one of the files, or in the main flash memory. + +In such a usage the `file container` flag is set on blocks, the field `fileSize` +holds the file size of the current file, and the field `targetAddr` holds the +offset in current file. + +The `not main flash` flag on blocks should be ignored when the `file container` is set. + +The file name is stored at `&data[payloadSize]` (ie., right after the actual payload) and +terminated with a `0x00` byte. The format of filename is dependent on the +bootloader (usually implemented as some sort of file system daemon). + +The bootloader will usually allow any size of the payload. + +The current files on device might be exposed as multiple UF2 files, instead of +a single `CURRENT.UF2`. They may reside in directories, however, due to UF2 general +design, it doesn't matter which directory the UF2 file is written to. + +Typical writing procedure is as follows: + + * validate UF2 magic numbers + * make sure that `targetAddr < fileSize` and that `fileSize` isn't out of reasonable range + * write `0x00` at `data[475]` to ensure NUL termination of file name + * read file name from `&data[payloadSize]`; perform any mapping on the file name + * create a directory where the file is to be written if it doesn't exist + * open the file for writing + * truncate the file to `fileSize` + * seek `targetAddr` + * write the payload (ie., `data[0 ... payloadSize - 1]`) + * close the file + +The fields `blockNo` and `numBlocks` refer to the entire UF2 file, not the current +file. + +## MD5 checksum + +When the `0x4000` flag is set, the last 24 bytes of `data[]` hold the following structure: + +| Offset | Size | Value | +|--------|------|---------------------------------------------------| +| 0 | 4 | Start address of region | +| 4 | 4 | Length of region in bytes | +| 8 | 16 | MD5 checksum in binary format | + +The flashing program should compute the MD5 sum of the specified region. +If the region checksum matches, flashing of the current block can be skipped. +Typically, many blocks in sequence will have the same region specified, +and can all be skipped, if the matching succeeded. +The position of the current block will typically be inside of the region. +The position and size of the region should be multiple of page erase size +(4k or 64k on typical SPI flash). + +This is currently only used on ESP32, which is also why MD5 checksum is used. + +## Extension tags + +When the `0x8000` flag is set, additional information can be appended right after +payload data (i.e., it starts at `32 + payloadSize`). +Every tag starts at 4 byte boundary. +The first byte of tag contains its total size in bytes (including the size byte +and type designation). +The next three bytes designate the type of tag (if you want to define custom +tags, pick them at random). +The last tag has size of `0` and type of `0`. + +Standard tag designations follow: + + * `0x9fc7bc` - version of firmware file - UTF8 semver string + + * `0x650d9d` - description of device for which the firmware file is destined (UTF8) + + * `0x0be9f7` - page size of target device (32 bit unsigned number) + + * `0xb46db0` - SHA-2 checksum of firmware (can be of various size) + + * `0xc8a729` - device type identifier - a refinement of `familyID` meant to identify a kind of device + (eg., a toaster with specific pinout and heating unit), not only MCU; 32 or 64 bit number; can be hash of `0x650d9d` + +For example, the following bytes encode firmware version `0.1.2` for device +named `ACME Toaster mk3` (line breaks added for clarity): + +``` +09 bc c7 9f 30 2e 31 2e 32 00 00 00 +14 9d 0d 65 41 43 4d 45 20 54 6f 61 73 74 65 72 20 6d 6b 33 +00 00 00 00 +``` + +Extension tags can, but don't have to, be repeated in all blocks. + +## Implementations + +### Bootloaders + + * [Microchip ATSAMD21 and ATSAMD51](https://github.com/Microsoft/uf2-samdx1) + * [Arduino UNO](https://github.com/mmoskal/uf2-uno) + * [STM32F103](https://github.com/mmoskal/uf2-stm32) + * [STM32F4](https://github.com/mmoskal/uf2-stm32f) + * [Nordic NRF52840](https://github.com/adafruit/Adafruit_nRF52840_Bootloader) + * [Linux (RPi Zero)](https://github.com/microsoft/uf2-linux) + * [Cypress FX2](https://github.com/whitequark/libfx2/tree/master/firmware/boot-uf2) + * [Tiny UF2](https://github.com/adafruit/tinyuf2) - Support ESP32-S2, iMXRT10xx, STM32F4 + * [RP2040 chip](https://www.raspberrypi.org/products/raspberry-pi-pico/) - native support in silicon + * [UF2-ChibiOS](https://github.com/striso/uf2-ChibiOS) - Supports STM32H7 + +There's an ongoing effort to implement UF2 in [Codal](https://github.com/lancaster-university/codal-core). + +### Editors + +* https://arcade.makecode.com +* https://makecode.adafruit.com +* https://makecode.seeedstudio.com +* https://maker.makecode.com + +### Libraries + +* https://www.npmjs.com/package/uf2 + +## License + +MIT + +## Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/Sming/Arch/Rp2040/Components/uf2/uf2conv.md b/Sming/Arch/Rp2040/Components/uf2/uf2conv.md new file mode 100644 index 0000000000..052f9c1ec1 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/uf2/uf2conv.md @@ -0,0 +1,35 @@ +# uf2conv -- Packing and unpacking UF2 files + +## SYNOPSIS + +This tool is based on the code at https://github.com/Microsoft/uf2 +but has been modified for use with Sming and the RP2040: + + - Handles multiple binary inputs using addr=content format + - Locations specified as offsets, relative to base address + - Defaults are set for RP2040, but can be changed (family ID and base address) + - Content is padded to flash sector size (4096 bytes) - required by RP2040 bootrom + - Content is padded with 0xff (not 0) as this represents erased (unprogrammed) flash location + - Renamed 'deploy' as 'upload' and added progress indicator + - Support for hex files and 'C' arrays removed + +Run `uf2conv.py -h` for full list of options. + +## EXAMPLES + +### Pack binary file(s) to .uf2 + +Specify source using `offset=content` values: + +```uf2conv.py --convert 0x2000=cpx/firmware.bin 0x4000=cpx/partitions.bin --output cpx/upload.uf2``` + +Offsets are relative to the start of flash memory. + +### Unpack a .uf2 file + +```uf2conv.py current.uf2 --output current.bin``` + +Produces a list of `current-{addr}.bin` files for each chunk found in the .uf2 source file. +Without the `--output` option just displays a chunk summary. + +Use the `--verbose` option to display details for every block in the file. diff --git a/Sming/Arch/Rp2040/Components/uf2/uf2conv.py b/Sming/Arch/Rp2040/Components/uf2/uf2conv.py new file mode 100644 index 0000000000..e51f4b7437 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/uf2/uf2conv.py @@ -0,0 +1,400 @@ +#!/usr/bin/env python3 +# +# uf2conv.py +# +# Tool for packing and unpacking UF2 files +# + +import sys, struct, os, re, subprocess, argparse, json, time + +# Sizes +UF2_BLOCK_SIZE = 512 +UF2_PAYLOAD_SIZE = 256 +UF2_MAX_PAYLOAD_SIZE = 476 +UF2_HEADER_SIZE = 32 +UF2_FOOTER_SIZE = 4 +# Header +UF2_MAGIC_START0 = 0x0A324655 # "UF2\n" +UF2_MAGIC_START1 = 0x9E5D5157 # Randomly selected +# Footer +UF2_MAGIC_END = 0x0AB16F30 # Ditto +# Flag bits +UF2_FLAG_NOFLASH = 0x00000001 # Block is "comment", do not flash +UF2_FLAG_FILE_CONTAINER = 0x00001000 +UF2_FLAG_FAMILY_ID = 0x00002000 +UF2_FLAG_MD5_CHECKSUM = 0x00004000 +UF2_FLAG_EXTENSION_TAGS = 0x00008000 # Block padding contains extension tags + +# Info file +INFO_FILE = "/INFO_UF2.TXT" +INFO_MODEL = "Model" +INFO_BOARD_ID = "Board-ID" + +DEFAULT_FAMILY_ID = "RP2040" +DEFAULT_BASE_ADDR = 0x10000000 # Flash window start address + +DEFAULT_WAIT_SECS = 20 + +# Content must be padded to complete flash sectors. Bootloader messes up otherwise. +FLASH_SECTOR_SIZE = 4096 + +# Global variables +base_address = 0 +familyid = 0 +verbose = False + +def is_uf2(buf): + w = struct.unpack(" UF2_MAX_PAYLOAD_SIZE: + error("Invalid UF2 data size at 0x%08x" % offset) + newaddr = hd[3] + if chunk_offset is None or curraddr != newaddr: + chunk_offset = newaddr - base_address + output[chunk_offset] = bytes() + if (hd[2] & UF2_FLAG_FAMILY_ID) and currfamilyid is None: + currfamilyid = hd[7] + if curraddr is None or ((hd[2] & UF2_FLAG_FAMILY_ID) and hd[7] != currfamilyid): + currfamilyid = hd[7] + curraddr = newaddr + if familyid == 0x0 or ((hd[2] & UF2_FLAG_FAMILY_ID) and familyid == hd[7]): + output[chunk_offset] += block[UF2_HEADER_SIZE : UF2_HEADER_SIZE + datalen] + curraddr = newaddr + datalen + if hd[2] & UF2_FLAG_FAMILY_ID: + if hd[7] in families_found.keys(): + if families_found[hd[7]] > newaddr: + families_found[hd[7]] = newaddr + else: + families_found[hd[7]] = newaddr + if prev_flag is None: + prev_flag = hd[2] + if prev_flag != hd[2]: + all_flags_same = False + + print("----- UF2 File Header Info -----") + families = load_families() + for family_hex in families_found.keys(): + family_short_name = "" + for name, value in families.items(): + if value == family_hex: + family_short_name = name + print("Family ID is %s, hex value is 0x%08x" % (family_short_name, family_hex)) + print("Target Address is 0x%08x" % families_found[family_hex]) + if all_flags_same: + print("All block flag values consistent, 0x%04x" % hd[2]) + else: + print("Flags were not all the same") + print(" Offset Size") + for addr, content in output.items(): + print(" 0x%08x 0x%06x %u" % (addr, len(content), len(content))) + print("--------------------------------") + if len(families_found) > 1 and familyid == 0x0: + output = [] + + return output + + +def getSectorCount(length): + return (length + FLASH_SECTOR_SIZE - 1) // FLASH_SECTOR_SIZE + + +def convert_to_uf2(content_map): + global familyid + global base_address + + numblocks = 0 + for addr, content in content_map.items(): + numblocks += getSectorCount(len(content)) * FLASH_SECTOR_SIZE // UF2_PAYLOAD_SIZE + + outp = [] + blockno = 0 + for addr in sorted(content_map): + content = bytearray(content_map[addr]) + length = len(content) + padding = (getSectorCount(length) * FLASH_SECTOR_SIZE) - length + content.extend(bytearray([0xff] * padding)) + length = len(content) + addr += base_address + offset = 0 + while offset < length: + chunk = content[offset:offset + UF2_PAYLOAD_SIZE] + flags = 0x0 + if familyid: + flags |= 0x2000 + block = struct.pack(b"= 3 and words[1] == "2" and words[2] == "FAT": + drives.append(words[0]) + else: + rootpath = "/media" + if sys.platform == "darwin": + rootpath = "/Volumes" + elif sys.platform == "linux": + tmp = rootpath + "/" + os.environ["USER"] + if os.path.isdir(tmp): + rootpath = tmp + for d in os.listdir(rootpath): + drives.append(os.path.join(rootpath, d)) + + def has_info(d): + try: + return os.path.isfile(d + INFO_FILE) + except: + return False + + return list(filter(has_info, drives)) + + +def board_info(path): + infofile = path + INFO_FILE + if not os.path.isfile(infofile): + return None + with open(infofile, mode='r') as f: + lines = f.readlines() + if len(lines) < 2: + return None + res = {} + res['header'] = lines[0].strip() + for line in lines[1:]: + k, v = line.split(': ') + res[k] = v.strip() + return res + + +def list_drives(): + drives = get_drives() + if len(drives) == 0: + print("No drives found.") + return + for d in drives: + info = board_info(d) + print("'%s' %s (%s)" % (d, info[INFO_BOARD_ID], info[INFO_MODEL])) + if verbose: + for k, v in info.items(): + print(" %s = %s" % (k, v)) + + +def write_file(name, content): + with open(name, "wb") as f: + f.write(content) + print("Wrote '%s', %u bytes" % (name, len(content))) + + +def upload_file(drive, content): + """Gives progress indication.""" + blockSize = UF2_BLOCK_SIZE * 32 + blockCount = (len(content) + blockSize - 1 ) // blockSize + offset = 0 + lastpercent = -1 + + # https://stackoverflow.com/questions/3173320/text-progress-bar-in-the-console + def progress(): + nonlocal lastpercent + block = (offset + blockSize - 1) // blockSize + percent = round(100 * block / blockCount) + if percent == lastpercent: + return + lastpercent = percent + + prefix = 'Progress:' + suffix = 'Complete' + length = 50 + fill = 'â–ˆ' + filledLength = round(length * percent / 100) + bar = fill * filledLength + '-' * (length - filledLength) + print(f'\r{prefix} |{bar}| {percent}% {suffix}', end = "\r") + + with open(drive + "/NEW.UF2", "wb") as f: + while offset < len(content): + f.write(content[offset:offset+blockSize]) + offset += blockSize + progress() + + print() + + +def load_families(): + # The expectation is that the `uf2families.json` file is in the same + # directory as this script. Make a path that works using `__file__` + # which contains the full path to this script. + filename = "uf2families.json" + pathname = os.path.join(os.path.dirname(os.path.abspath(__file__)), filename) + with open(pathname) as f: + raw_families = json.load(f) + + families = {} + for family in raw_families: + families[family["short_name"]] = int(family["id"], 0) + + return families + + +def error(msg): + print(msg) + sys.exit(1) + + +def main(): + global familyid + global base_address + global verbose + + parser = argparse.ArgumentParser(description='Utility to manage UF2 file conversion and flashing.') + parser.add_argument('-b' , '--base', dest='base', type=str, + default="0x%08x" % DEFAULT_BASE_ADDR, + help='set base address for chunks (default: 0x%08x)' % DEFAULT_BASE_ADDR) + parser.add_argument('input', metavar='INPUT', type=str, nargs='*', + help='Name of UF2 file to read, or addr=content pairs for source binary data') + parser.add_argument('-o' , '--output', metavar="FILE", dest='output', type=str, + help='Write output to named file. Defaults to "flash.uf2" or "flash.bin" as appropriate.') + parser.add_argument('-d' , '--device', dest="device_path", + help='Select a device path to flash. If not specified will auto-detect.') + parser.add_argument('-w' , '--wait', dest="wait_secs", type=int, + default=DEFAULT_WAIT_SECS, + help='Time in seconds to wait for target device connection') + parser.add_argument('-l' , '--list', action='store_true', + help='List connected devices') + parser.add_argument('-c' , '--convert', action='store_true', + help='Convert list of "addr=file" binary source files into single .uf2 file') + parser.add_argument('-u' , '--upload', action='store_true', + help='Just flash, do not convert') + parser.add_argument('-f' , '--family', dest='family', type=str, + default=DEFAULT_FAMILY_ID, + help='Specify familyID number or name (default: %s)' % DEFAULT_FAMILY_ID) + parser.add_argument('-v', '--verbose', action='store_true', + help='enable verbose console messages') + args = parser.parse_args() + + base_address = int(args.base, 0) + verbose = args.verbose + + families = load_families() + familyid = families.get(args.family.upper()) + if familyid is None: + try: + familyid = int(args.family, 0) + except ValueError: + error("Family ID needs to be a number or one of: " + ", ".join(families.keys())) + + if args.list: + list_drives() + return + + if not args.input: + parser.print_usage() + error("Need input file(s)") + + source_map = None + output_map = None + uf2 = None + if len(args.input) == 1 and args.input[0].endswith('.uf2') and not args.convert: + filename = args.input[0] + with open(filename, mode='rb') as f: + inpbuf = f.read() + if not is_uf2(inpbuf): + error("Not a UF2 file: %s" % filename) + output_map = convert_from_uf2(inpbuf) + else: + source_map = {} + for e in args.input: + try: + addr, srcfile = e.split('=') + addr = int(addr, 0) + except ValueError: + error("Invalid parameter '%s', expecting addr=content pair" % e) + with open(srcfile, mode='rb') as f: + content = f.read() + source_map[addr] = content + uf2 = convert_to_uf2(source_map) + + if args.output: + if uf2 is not None: + write_file(args.output, uf2) + if output_map is not None: + base = os.path.splitext(args.output) + for addr, content in output_map.items(): + filename = "%s-0x%08x%s" % (base[0], addr, base[1]) + with open(filename, "wb") as f: + f.write(content) + print("Wrote '%s', %u bytes" % (filename, len(content))) + + if args.upload or args.device_path is not None: + d = None + info = None + for attempt in range(args.wait_secs, 0, -1): + print("Waiting for target device to enter boot mode... %u " % attempt, end='\r') + sys.stdout.flush() + d = args.device_path + if d is None: + drives = get_drives() + if len(drives) != 0: + d = drives[0] + if d is not None: + info = board_info(d) + if info is not None: + break + time.sleep(1) + + if info is None: + error("\nUF2 drive not found!") + + print("Flashing to '%s' (%s / %s)" % (d, info[INFO_MODEL], info[INFO_BOARD_ID])) + upload_file(d, uf2) + + +if __name__ == "__main__": + main() diff --git a/Sming/Arch/Rp2040/Components/uf2/uf2families.json b/Sming/Arch/Rp2040/Components/uf2/uf2families.json new file mode 100644 index 0000000000..42b5bbc2a2 --- /dev/null +++ b/Sming/Arch/Rp2040/Components/uf2/uf2families.json @@ -0,0 +1,172 @@ +[ + { + "id": "0x16573617", + "short_name": "ATMEGA32", + "description": "Microchip (Atmel) ATmega32" + }, + { + "id": "0x1851780a", + "short_name": "SAML21", + "description": "Microchip (Atmel) SAML21" + }, + { + "id": "0x1b57745f", + "short_name": "NRF52", + "description": "Nordic NRF52" + }, + { + "id": "0x1c5f21b0", + "short_name": "ESP32", + "description": "ESP32" + }, + { + "id": "0x1e1f432d", + "short_name": "STM32L1", + "description": "ST STM32L1xx" + }, + { + "id": "0x202e3a91", + "short_name": "STM32L0", + "description": "ST STM32L0xx" + }, + { + "id": "0x21460ff0", + "short_name": "STM32WL", + "description": "ST STM32WLxx" + }, + { + "id": "0x2abc77ec", + "short_name": "LPC55", + "description": "NXP LPC55xx" + }, + { + "id": "0x300f5633", + "short_name": "STM32G0", + "description": "ST STM32G0xx" + }, + { + "id": "0x31d228c6", + "short_name": "GD32F350", + "description": "GD32F350" + }, + { + "id": "0x04240bdf", + "short_name": "STM32L5", + "description": "ST STM32L5xx" + }, + { + "id": "0x4c71240a", + "short_name": "STM32G4", + "description": "ST STM32G4xx" + }, + { + "id": "0x4fb2d5bd", + "short_name": "MIMXRT10XX", + "description": "NXP i.MX RT10XX" + }, + { + "id": "0x53b80f00", + "short_name": "STM32F7", + "description": "ST STM32F7xx" + }, + { + "id": "0x55114460", + "short_name": "SAMD51", + "description": "Microchip (Atmel) SAMD51" + }, + { + "id": "0x57755a57", + "short_name": "STM32F4", + "description": "ST STM32F401" + }, + { + "id": "0x5a18069b", + "short_name": "FX2", + "description": "Cypress FX2" + }, + { + "id": "0x5d1a0a2e", + "short_name": "STM32F2", + "description": "ST STM32F2xx" + }, + { + "id": "0x5ee21072", + "short_name": "STM32F1", + "description": "ST STM32F103" + }, + { + "id": "0x647824b6", + "short_name": "STM32F0", + "description": "ST STM32F0xx" + }, + { + "id": "0x68ed2b88", + "short_name": "SAMD21", + "description": "Microchip (Atmel) SAMD21" + }, + { + "id": "0x6b846188", + "short_name": "STM32F3", + "description": "ST STM32F3xx" + }, + { + "id": "0x6d0922fa", + "short_name": "STM32F407", + "description": "ST STM32F407" + }, + { + "id": "0x6db66082", + "short_name": "STM32H7", + "description": "ST STM32H7xx" + }, + { + "id": "0x70d16653", + "short_name": "STM32WB", + "description": "ST STM32WBxx" + }, + { + "id": "0x7eab61ed", + "short_name": "ESP8266", + "description": "ESP8266" + }, + { + "id": "0x7f83e793", + "short_name": "KL32L2", + "description": "NXP KL32L2x" + }, + { + "id": "0x8fb060fe", + "short_name": "STM32F407VG", + "description": "ST STM32F407VG" + }, + { + "id": "0xada52840", + "short_name": "NRF52840", + "description": "Nordic NRF52840" + }, + { + "id": "0xbfdd4eee", + "short_name": "ESP32S2", + "description": "ESP32-S2" + }, + { + "id": "0xc47e5767", + "short_name": "ESP32S3", + "description": "ESP32-S3" + }, + { + "id": "0xd42ba06c", + "short_name": "ESP32C3", + "description": "ESP32-C3" + }, + { + "id": "0xe48bff56", + "short_name": "RP2040", + "description": "Raspberry Pi RP2040" + }, + { + "id": "0x00ff6919", + "short_name": "STM32L4", + "description": "ST STM32L4xx" + } +] \ No newline at end of file diff --git a/Sming/Arch/Rp2040/Core/.cs b/Sming/Arch/Rp2040/Core/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Arch/Rp2040/Core/Digital.cpp b/Sming/Arch/Rp2040/Core/Digital.cpp new file mode 100644 index 0000000000..9bc6e998d1 --- /dev/null +++ b/Sming/Arch/Rp2040/Core/Digital.cpp @@ -0,0 +1,132 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http:// github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Digital.cpp + * + * Based on https:// github.com/earlephilhower/arduino-pico/blob/master/cores/rp2040/wiring_digital.cpp + * + ****/ + +#include +#include +#include +#include + +namespace +{ +constexpr uint32_t PIN_COUNT = NUM_BANK0_GPIOS; + +#define CHECK_PIN(pin_number, ...) \ + if(pin_number >= PIN_COUNT) { \ + SYSTEM_ERROR("ERROR: Illegal pin in %s (%d)", __FUNCTION__, pin_number); \ + return __VA_ARGS__; \ + } + +} // namespace + +void pinMode(uint16_t pin, uint8_t mode) +{ + CHECK_PIN(pin) + + switch(mode) { + case INPUT: + gpio_init(pin); + gpio_set_dir(pin, GPIO_IN); + gpio_disable_pulls(pin); + break; + case INPUT_PULLUP: + gpio_init(pin); + gpio_set_dir(pin, GPIO_IN); + gpio_pull_up(pin); + gpio_put(pin, 0); + break; + case INPUT_PULLDOWN: + gpio_init(pin); + gpio_set_dir(pin, GPIO_IN); + gpio_pull_down(pin); + gpio_put(pin, 1); + break; + case OUTPUT: + gpio_init(pin); + gpio_set_dir(pin, GPIO_OUT); + gpio_disable_pulls(pin); + break; + default: + SYSTEM_ERROR("ERROR: Illegal pinMode mode (%d)", mode); + return; + } +} + +bool isInputPin(uint16_t pin) +{ + CHECK_PIN(pin, false) + + return !gpio_is_dir_out(pin); +} + +void digitalWrite(uint16_t pin, uint8_t val) +{ + CHECK_PIN(pin) + + if(gpio_is_pulled_down(pin)) { + gpio_set_dir(pin, val != LOW); + } else if(gpio_is_pulled_up(pin)) { + gpio_set_dir(pin, val != HIGH); + } else { + gpio_put(pin, val != LOW); + } +} + +uint8_t digitalRead(uint16_t pin) +{ + CHECK_PIN(pin, LOW) + return gpio_get(pin); +} + +void pullup(uint16_t pin) +{ + CHECK_PIN(pin) + gpio_pull_up(pin); +} + +void noPullup(uint16_t pin) +{ + CHECK_PIN(pin) + gpio_disable_pulls(pin); +} + +unsigned long pulseIn(uint16_t pin, uint8_t state, unsigned long timeout) +{ + CHECK_PIN(pin, 0) + + OneShotFastUs timeout_timer; + timeout_timer.reset(timeout); + + // Wait for deassert, if needed + while(gpio_get(pin) != !state) { + if(timeout_timer.expired()) { + return 0; + } + } + + // Wait for assert + while(gpio_get(pin) != !!state) { + if(timeout_timer.expired()) { + return 0; + } + } + + OneShotFastUs timer; + + // Wait for deassert + while(gpio_get(pin) != !state) { + if(timeout_timer.expired()) { + return 0; + } + } + + return timer.elapsedTicks(); +} diff --git a/Sming/Arch/Rp2040/Core/HardwarePWM.cpp.todo b/Sming/Arch/Rp2040/Core/HardwarePWM.cpp.todo new file mode 100644 index 0000000000..fc5e832147 --- /dev/null +++ b/Sming/Arch/Rp2040/Core/HardwarePWM.cpp.todo @@ -0,0 +1,136 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * HardwarePWM.cpp + * + * Original Author: https://github.com/hrsavla + * + * This HardwarePWM library enables Sming framework user to use ESP SDK PWM API + * Period of PWM is fixed to 1000us / Frequency = 1khz + * Duty at 100% = 22222. Duty at 0% = 0 + * You can use function setPeriod() to change frequency/period. + * Calculate the max duty as per the formulae give in ESP8266 SDK + * Max Duty = (Period * 1000) / 45 + * + * PWM can be generated on upto 8 pins (ie All pins except pin 16) + * Created on August 17, 2015, 2:27 PM + * + * See also ESP8266 Technical Reference, Chapter 12: + * http://espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf + * + */ + +#include +#include "ESP8266EX.h" + +#include + +#define PERIOD_TO_MAX_DUTY(x) (x * 25) + +HardwarePWM::HardwarePWM(uint8* pins, uint8 no_of_pins) : channel_count(no_of_pins) +{ + if(no_of_pins > 0) { + uint32 io_info[PWM_CHANNEL_NUM_MAX][3]; // pin information + uint32 pwm_duty_init[PWM_CHANNEL_NUM_MAX]; // pwm duty + for(uint8 i = 0; i < no_of_pins; i++) { + io_info[i][0] = EspDigitalPins[pins[i]].mux; + io_info[i][1] = EspDigitalPins[pins[i]].gpioFunc; + io_info[i][2] = EspDigitalPins[pins[i]].id; + pwm_duty_init[i] = 0; // Start with zero output + channels[i] = pins[i]; + } + const int initial_period = 1000; + pwm_init(initial_period, pwm_duty_init, no_of_pins, io_info); + update(); + maxduty = PERIOD_TO_MAX_DUTY(initial_period); // for period of 1000 + } +} + +HardwarePWM::~HardwarePWM() +{ + // There is no function in the SDK to stop PWM output, yet. +} + +/* Function Name: getChannel + * Description: This function is used to get channel number for given pin + * Parameters: pin - Esp8266 pin number + */ +uint8 HardwarePWM::getChannel(uint8 pin) +{ + for(uint8 i = 0; i < channel_count; i++) { + if(channels[i] == pin) { + //debugf("getChannel %d is %d", pin, i); + return i; + } + } + //debugf("getChannel: can't find pin %d", pin); + return PWM_BAD_CHANNEL; +} + +/* Function Name: getDutyChan + * Description: This function is used to get the duty cycle number for a given channel + * Parameters: chan -Esp8266 channel number + */ +uint32 HardwarePWM::getDutyChan(uint8 chan) +{ + if(chan == PWM_BAD_CHANNEL) { + return 0; + } else { + return pwm_get_duty(chan); + } +} + +/* Function Name: setDutyChan + * Description: This function is used to set the pwm duty cycle for a given channel. If parameter 'update' is false + * then you have to call update() later to update duties. + * Parameters: chan - channel number + * duty - duty cycle value + * update - update PWM output + */ +bool HardwarePWM::setDutyChan(uint8 chan, uint32 duty, bool update) +{ + if(chan == PWM_BAD_CHANNEL) { + return false; + } else if(duty <= maxduty) { + pwm_set_duty(duty, chan); + if(update) { + this->update(); + } + return true; + } else { + debugf("Duty cycle value too high for current period."); + return false; + } +} + +/* Function Name: getPeriod + * Description: This function is used to get Period of PWM. + * Period / frequency will remain same for all pins. + * + */ +uint32 HardwarePWM::getPeriod() +{ + return pwm_get_period(); +} + +/* Function Name: setPeriod + * Description: This function is used to set Period of PWM. + * Period / frequency will remain same for all pins. + */ +void HardwarePWM::setPeriod(uint32 period) +{ + maxduty = PERIOD_TO_MAX_DUTY(period); + pwm_set_period(period); + update(); +} + +/* Function Name: update + * Description: This function is used to actually update the PWM. + */ +void HardwarePWM::update() +{ + pwm_start(); +} diff --git a/Sming/Arch/Rp2040/Core/Interrupts.cpp b/Sming/Arch/Rp2040/Core/Interrupts.cpp new file mode 100644 index 0000000000..133c22a939 --- /dev/null +++ b/Sming/Arch/Rp2040/Core/Interrupts.cpp @@ -0,0 +1,130 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Interrupts.cpp + * + ****/ + +#include +#include +#include +#include + +namespace +{ +constexpr unsigned PIN_COUNT = NUM_BANK0_GPIOS; + +struct Handler { + enum class Type { + none, + interrupt, + delegate, + }; + union { + InterruptCallback interrupt; + InterruptDelegate* delegate; + }; + Type type; + + void reset() + { + if(type == Type::delegate) { + delete delegate; + } + type = Type::none; + } + + void setInterrupt(InterruptCallback interrupt) + { + reset(); + this->interrupt = interrupt; + type = Type::interrupt; + } + + void setDelegate(InterruptDelegate delegate) + { + reset(); + this->delegate = new InterruptDelegate(delegate); + type = Type::delegate; + } +}; + +Handler handlers[PIN_COUNT]{}; +bool interruptHandlerAttached; + +#define CHECK_PIN(pin_number, ...) \ + if(pin_number >= PIN_COUNT) { \ + SYSTEM_ERROR("ERROR: Illegal pin in %s (%d)", __FUNCTION__, pin_number); \ + return __VA_ARGS__; \ + } + +void interruptDelegateCallback(uint32_t gpio) +{ + auto& handler = handlers[gpio]; + if(handler.type == Handler::Type::delegate) { + (*handler.delegate)(); + } +} + +void IRAM_ATTR interruptHandler(uint gpio, uint32_t events) +{ + auto& handler = handlers[gpio]; + if(handler.type == Handler::Type::interrupt) { + handler.interrupt(); + } else if(handler.type == Handler::Type::delegate) { + System.queueCallback(interruptDelegateCallback, gpio); + } +} + +} // namespace + +void attachInterrupt(uint8_t pin, InterruptCallback callback, GPIO_INT_TYPE type) +{ + CHECK_PIN(pin) + + auto& handler = handlers[pin]; + handler.setInterrupt(callback); + attachInterruptHandler(pin, type); +} + +void attachInterrupt(uint8_t pin, InterruptDelegate delegateFunction, GPIO_INT_TYPE type) +{ + CHECK_PIN(pin) + + auto& handler = handlers[pin]; + handler.setDelegate(delegateFunction); + attachInterruptHandler(pin, type); +} + +void attachInterruptHandler(uint8_t pin, GPIO_INT_TYPE type) +{ + CHECK_PIN(pin) + + pinMode(pin, INPUT); + + if(interruptHandlerAttached) { + gpio_set_irq_enabled(pin, type, true); + } else { + gpio_set_irq_enabled_with_callback(pin, type, true, interruptHandler); + interruptHandlerAttached = true; + } +} + +void detachInterrupt(uint8_t pin) +{ + CHECK_PIN(pin) + + gpio_set_irq_enabled(pin, 0, false); + handlers[pin].reset(); +} + +void interruptMode(uint8_t pin, GPIO_INT_TYPE type) +{ + CHECK_PIN(pin) + + pinMode(pin, INPUT); + gpio_set_irq_enabled(pin, type, type != 0); +} diff --git a/Sming/Arch/Rp2040/Core/SPI.cpp.todo b/Sming/Arch/Rp2040/Core/SPI.cpp.todo new file mode 100644 index 0000000000..a566605f3f --- /dev/null +++ b/Sming/Arch/Rp2040/Core/SPI.cpp.todo @@ -0,0 +1,345 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * SPI.cpp + * + * Created on: Mar 2, 2016 + * Author: harry-boe + * + * Some code is derived from: + * David Ogilvy (MetalPhreak) + * + ****/ + +#include "SPI.h" +#include +#include "espinc/eagle_soc.h" +#include "espinc/spi_register.h" + +// define the static singleton +SPIClass SPI; + +namespace +{ +// Used internally to calculate optimum SPI speed +struct SpiPreDiv { + unsigned freq; + unsigned prescale; + unsigned divisor; +}; + +/** + * @brief Wait until HSPI has finished any current transaction + */ +__forceinline void spi_wait() +{ + while(READ_PERI_REG(SPI_CMD(SPI_NO)) & SPI_USR) { + // + } +} + +/** + * @brief Initiate an HSPI user transaction + */ +__forceinline void spi_send() +{ + SET_PERI_REG_MASK(SPI_CMD(SPI_NO), SPI_USR); +} + +/** + * @brief Configure SPI mode parameters for clock edge and clock polarity. + * + * Private method used by SPISetings + * + * @param SPI_MODE0 .. SPI_MODE4 + * + * Mode Clock Polarity (CPOL) Clock Phase (CPHA) + * SPI_MODE0 0 0 + * SPI_MODE1 0 1 + * SPI_MODE2 1 0 + * SPI_MODE3 1 1 + */ +void spi_mode(uint8_t mode) +{ + uint8_t spi_cpha = mode & 0x0F; + uint8_t spi_cpol = mode & 0xF0; + +#ifdef SPI_DEBUG + debugf("SPIClass::spi_mode(mode %x) spi_cpha %X,spi_cpol %X)", mode, spi_cpha, spi_cpol); +#endif + + if(spi_cpha == spi_cpol) { + CLEAR_PERI_REG_MASK(SPI_USER(SPI_NO), SPI_CK_OUT_EDGE); + } else { + SET_PERI_REG_MASK(SPI_USER(SPI_NO), SPI_CK_OUT_EDGE); + } + + if(spi_cpol) { + SET_PERI_REG_MASK(SPI_PIN(SPI_NO), SPI_IDLE_EDGE); + } else { + CLEAR_PERI_REG_MASK(SPI_PIN(SPI_NO), SPI_IDLE_EDGE); + } +} + +/** + * @brief Setup the byte order for shifting data out of buffer + * + * Private method used by SPISetings + * + * @param MSBFIRST 1 + * Data is sent out starting with Bit31 and down to Bit0 + * LSBFIRST 0 + * Data is sent out starting with the lowest BYTE, from MSB to LSB + * 0xABCDEFGH would be sent as 0xGHEFCDAB + */ +void spi_byte_order(uint8_t byte_order) +{ +#ifdef SPI_DEBUG + debugf("SPIClass::spi_byte_order(byte_order %u)", byte_order); +#endif + + if(byte_order) { + SET_PERI_REG_MASK(SPI_USER(SPI_NO), SPI_WR_BYTE_ORDER); + SET_PERI_REG_MASK(SPI_USER(SPI_NO), SPI_RD_BYTE_ORDER); + } else { + CLEAR_PERI_REG_MASK(SPI_USER(SPI_NO), SPI_WR_BYTE_ORDER); + CLEAR_PERI_REG_MASK(SPI_USER(SPI_NO), SPI_RD_BYTE_ORDER); + } +} + +/** + * @brief Calculate the closest prescale value for a given frequency and clock-divider + * @param cpuFreq current CPU frequency, in Hz + * @param freq target SPI bus frequency, in Hz + * @param div divisor value to use + * @retval SpiPreDiv contains resulting frequency, prescaler and divisor values + */ +SpiPreDiv calculateSpeed(unsigned cpuFreq, unsigned freq, unsigned div) +{ + SpiPreDiv prediv; + unsigned pre = cpuFreq / (freq * div); + if(pre == 0) { + pre = 1; + } + unsigned n = pre * div; + while(true) { + prediv.freq = cpuFreq / n; + if(prediv.freq <= freq) { + break; + } + ++pre; + n += div; + } + prediv.prescale = pre; + prediv.divisor = div; + +#ifdef SPI_DEBUG + debugf("SPI calculateSpeed(uint freq %u, uint pre %u, uint div %u)", f, pre, div); +#endif + + return prediv; +} + +/** @brief Check speed settings and perform any pre-calculation required + * @param speed IN: requested bus frequency, OUT: Modified settings with prescale values + * @note + * The algorithm is testing with clock dividers 2,3 and 5 to find the best pre-divider + * The resulting clock frequency is not 100% accurate but delivers result within 5% + * + * It is guaranteed that the frequency will not exceed the given target + * + * Make sure that the ESP clock frequency is set before initializing the SPI bus. + * Changes on the ESP clock are not recognised once initialized + */ +void checkSpeed(SPISpeed& speed) +{ + unsigned cpuFreq = system_get_cpu_freq() * 1000000UL; +#ifdef SPI_DEBUG + debugf("SPIClass::calculateSpeed() -> current cpu frequency %u", cpuFreq); +#endif + + SpiPreDiv prediv; + + // If we're not running at max then need to determine appropriate prescale values + if(speed.frequency >= cpuFreq) { + // Use maximum speed + prediv.freq = cpuFreq; + prediv.divisor = 0; + speed.regVal = SPI_CLK_EQU_SYSCLK; + } else { + prediv = calculateSpeed(cpuFreq, speed.frequency, 2); + if(prediv.freq != speed.frequency) { + // Use whichever divisor gives the highest frequency + SpiPreDiv pd3 = calculateSpeed(cpuFreq, speed.frequency, 3); + SpiPreDiv pd5 = calculateSpeed(cpuFreq, speed.frequency, 5); + if(pd3.freq > prediv.freq || pd5.freq > prediv.freq) { + prediv = (pd3.freq > pd5.freq) ? pd3 : pd5; + } + } + + // We have prescale and divisor values, now get regVal so we don't need to do this every time prepare() is called + speed.regVal = (((prediv.prescale - 1) & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) | + (((prediv.divisor - 1) & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) | + (((prediv.divisor >> 1) & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) | + ((0 & SPI_CLKCNT_L) << SPI_CLKCNT_L_S); + } + + //#ifdef SPI_DEBUG + debug_e("-> Using clock divider %u -> target freq %u -> result %u", prediv.divisor, speed.frequency, prediv.freq); + //#endif + + speed.frequency = prediv.freq; +} + +void spi_set_clock(SPISpeed& speed) +{ + // Clock register value is never 0, so indicates it hasn't been calculated + if(speed.regVal == 0) { + checkSpeed(speed); + } else { +#ifdef SPI_DEBUG + unsigned prescale = (speed.regVal >> SPI_CLKDIV_PRE_S) + 1; + unsigned divisor = (speed.regVal >> SPI_CLKCNT_N_S) + 1; + debugf("spi_set_clock(prescaler %u, divisor %u) for target %u", prescale, divisor, speed.frequency); +#endif + } + + WRITE_PERI_REG(SPI_CLOCK(SPI_NO), speed.regVal); +} + +} // namespace + +bool SPIClass::begin() +{ + CLEAR_PERI_REG_MASK(PERIPHS_IO_MUX, BIT9); + + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2); // HSPIQ MISO == GPIO12 + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2); // HSPID MOSI == GPIO13 + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2); // CLK == GPIO14 + + checkSpeed(SPIDefaultSettings.speed); + prepare(SPIDefaultSettings); + + return true; +} + +uint32_t SPIClass::transfer32(uint32_t data, uint8_t bits) +{ + uint32_t regvalue = READ_PERI_REG(SPI_USER(SPI_NO)) & (SPI_WR_BYTE_ORDER | SPI_RD_BYTE_ORDER | SPI_CK_OUT_EDGE); + + spi_wait(); + + regvalue |= SPI_USR_MOSI | SPI_DOUTDIN | SPI_CK_I_EDGE; + WRITE_PERI_REG(SPI_USER(SPI_NO), regvalue); + + WRITE_PERI_REG(SPI_USER1(SPI_NO), (((bits - 1) & SPI_USR_MOSI_BITLEN) << SPI_USR_MOSI_BITLEN_S) | + (((bits - 1) & SPI_USR_MISO_BITLEN) << SPI_USR_MISO_BITLEN_S)); + + // copy data to W0 + if(READ_PERI_REG(SPI_USER(SPI_NO)) & SPI_WR_BYTE_ORDER) { + WRITE_PERI_REG(SPI_W0(SPI_NO), data << (32 - bits)); + } else { + WRITE_PERI_REG(SPI_W0(SPI_NO), data); + } + + spi_send(); + spi_wait(); + + auto res = READ_PERI_REG(SPI_W0(SPI_NO)); + if(READ_PERI_REG(SPI_USER(SPI_NO)) & SPI_RD_BYTE_ORDER) { + res >>= (32 - bits); + } + + return res; +} + +uint8_t SPIClass::read8() +{ + spi_wait(); + + WRITE_PERI_REG(SPI_W0(SPI_NO), 0x00); + + spi_send(); + spi_wait(); + + auto res = READ_PERI_REG(SPI_W0(SPI_NO)); + if(READ_PERI_REG(SPI_USER(SPI_NO)) & SPI_RD_BYTE_ORDER) { + res >>= 24; + } + + return res; +} + +void SPIClass::transfer(uint8_t* buffer, size_t numberBytes) +{ +#define BLOCKSIZE 64U // the max length of the ESP SPI_W0 registers + + unsigned bufIndx = 0; + + unsigned blocks = ((numberBytes - 1) / BLOCKSIZE) + 1; +#ifdef SPI_DEBUG + unsigned total = blocks; +#endif + + // loop number of blocks + while(blocks--) { + // get full BLOCKSIZE or number of remaining bytes + auto bufLength = std::min(numberBytes - bufIndx, BLOCKSIZE); + +#ifdef SPI_DEBUG + debugf("Write/Read Block %u total %u bytes", total - blocks, bufLength); +#endif + + // compute the number of bits to clock + auto num_bits = bufLength * 8; + + uint32_t regvalue = READ_PERI_REG(SPI_USER(SPI_NO)) & (SPI_WR_BYTE_ORDER | SPI_RD_BYTE_ORDER | SPI_CK_OUT_EDGE); + + spi_wait(); + + regvalue |= SPI_USR_MOSI | SPI_DOUTDIN | SPI_CK_I_EDGE; + WRITE_PERI_REG(SPI_USER(SPI_NO), regvalue); + + // setup bit length + WRITE_PERI_REG(SPI_USER1(SPI_NO), (((num_bits - 1) & SPI_USR_MOSI_BITLEN) << SPI_USR_MOSI_BITLEN_S) | + (((num_bits - 1) & SPI_USR_MISO_BITLEN) << SPI_USR_MISO_BITLEN_S)); + + // copy the registers starting from last index position + if(IS_ALIGNED(buffer)) { + memcpy((void*)SPI_W0(SPI_NO), &buffer[bufIndx], ALIGNUP4(bufLength)); + } else { + uint32_t wordBuffer[BLOCKSIZE / 4]; + memcpy(wordBuffer, &buffer[bufIndx], bufLength); + memcpy((void*)SPI_W0(SPI_NO), wordBuffer, ALIGNUP4(bufLength)); + } + + spi_send(); + spi_wait(); + + // copy the registers starting from last index position + memcpy(&buffer[bufIndx], (void*)SPI_W0(SPI_NO), bufLength); + + // increment bufIndex + bufIndx += bufLength; + } +} + +void SPIClass::prepare(SPISettings& settings) +{ +#ifdef SPI_DEBUG + debugf("SPIClass::prepare(SPISettings)"); + settings.print("settings"); +#endif + + // setup clock + spi_set_clock(settings.speed); + + // set byte order + spi_byte_order(settings.byteOrder); + + // set spi mode + spi_mode(settings.dataMode); +} diff --git a/Sming/Arch/Rp2040/Core/SPI.h b/Sming/Arch/Rp2040/Core/SPI.h new file mode 100644 index 0000000000..e6ad930196 --- /dev/null +++ b/Sming/Arch/Rp2040/Core/SPI.h @@ -0,0 +1,63 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * SPI.h + * + * Created on: Mar 2, 2016 + * Author: harry-boe + * + */ + +/** @defgroup hw_spi SPI Hardware support + * @brief Provides hardware SPI support + */ + +#pragma once + +#include "SPIBase.h" +#include "SPISettings.h" + +//#define SPI_DEBUG 1 + +// for compatibility when porting from Arduino +#define SPI_HAS_TRANSACTION 0 + +#define SPI_NO 1 + +/** + * @brief Hardware SPI object + * @addtogroup hw_spi + * @{ + */ + +class SPIClass : public SPIBase +{ +public: + SPIClass() + { + } + + SPIClass(const SPIClass&) = delete; + SPIClass& operator=(const SPIClass&) = delete; + + bool begin() override; + + void end() override + { + } + + uint8_t read8() override; + uint32_t transfer32(uint32_t val, uint8_t bits = 32) override; + + using SPIBase::transfer; + void transfer(uint8_t* buffer, size_t numberBytes) override; + +protected: + void prepare(SPISettings& settings) override; +}; + +/** @brief Global instance of SPI class */ +extern SPIClass SPI; diff --git a/Sming/Arch/Rp2040/Core/adc.cpp.todo b/Sming/Arch/Rp2040/Core/adc.cpp.todo new file mode 100644 index 0000000000..6ab92823ff --- /dev/null +++ b/Sming/Arch/Rp2040/Core/adc.cpp.todo @@ -0,0 +1,10 @@ +#include +#include + +uint16_t analogRead(uint16_t pin) +{ + if(pin == A0) + return system_adc_read(); + else + return -1; // Not supported +} diff --git a/Sming/Arch/Rp2040/Core/pins_arduino.h b/Sming/Arch/Rp2040/Core/pins_arduino.h new file mode 100644 index 0000000000..faed73a75b --- /dev/null +++ b/Sming/Arch/Rp2040/Core/pins_arduino.h @@ -0,0 +1,34 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * pins_arduino.h + * + ****/ + +// File name selected for compatibility + +#pragma once + +extern const unsigned int A0; // Single ESP8266EX analog input pin (TOUT) 10 bit, 0..1v + +#define NOT_A_PIN 0 +#define NOT_A_PORT 0 +#define NOT_ON_TIMER 0 + +#define PA 1 +#define PB 2 +#define PC 3 + +#define GPIO_REG_TYPE uint32_t + +// We use maximum compatibility to standard Arduino logic. + +#define digitalPinToPort(pin) (0) +#define digitalPinToBitMask(pin) (1UL << (pin)) +#define digitalPinToTimer(pin) (NOT_ON_TIMER) +#define portOutputRegister(port) ((volatile uint32_t*)&GPO) +#define portInputRegister(port) ((volatile uint32_t*)&GPI) +#define portModeRegister(port) ((volatile uint32_t*)&GPE) diff --git a/Sming/Arch/Rp2040/Core/twi_arch.h b/Sming/Arch/Rp2040/Core/twi_arch.h new file mode 100644 index 0000000000..76cc327e28 --- /dev/null +++ b/Sming/Arch/Rp2040/Core/twi_arch.h @@ -0,0 +1,19 @@ +/* + * twi_arch.h - Platform-specific code + * + * See Sming/Core/si2c.cpp + * + */ + +#pragma once + +#include + +//Enable SDA (becomes output and since GPO is 0 for the pin, it will pull the line low) +#define SDA_LOW() gpio_set_dir(twi_sda, true) +//Disable SDA (becomes input and since it has pullup it will go high) +#define SDA_HIGH() gpio_set_dir(twi_sda, false) +#define SDA_READ() gpio_get(twi_sda) +#define SCL_LOW() gpio_set_dir(twi_scl, true) +#define SCL_HIGH() gpio_set_dir(twi_scl, false) +#define SCL_READ() gpio_get(twi_scl) diff --git a/Sming/Arch/Rp2040/Platform/.cs b/Sming/Arch/Rp2040/Platform/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Arch/Rp2040/Platform/Clocks.h b/Sming/Arch/Rp2040/Platform/Clocks.h new file mode 100644 index 0000000000..fe2184cf81 --- /dev/null +++ b/Sming/Arch/Rp2040/Platform/Clocks.h @@ -0,0 +1,25 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Clocks.h + * + ****/ + +#pragma once + +/** @ingroup system_clocks + * @{ + */ + +using OsTimerClock = Timer2Clock; + +using PolledTimerClock = Timer2Clock; + +using CpuCycleClockSlow = CpuCycleClock; +using CpuCycleClockNormal = CpuCycleClock; +using CpuCycleClockFast = CpuCycleClock; + +/** @} */ diff --git a/Sming/Arch/Rp2040/Platform/RTC.cpp b/Sming/Arch/Rp2040/Platform/RTC.cpp new file mode 100644 index 0000000000..6d211744e4 --- /dev/null +++ b/Sming/Arch/Rp2040/Platform/RTC.cpp @@ -0,0 +1,79 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * RTC.cpp + * + ****/ + +#include +#include +#include + +RtcClass RTC; + +#define NS_PER_SECOND 1000000000 + +namespace +{ +bool initialised; + +void checkInit() +{ + if(initialised) { + return; + } + rtc_init(); + initialised = true; +} + +} // namespace + +RtcClass::RtcClass() +{ + rtc_init(); +} + +uint64_t RtcClass::getRtcNanoseconds() +{ + return uint64_t(getRtcSeconds()) * NS_PER_SECOND; +} + +uint32_t RtcClass::getRtcSeconds() +{ + checkInit(); + + datetime_t t; + rtc_get_datetime(&t); + + DateTime dt; + dt.setTime(t.sec, t.min, t.hour, t.day, t.month, t.year); + + return time_t(dt); +} + +bool RtcClass::setRtcNanoseconds(uint64_t nanoseconds) +{ + return setRtcSeconds(nanoseconds / NS_PER_SECOND); +} + +bool RtcClass::setRtcSeconds(uint32_t seconds) +{ + checkInit(); + + DateTime dt{seconds}; + + datetime_t t = { + .year = int16_t(dt.Year), + .month = int8_t(dt.Month), + .day = int8_t(dt.Day), + .dotw = int8_t(dt.DayofWeek), + .hour = int8_t(dt.Hour), + .min = int8_t(dt.Minute), + .sec = int8_t(dt.Second), + }; + + return rtc_set_datetime(&t); +} diff --git a/Sming/Arch/Rp2040/README.rst b/Sming/Arch/Rp2040/README.rst new file mode 100644 index 0000000000..56c003b33e --- /dev/null +++ b/Sming/Arch/Rp2040/README.rst @@ -0,0 +1,224 @@ +Sming RP2040 Architecture +========================= + +.. highlight:: bash + +Support building Sming for the `Raspberry Pi RP2040 SOC +`__. + +Testing so far has been limited to the Rasperry Pi Pico, but there are lots of other boards available. +Configure this using the :envvar:`PICO_BOARD` setting. The default is ``pico``. +You can find the `full list here `__. + +Special mention to the arduino-pico project https://github.com/earlephilhower/arduino-pico. +Lots of helpful stuff in there! + +.. note:: + + This architecture should be considered experimental at present. + +Tested and working: + +- CPU frequency adjustment :cpp:func:`system_get_cpu_freq`, :cpp:func:`system_update_cpu_freq` +- Timers working: hardware, software and CPU cycle counter +- Hardware serial ports (UART driver) +- Task queue +- Flash memory routines +- :cpp:func:`os_random` and :cpp:func:`os_get_random` implemented using ring oscillator. + This is the best the hardware is capable of, but not crypto grade. +- Heap is standard newlib implementation, :cpp:func:`system_get_free_heap_size` provided. +- Software watchdog implemented, timeout is 8 seconds +- A disassembly and symbol file are generated for this architecture. +- SDK declares flash memory size in the board header files. + This is checked at compile time against the value declared in the partition table. +- Reset information :cpp:func:`system_get_rst_info` indicates watchdog or manual resets. + Exception information not yet implemented. +- System functions :cpp:func:`system_get_chip_id`, :cpp:func:`system_get_sdk_version`. +- Partitions and file systems (except SD cards and FAT) + +The following items are yet to be implemented: + +USB + Best to write a separate ``Sming-USB`` library (based on TinyUSB) to support the RP2040, ESP32-S2 & ESP32-S3 variants. + Needs some thought about good integration into the framework. + Arduino-Pico overrides ``HardwareSerial`` to support serial devices, we can do something similar. +SPI + Nice for messing around with displays. +Analogue I/O + Has 4 channels + temperature. +PWM + Hardware can drive up to 16 outputs and measure input frequency/duty cycle. +I2C + Has hardware support +RTC + Can wake from deep sleep but requires an external clock (e.g. 32kHz crystal) and appropriate API. + (Setting and reading the time is implemented.) +Low-power modes + Deep sleep / suspend / power-saving +Dual-core support + RP2040 is a dual-core processor! +Networking + Applications must currently be built with :envvar:`DISABLE_NETWORK` =1. + RP2040 doesn't have WiFi but a network stack via ethernet adapter would be useful. + USB offers the possibility of CDMA networking for mobile applications. +PIO (Programmable I/O) + A killer feature for the RP2040. + Uses range from simple glue logic to I2S, etc. +Crash/exception handling & serial debugging + RP2040 supports JTAG debugging but requires extra hardware. + Serial debugging is often enough and easier to set up. + Requires GDB stub plus implementing crash handler callbacks, etc. +Multi-boot / OTA updates. + If you run ``make map`` you'll see there is no bootloader! + It's part of the firmware image at present. + Adding RP2040 support to rBoot would probably be simplest. + + +Requirements +------------ + +These requirements are in addition to the standard Sming setup. + +The easiest way to get started is with the Sming installer - see :doc:`/getting-started/index`. + +Note: Windows is not currently included in the chocolatey respository. +The following instructions should help. + +Compiler/linker + The RP2040 contains two ARM Cortex-M0+ cores. Tools for all platforms can be downloaded from the + `ARM developer website `__. + + Unzip the archive to a suitable location (e.g. ``/opt/rp2040`` or ``c:\tools\rp2040``) and set :envvar:`PICO_TOOLCHAIN_PATH` accordingly. + + .. note:: + + At time of writing the Ubuntu repositories contain an older version of this toolchain. + It also does not contain GDB, but can be installed separately: + + sudo apt install gcc-arm-none-eabi gdb-multiarch + + To use gdb-multiarch you'll need to do this: + + make gdb GDB=gdb-multiarch + +Ninja + This is used to build the RP2040 SDK code: + + sudo apt install ninja-build + + It is available for other platforms at https://ninja-build.org/ + and consists of a single executable file. + The application should either be in the system path, or set :envvar:`NINJA` + to the full path of the executable file. + + If you have Sming working with the ESP32 then you'll already have it installed. + + +Setup and programming +--------------------- + +Serial support requires a 3.3v USB adapter connected to the appropriate pins: + +- UART0: TX = 0, RX = 1 +- UART1: TX = 4, RX = 5 + +To program your device, unplug the USB cable (i.e. remove power from the device) +then hold down the ``BOOTSEL`` button whilst plugging it back in again. + +You can then run: + + make flash + +as usual and the device will be programmed. + +Once Sming is running on the device, reprogramming is simpler and only requires pressing +the ``BOOTSEL`` button (no power cycle). + +If the firmware has crashed or stalled the watchdog timer should reboot the system after 8 seconds, +at which point BOOTSEL should be detected. So just hold the button down until this happens. + +If all else fails, go through the initial power-cycle process. + +This behaviour can be disabled using the :envvar:`ENABLE_BOOTSEL` setting. + + +Boot process +------------ + +Unlike the Espressif parts, the RP2040 is not programmed via the serial port, +but written to the device when configured as a Mass Storage device (removable flash drive). + +Data to be flashed must be in `UF2 format `__ and +sent as a single file. See :component-rp2040:`uf2`. + +Once the file has finished sending the RP2040 reboots itself into normal operating mode +(assuming BOOTSEL has been released). + +The RP2040 can also be programmed via JTAG debugging but this requires additional hardware and setup. + +.. note:: + + The RP2040 bootloader does not include support for reading flash memory via mass storage, + so commands such as ``make verifyflash`` won't work at present. + + +Source code +----------- + +The RP2040 is a very capable SOC, albeit without WiFi. +A massive advantage is that the platform is fully open-source. +Even the bootrom is published! + +Here's a summary of the various Github repositories the Raspberry Pi Foundation have made available: + +https://github.com/raspberrypi/pico-sdk + The core SDK for the RP2040 SOC. Sming includes this as a submodule. + +https://github.com/raspberrypi/picotool + This is a tool for inspecting RP2040 binaries, and interacting with RP2040 devices + when they are in BOOTSEL mode. + It does this by talking to a custom USB device implemented in the RP2040 bootrom. + + Getting this to build is a bit fiddly. + So far I've managed without it, but there is a ``picotool`` component in the framework + which can be used to try building it. + +https://github.com/raspberrypi/pico-bootrom + Contents of the RP2040 boot rom. Very handy to be able to see this. + +https://github.com/raspberrypi/pico-examples + Examples using the pico SDK directly. + +https://github.com/raspberrypi/picoprobe + An RP2040 board can be used as a low-cost JTAG adapter using this firmware. + Takes some setting up to use though. + See [Getting Started PDF](https://datasheets.raspberrypi.org/pico/getting-started-with-pico.pdf) + for details. + +https://github.com/raspberrypi/pico-extras + Some additional libraries which may or may not end up in the SDK. + +https://github.com/raspberrypi/pico-playground + Further examples using the pico-extras libraries. + + +Build variables +--------------- + +.. envvar:: PICO_TOOLCHAIN_PATH + + This contains the base directory for the toolchain used to build the framework. + Pre-compiled toolchains can be downloaded from the + `ARM Developer website `__. + + +Components +---------- + +.. toctree:: + :glob: + :maxdepth: 1 + :titlesonly: + + Components/sming-arch/index + Components/*/index diff --git a/Sming/Arch/Rp2040/Services/Profiling/TaskStat.cpp b/Sming/Arch/Rp2040/Services/Profiling/TaskStat.cpp new file mode 100644 index 0000000000..dcedcfcec3 --- /dev/null +++ b/Sming/Arch/Rp2040/Services/Profiling/TaskStat.cpp @@ -0,0 +1,32 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * TaskStat.cpp + * + */ + +#include + +namespace Profiling +{ +struct TaskStat::Info { +}; + +TaskStat::TaskStat(Print& out) : out(out) +{ +} + +TaskStat::~TaskStat() +{ +} + +bool TaskStat::update() +{ + out.println("[TaskStat] Not Implemented"); + return false; +} + +} // namespace Profiling diff --git a/Sming/Arch/Rp2040/System/include/user_config.h b/Sming/Arch/Rp2040/System/include/user_config.h new file mode 100644 index 0000000000..cbad5e54b6 --- /dev/null +++ b/Sming/Arch/Rp2040/System/include/user_config.h @@ -0,0 +1,21 @@ +#ifndef __USER_CONFIG_H__ +#define __USER_CONFIG_H__ + +// UART config +#define SERIAL_BAUD_RATE COM_SPEED_SERIAL + +#include + +// Extended string conversion for compatibility +#include + +typedef unsigned char u8_t; +typedef signed char s8_t; +typedef unsigned short u16_t; +typedef signed short s16_t; +typedef unsigned long u32_t; +typedef signed long s32_t; +typedef unsigned long mem_ptr_t; +typedef signed short sint16_t; + +#endif diff --git a/Sming/Arch/Rp2040/Tools/ci/build.run.cmd b/Sming/Arch/Rp2040/Tools/ci/build.run.cmd new file mode 100644 index 0000000000..c2fa057237 --- /dev/null +++ b/Sming/Arch/Rp2040/Tools/ci/build.run.cmd @@ -0,0 +1,12 @@ +REM Rp2040 build.run.cmd + +set RP2040_PROJECTS=Basic_Blink Basic_Serial Basic_Storage + +%MAKE_PARALLEL% %RP2040_PROJECTS% DEBUG_VERBOSE_LEVEL=3 || goto :error + +goto :EOF + + +:error +echo Failed with error #%errorlevel%. +exit /b %errorlevel% diff --git a/Sming/Arch/Rp2040/Tools/ci/build.run.sh b/Sming/Arch/Rp2040/Tools/ci/build.run.sh new file mode 100644 index 0000000000..b3c9969d48 --- /dev/null +++ b/Sming/Arch/Rp2040/Tools/ci/build.run.sh @@ -0,0 +1,7 @@ +#!/bin/bash +# +# Rp2040 build.run.sh + +RP2040_PROJECTS="Basic_Blink Basic_Serial Basic_Storage" + +$MAKE_PARALLEL $RP2040_PROJECTS DEBUG_VERBOSE_LEVEL=3 STRICT=1 diff --git a/Sming/Arch/Rp2040/Tools/ci/build.setup.cmd b/Sming/Arch/Rp2040/Tools/ci/build.setup.cmd new file mode 100644 index 0000000000..ae04d410c7 --- /dev/null +++ b/Sming/Arch/Rp2040/Tools/ci/build.setup.cmd @@ -0,0 +1 @@ +REM Rp2040 build.setup.cmd diff --git a/Sming/Arch/Rp2040/Tools/ci/build.setup.sh b/Sming/Arch/Rp2040/Tools/ci/build.setup.sh new file mode 100755 index 0000000000..f9f33cc4fd --- /dev/null +++ b/Sming/Arch/Rp2040/Tools/ci/build.setup.sh @@ -0,0 +1,3 @@ +#!/bin/bash +# +# Rp2040 build.setup.sh diff --git a/Sming/Arch/Rp2040/Tools/ci/install.cmd b/Sming/Arch/Rp2040/Tools/ci/install.cmd new file mode 100644 index 0000000000..7fd04c4f7e --- /dev/null +++ b/Sming/Arch/Rp2040/Tools/ci/install.cmd @@ -0,0 +1,19 @@ +REM Rp2040 install.cmd + +if exist "%PICO_TOOLCHAIN_PATH%/arm-none-eabi" goto :already_got +set TOOLCHAIN_VERSION=10.3-2021.07 +set TOOLCHAIN_BASE_URL=https://developer.arm.com/-/media/Files/downloads/gnu-rm +set TOOLCHAIN_NAME=gcc-arm-none-eabi-%TOOLCHAIN_VERSION% +set TOOLCHAIN_FILE=%TOOLCHAIN_NAME%-win32.zip +curl -Lo tmp.zip %TOOLCHAIN_BASE_URL%/%TOOLCHAIN_VERSION%/%TOOLCHAIN_FILE% || goto :EOF +7z -o"%PICO_TOOLCHAIN_PATH%-tmp" x tmp.zip || goto :EOF +del tmp.zip +move "%PICO_TOOLCHAIN_PATH%-tmp/%TOOLCHAIN_NAME%" "%PICO_TOOLCHAIN_PATH%" +rmdir "%PICO_TOOLCHAIN_PATH%-tmp" +goto :EOF + + +:already_got +echo. +echo ** Skipping Rp2040 tools installation: '%PICO_TOOLCHAIN_PATH%' exists +echo. diff --git a/Sming/Arch/Rp2040/Tools/install.sh b/Sming/Arch/Rp2040/Tools/install.sh new file mode 100755 index 0000000000..24f6b27359 --- /dev/null +++ b/Sming/Arch/Rp2040/Tools/install.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Rp2040 install.sh + +$PKG_INSTALL ninja-build + +if [ -d "$PICO_TOOLCHAIN_PATH/arm-none-eabi" ]; then + printf "\n\n** Skipping Rp2040 tools installation: '$PICO_TOOLCHAIN_PATH' exists\n\n" +else + TOOLCHAIN_VERSION="10.3-2021.07" + TOOLCHAIN_BASE_URL="https://developer.arm.com/-/media/Files/downloads/gnu-rm" + TOOLCHAIN_NAME="gcc-arm-none-eabi-$TOOLCHAIN_VERSION" + TOOLCHAIN_FILE="$TOOLCHAIN_NAME-x86_64-linux.tar.bz2" + TOOLCHAIN_URL="$TOOLCHAIN_BASE_URL/$TOOLCHAIN_VERSION/$TOOLCHAIN_FILE" + $WGET "$TOOLCHAIN_URL" -O "$DOWNLOADS/$TOOLCHAIN_FILE" + mkdir -p "$PICO_TOOLCHAIN_PATH" + tar -jxf "$DOWNLOADS/$TOOLCHAIN_FILE" -C "$PICO_TOOLCHAIN_PATH" --totals --transform='s|^/*||' + mv "$PICO_TOOLCHAIN_PATH/$TOOLCHAIN_NAME/"* "$PICO_TOOLCHAIN_PATH" +fi diff --git a/Sming/Arch/Rp2040/Tools/memanalyzer.py b/Sming/Arch/Rp2040/Tools/memanalyzer.py new file mode 100644 index 0000000000..be2e1d79ab --- /dev/null +++ b/Sming/Arch/Rp2040/Tools/memanalyzer.py @@ -0,0 +1,72 @@ +#!/bin/python +######################################################## +# +# Memory Analyzer +# Author: Slavey Karadzhov +# Based on https://github.com/Sermus/ESP8266_memory_analyzer +# +######################################################## +from collections import OrderedDict +import os.path +import shlex +import subprocess +import sys + +TOTAL_RAM = 264 * 1024 + +sections = OrderedDict([ + ("data", "Initialized Data (RAM)"), + ("bss", "Uninitialized Data (RAM)"), + ("flash_binary", "Uncached Code (SPI)") +]) + +if len(sys.argv) < 2: + print("Usage: \n\t%s%s " % sys.argv[0]) + sys.exit(1) + +objectDumpBin = sys.argv[1] +appOut = sys.argv[2] + +if not os.path.exists(appOut): + print("Cannot find application out file: %s" % appOut) + sys.exit(1) + + +command = "%s -t '%s' " % (sys.argv[1], sys.argv[2]) +response = subprocess.check_output(shlex.split(command)) +if isinstance(response, bytes): + response = response.decode('utf-8') +lines = response.split('\n') + +print("{0: >12}|{1: >30}|{2: >12}|{3: >12}|{4: >8}".format("Section", "Description", "Start (hex)", "End (hex)", "Used space")); +print("------------------------------------------------------------------------------"); + +usedRAM = 0 + +i = 0 +for (name, descr) in list(sections.items()): + sectionStartToken = " __%s_start" % name + sectionEndToken = " __%s_end" % name + sectionStart = -1; + sectionEnd = -1; + for line in lines: + if sectionStartToken in line: + data = line.split(' ') + sectionStart = int(data[0], 16) + + if sectionEndToken in line: + data = line.split(' ') + sectionEnd = int(data[0], 16) + + if sectionStart != -1 and sectionEnd != -1: + break + + sectionLength = sectionEnd - sectionStart + if i < 2: + usedRAM += sectionLength + + print("{0: >12}|{1: >30}|{2:12X}|{3:12X}|{4:8}".format(name, descr, sectionStart, sectionEnd, sectionLength)) + i += 1 + +print("Total Used RAM : %d" % usedRAM) +print("Free RAM : %d" % (TOTAL_RAM - usedRAM)) diff --git a/Sming/Arch/Rp2040/app.mk b/Sming/Arch/Rp2040/app.mk new file mode 100644 index 0000000000..c8c0c32f0f --- /dev/null +++ b/Sming/Arch/Rp2040/app.mk @@ -0,0 +1,38 @@ +### +# +# SMING Application Makefile for RP2040 architecture +# +### + +# linker flags used to generate the main object file +LDFLAGS += \ + -Wl,--build-id=none \ + --specs=nosys.specs \ + -mcpu=cortex-m0plus \ + -mthumb + +TARGET_DIS = $(TARGET_OUT:.out=.dis) +TARGET_SYM = $(TARGET_OUT:.out=.sym) + +.PHONY: application +application: $(TARGET_BIN) + +$(TARGET_OUT): $(COMPONENTS_AR) + $(info $(notdir $(PROJECT_DIR)): Linking $@) + $(Q) $(LD) $(addprefix -L,$(LIBDIRS)) $(LDFLAGS) -Wl,--start-group $(COMPONENTS_AR) $(addprefix -l,$(LIBS)) -Wl,--end-group -o $@ $(BOOTLOADER) + $(Q) $(MEMANALYZER) $@ > $(FW_MEMINFO) + $(Q) cat $(FW_MEMINFO) + +$(TARGET_BIN): $(TARGET_OUT) + @# Disassembly + $(Q) $(OBJDUMP) -h -C $< > $(TARGET_DIS) + $(Q) $(OBJDUMP) -d -S -C $< >> $(TARGET_DIS) + @# Symbols + @printf "Symbols by name\n" > $(TARGET_SYM) + $(Q) $(NM) -S -l -C $< >> $(TARGET_SYM) + @printf "\n\nSymbols by address\n" >> $(TARGET_SYM) + $(Q) $(NM) -S -l -n -C $< >> $(TARGET_SYM) + @printf "\n\nSymbols by size\n">> $(TARGET_SYM) + $(Q) $(NM) -S -l --size-sort -C $< >> $(TARGET_SYM) + @# Binary output + $(Q) $(OBJCOPY) -O binary $< $@ diff --git a/Sming/Arch/Rp2040/build.mk b/Sming/Arch/Rp2040/build.mk new file mode 100644 index 0000000000..329a435599 --- /dev/null +++ b/Sming/Arch/Rp2040/build.mk @@ -0,0 +1,53 @@ +############## +# +# Rp2040 Architecture build environment +# +############## + +override ESP_VARIANT := + +CPPFLAGS += \ + -DARCH_RP2040 \ + -march=armv6-m \ + -mcpu=cortex-m0plus \ + -mthumb + +CXXFLAGS += \ + -fno-exceptions \ + -fno-threadsafe-statics \ + -fno-use-cxa-atexit + +CONFIG_TOOLPREFIX := arm-none-eabi + +# This will differ based on platform +DEBUG_VARS += PICO_TOOLCHAIN_PATH +ifdef PICO_TOOLCHAIN_PATH +export PICO_TOOLCHAIN_PATH := $(call FixPath,$(PICO_TOOLCHAIN_PATH)) +TOOLSPEC := $(PICO_TOOLCHAIN_PATH)/bin/$(CONFIG_TOOLPREFIX)- +ifeq (,$(wildcard $(PICO_TOOLCHAIN_PATH)/$(CONFIG_TOOLPREFIX))) +$(error PICO_TOOLCHAIN_PATH not set correctly: $(PICO_TOOLCHAIN_PATH)) +endif +else +PICO_TOOLCHAIN_PATH := $(shell which $(CONFIG_TOOLPREFIX)-gcc) +ifeq (,$(PICO_TOOLCHAIN_PATH)) +$(error Toolchain not found, maybe set PICO_TOOLCHAIN_PATH) +endif +TOOLSPEC := $(dir $(PICO_TOOLCHAIN_PATH))$(CONFIG_TOOLPREFIX)- +endif + +# select which tools to use as assembler, compiler, librarian and linker +AS := $(TOOLSPEC)gcc +CC := $(TOOLSPEC)gcc +CXX := $(TOOLSPEC)g++ +AR := $(TOOLSPEC)ar +LD := $(TOOLSPEC)gcc +NM := $(TOOLSPEC)nm +OBJCOPY := $(TOOLSPEC)objcopy +OBJDUMP := $(TOOLSPEC)objdump +GDB := $(TOOLSPEC)gdb + +CPPFLAGS += \ + -nostdlib + +# => Tools +MEMANALYZER = $(PYTHON) $(ARCH_TOOLS)/memanalyzer.py $(OBJDUMP)$(TOOL_EXT) diff --git a/Sming/Arch/Rp2040/standard.hw b/Sming/Arch/Rp2040/standard.hw new file mode 100644 index 0000000000..d143cd3ba0 --- /dev/null +++ b/Sming/Arch/Rp2040/standard.hw @@ -0,0 +1,19 @@ +{ + "name": "Standard config", + "comment": "Should work with any Rp2040 variant", + "arch": "Rp2040", + "bootloader_size": 0, + "partition_table_offset": "self.devices[0].size - 0x1000", + "options": [ + "2m" + ], + "partitions": { + "rom0": { + "address": 0, + "size": "0xC0000", + "type": "app", + "subtype": "factory", + "filename": "$(TARGET_BIN)" + } + } +} \ No newline at end of file diff --git a/Sming/Components/FlashString b/Sming/Components/FlashString index a443c0bc49..7db8cf627e 160000 --- a/Sming/Components/FlashString +++ b/Sming/Components/FlashString @@ -1 +1 @@ -Subproject commit a443c0bc49fbc74300d1082e1270400d7f6a9010 +Subproject commit 7db8cf627ed5fbb6010d15e6da0213c388e156c2 diff --git a/Sming/Components/Storage/Tools/hwconfig/partition.py b/Sming/Components/Storage/Tools/hwconfig/partition.py index 01a680b4fa..6585d81544 100644 --- a/Sming/Components/Storage/Tools/hwconfig/partition.py +++ b/Sming/Components/Storage/Tools/hwconfig/partition.py @@ -52,6 +52,9 @@ }, "Host": { APP_TYPE: 0x1000, + }, + "Rp2040": { + APP_TYPE: 0x1000, } } @@ -571,7 +574,8 @@ def add_unused(table, device, address, last_end): # Take copy of source partitions and add internal ones to appear in the map partitions = copy.copy(config.partitions) if config.partition_table_offset != 0: - add(partitions, device, 'Boot Sector', 0, config.bootloader_size, INTERNAL_BOOT_SECTOR) + if config.bootloader_size != 0: + add(partitions, device, 'Boot Sector', 0, config.bootloader_size, INTERNAL_BOOT_SECTOR) add(partitions, device, 'Partition Table', config.partition_table_offset, PARTITION_TABLE_SIZE, INTERNAL_PARTITION_TABLE) # Devices with no defined partitions diff --git a/Sming/Components/axtls-8266/axtls-8266.patch b/Sming/Components/axtls-8266/axtls-8266.patch index d3f5354189..c737320ae0 100644 --- a/Sming/Components/axtls-8266/axtls-8266.patch +++ b/Sming/Components/axtls-8266/axtls-8266.patch @@ -508,7 +508,7 @@ index 6e41f40..8e4055b 100644 /* decrypt using the key/iv */ diff --git a/ssl/x509.c b/ssl/x509.c -index a51b948..bd42b57 100644 +index a51b948..8e10d85 100644 --- a/ssl/x509.c +++ b/ssl/x509.c @@ -109,76 +109,52 @@ int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx) @@ -613,7 +613,16 @@ index a51b948..bd42b57 100644 break; } - +@@ -305,7 +281,7 @@ static int x509_v3_basic_constraints(const uint8_t *cert, int offset, + /* If the Sequence Length is greater than 3, it has more content than + the basic_constraint_cA bool, so grab the pathLenConstraint */ + if ((lenSeq>3) && (asn1_get_int(cert, &offset, +- &x509_ctx->basic_constraint_pathLenConstraint) < 0)) ++ (int32_t*)&x509_ctx->basic_constraint_pathLenConstraint) < 0)) + { + ret = X509_NOT_OK; + } + diff --git a/ssl/gen_cert.c b/ssl/gen_cert.c index 093ae9c..d611ab0 100644 --- a/ssl/gen_cert.c @@ -715,3 +724,16 @@ index da24d31..4de139b 100644 int key_len, uint8_t *digest); void hmac_sha1_v(const uint8_t **msg, int* length, int count, const uint8_t *key, +diff --git a/ssl/asn1.c b/ssl/asn1.c +index a08a618..273d08b 100644 +--- a/ssl/asn1.c ++++ b/ssl/asn1.c +@@ -405,7 +405,7 @@ static int asn1_get_utc_time(const uint8_t *buf, int *offset, time_t *t) + int asn1_version(const uint8_t *cert, int *offset, int *val) + { + (*offset) += 2; /* get past explicit tag */ +- return asn1_get_int(cert, offset, val); ++ return asn1_get_int(cert, offset, (int32_t*)val); + } + + /** diff --git a/Sming/Arch/Esp32/Components/fatfs/README.rst b/Sming/Components/fatfs/README.rst similarity index 100% rename from Sming/Arch/Esp32/Components/fatfs/README.rst rename to Sming/Components/fatfs/README.rst diff --git a/Sming/Arch/Esp32/Components/fatfs/diskio.h b/Sming/Components/fatfs/diskio.h similarity index 100% rename from Sming/Arch/Esp32/Components/fatfs/diskio.h rename to Sming/Components/fatfs/diskio.h diff --git a/Sming/Arch/Esp32/Components/fatfs/ff.c b/Sming/Components/fatfs/ff.c similarity index 100% rename from Sming/Arch/Esp32/Components/fatfs/ff.c rename to Sming/Components/fatfs/ff.c diff --git a/Sming/Arch/Esp32/Components/fatfs/ff.h b/Sming/Components/fatfs/ff.h similarity index 100% rename from Sming/Arch/Esp32/Components/fatfs/ff.h rename to Sming/Components/fatfs/ff.h diff --git a/Sming/Arch/Esp32/Components/fatfs/ffconf.h b/Sming/Components/fatfs/ffconf.h similarity index 100% rename from Sming/Arch/Esp32/Components/fatfs/ffconf.h rename to Sming/Components/fatfs/ffconf.h diff --git a/Sming/Arch/Esp32/Components/fatfs/integer.h b/Sming/Components/fatfs/integer.h similarity index 100% rename from Sming/Arch/Esp32/Components/fatfs/integer.h rename to Sming/Components/fatfs/integer.h diff --git a/Sming/Components/malloc_count/component.mk b/Sming/Components/malloc_count/component.mk index e0e6f5365c..0cd6448ca5 100644 --- a/Sming/Components/malloc_count/component.mk +++ b/Sming/Components/malloc_count/component.mk @@ -7,14 +7,8 @@ ifeq ($(ENABLE_MALLOC_COUNT),1) COMPONENT_CXXFLAGS += -DENABLE_MALLOC_COUNT=1 # Hook all the memory allocation functions we need to monitor heap activity -MC_WRAP_FUNCS := \ - malloc \ - calloc \ - realloc \ - free - ifeq ($(SMING_ARCH),Esp8266) -MC_WRAP_FUNCS += \ +MC_WRAP_FUNCS := \ pvPortMalloc \ pvPortCalloc \ pvPortRealloc \ @@ -22,7 +16,11 @@ MC_WRAP_FUNCS += \ pvPortZallocIram \ vPortFree else -MC_WRAP_FUNCS += \ +MC_WRAP_FUNCS := \ + malloc \ + calloc \ + realloc \ + free \ strdup endif diff --git a/Sming/Components/malloc_count/malloc_count.cpp b/Sming/Components/malloc_count/malloc_count.cpp index 315cca5f65..f094888c72 100644 --- a/Sming/Components/malloc_count/malloc_count.cpp +++ b/Sming/Components/malloc_count/malloc_count.cpp @@ -42,31 +42,20 @@ #include // Names for the actual implementations -#ifdef ARCH_HOST - -#define F_MALLOC malloc -#define F_CALLOC calloc -#define F_REALLOC realloc -#define F_FREE free - -#elif defined(ARCH_ESP8266) +#ifdef ARCH_ESP8266 #define F_MALLOC pvPortMalloc #define F_CALLOC pvPortCalloc #define F_REALLOC pvPortRealloc #define F_FREE vPortFree -#elif defined(ARCH_ESP32) +#else #define F_MALLOC malloc #define F_CALLOC calloc #define F_REALLOC realloc #define F_FREE free -#else - -#error Unsupported Architecture - #endif #define CONCAT(a, b) a##b @@ -369,7 +358,7 @@ extern "C" void* WRAP(pvPortZalloc)(size_t) __attribute__((alias("mc_zalloc"))); extern "C" void* WRAP(pvPortZallocIram)(size_t) __attribute__((alias("mc_zalloc"))); extern "C" void WRAP(vPortFree)(void*) __attribute__((alias("mc_free"))); -#elif defined(ARCH_HOST) || defined(ARCH_ESP32) +#else using namespace MallocCount; @@ -425,10 +414,6 @@ extern "C" char* WRAP(strdup)(const char* s) return dup; } -#else - -static_assert(false, "ARCH not supported"); - #endif #endif // ENABLE_MALLOC_COUNT diff --git a/Sming/Core/Interrupts.h b/Sming/Core/Interrupts.h index 42be9770f7..446d8e2387 100644 --- a/Sming/Core/Interrupts.h +++ b/Sming/Core/Interrupts.h @@ -32,9 +32,9 @@ __forceinline GPIO_INT_TYPE ConvertArduinoInterruptMode(uint8_t mode) switch(mode) { case LOW: // to trigger the interrupt whenever the pin is low, return GPIO_PIN_INTR_LOLEVEL; - case CHANGE: // to trigger the interrupt whenever the pin changes value - return (GPIO_INT_TYPE)3; // GPIO_PIN_INTR_ANYEDGE - case RISING: // to trigger when the pin goes from low to high, + case CHANGE: // to trigger the interrupt whenever the pin changes value + return GPIO_PIN_INTR_ANYEDGE; + case RISING: // to trigger when the pin goes from low to high, return GPIO_PIN_INTR_POSEDGE; case FALLING: // for when the pin goes from high to low. return GPIO_PIN_INTR_NEGEDGE; diff --git a/Sming/Core/SmingCore.h b/Sming/Core/SmingCore.h index 9e06713097..afbb1c2407 100644 --- a/Sming/Core/SmingCore.h +++ b/Sming/Core/SmingCore.h @@ -62,6 +62,4 @@ #include "DateTime.h" -#include "fatfs/ff.h" - #include diff --git a/Sming/Kconfig b/Sming/Kconfig index febd794328..456ddef4c7 100644 --- a/Sming/Kconfig +++ b/Sming/Kconfig @@ -52,7 +52,7 @@ mainmenu "Sming Framework Configuration: $(SMING_ARCH) $(ESP_VARIANT)" config TASK_QUEUE_LENGTH int "Length of task queue" default 10 - depends on SMING_ARCH="Esp8266" || SMING_ARCH="Host" + depends on SMING_ARCH="Esp8266" || SMING_ARCH="Host" || SMING_ARCH="Rp2040" config STRING_OBJECT_SIZE int "Size of a Wiring String object" diff --git a/Sming/Libraries/SDCard/SDCard.cpp b/Sming/Libraries/SDCard/SDCard.cpp index 885d00f4da..84e2615a24 100644 --- a/Sming/Libraries/SDCard/SDCard.cpp +++ b/Sming/Libraries/SDCard/SDCard.cpp @@ -33,7 +33,6 @@ Descr: Low-level SDCard functions /-------------------------------------------------------------------------*/ #include "SDCard.h" -#include "fatfs/ff.h" #include "fatfs/diskio.h" /* Declarations of disk I/O functions */ #include #include diff --git a/Sming/Libraries/SDCard/SDCard.h b/Sming/Libraries/SDCard/SDCard.h index aff11003d8..05265e99ba 100644 --- a/Sming/Libraries/SDCard/SDCard.h +++ b/Sming/Libraries/SDCard/SDCard.h @@ -8,6 +8,8 @@ Descr: Low-level SDCard functions #pragma once #include +#include "fatfs/ff.h" + /** * @brief Intialise SD card interface diff --git a/Sming/Libraries/SDCard/component.mk b/Sming/Libraries/SDCard/component.mk new file mode 100644 index 0000000000..5397d0b008 --- /dev/null +++ b/Sming/Libraries/SDCard/component.mk @@ -0,0 +1 @@ +COMPONENT_DEPENDS := fatfs diff --git a/Sming/Platform/System.h b/Sming/Platform/System.h index 948fd375aa..562e5d5074 100644 --- a/Sming/Platform/System.h +++ b/Sming/Platform/System.h @@ -65,12 +65,14 @@ class ISystemReadyHandler }; /** - * @brief CPU Frequency + * @brief Common CPU frequencies */ enum CpuFrequency { - eCF_80MHz = 80, ///< CPU 80MHz - eCF_160MHz = 160, ///< CPU 160MHz - eCF_240MHz = 240, ///< CPU 240MHz + eCF_80MHz = 80, + eCF_125MHz = 125, + eCF_133MHz = 133, + eCF_160MHz = 160, + eCF_240MHz = 240, }; /** @@ -127,10 +129,11 @@ class SystemClass /** @brief Set the CPU frequency * @param freq Frequency to set CPU + * @retval bool true on success */ - void setCpuFrequency(CpuFrequency freq) + bool setCpuFrequency(CpuFrequency freq) { - system_update_cpu_freq(freq); + return system_update_cpu_freq(freq); } /** @brief Get the CPU frequency diff --git a/Sming/System/include/gdb/gdb_hooks.h b/Sming/System/include/gdb/gdb_hooks.h index 98bc1fc21e..e7b06c38e7 100644 --- a/Sming/System/include/gdb/gdb_hooks.h +++ b/Sming/System/include/gdb/gdb_hooks.h @@ -57,8 +57,10 @@ void gdb_enable(bool state); #define gdb_do_break() __asm__("int $0x03") #elif defined(ARCH_ESP8266) #define gdb_do_break() __asm__("break 0,0") -#else +#elif defined(ARCH_ESP32) #define gdb_do_break() cpu_hal_break() +elif defined(ARCH_RP2040) +#define gdb_do_break() __asm__("bkpt #0") #endif #else #define gdb_do_break() \ diff --git a/Sming/Wiring/WConstants.h b/Sming/Wiring/WConstants.h index 1c11d7c792..97af55bce0 100644 --- a/Sming/Wiring/WConstants.h +++ b/Sming/Wiring/WConstants.h @@ -41,7 +41,8 @@ //GPIO FUNCTIONS #define INPUT 0x00 #define INPUT_PULLUP 0x02 -#define INPUT_PULLDOWN_16 0x04 // PULLDOWN only possible for pin16 +#define INPUT_PULLDOWN_16 0x04 // PULLDOWN only possible for pin16 on ESP8266 +#define INPUT_PULLDOWN 0x04 #define OUTPUT 0x01 #define OUTPUT_OPEN_DRAIN 0x03 #define WAKEUP_PULLUP 0x05 diff --git a/Sming/build.mk b/Sming/build.mk index c38b928f9c..f2a9f46a86 100644 --- a/Sming/build.mk +++ b/Sming/build.mk @@ -17,6 +17,7 @@ DEBUG_VARS += \ CXX \ AR \ LD \ + NM \ OBJCOPY \ OBJDUMP \ GDB @@ -366,29 +367,10 @@ endef dirx = $(patsubst %/,%,$(dir $1)) # Extract commented target information from makefiles and display -# Based on code from https://suva.sh/posts/well-documented-makefiles/ define PrintHelp @echo @echo Welcome to the Sming build system! - @$(AWK) 'BEGIN { \ - FS = "(:.*##)|(##@)"; \ - printf "Usage:\n make \033[1;36m\033[0m\n"; \ - } /^##@/ { \ - group = $$2; \ - groups[group] = group; \ - } /^[a-zA-Z0-9_-]+:.*?##/ { \ - targets[$$1, group] = $$2; \ - } \ - END { \ - for (g in groups) { \ - printf "\n\033[1m%s\033[0m\n", g; \ - for (t in targets) { \ - split(t, sep, SUBSEP); \ - if (sep[2] == g) \ - printf " \033[1;36m%-20s\033[0m %s\n", sep[1], targets[t] \ - } \ - } \ - } ' $(foreach f,$(MAKEFILE_LIST),"$(f)") + @$(AWK) -f $(SMING_HOME)/help.awk $(foreach f,$(MAKEFILE_LIST),"$(f)") @echo endef @@ -437,12 +419,22 @@ define TryApplyPatch fi endef +# Fetch and patch a submodule +# $1 -> submodule parent path +# $2 -> submodule name +define FetchAndPatch + $(info ) + $(info Fetching submodule '$2' ...) + $(Q) cd $1 && ( \ + rm -rf $2; \ + if [ -f "$2.no-recursive" ]; then OPTS=""; else OPTS="--recursive"; fi; \ + $(GIT) submodule update --init --force $$OPTS $2 \ + ) + $(Q) $(call TryApplyPatch,$1/$2,$2.patch) +endef + # Update and patch submodule # Patch file is either in submodule parent directory itself or subdirectory .patches from there %/.submodule: - $(info ) - $(info Fetching submodule '$*' ...) - $(Q) cd $(abspath $*/..) && (rm -rf $(*F); $(GIT) submodule update --init --force --recursive $(*F)) - $(Q) $(call TryApplyPatch,$*,$(*F).patch) + $(call FetchAndPatch,$(abspath $*/..),$(*F)) $(Q) touch $@ - diff --git a/Sming/building.rst b/Sming/building.rst index ab103ddb23..3264e5a369 100644 --- a/Sming/building.rst +++ b/Sming/building.rst @@ -44,6 +44,12 @@ These are the main variables you need to be aware of: - **Host** builds a version of the library for native host debugging on Linux or Windows + - **Rp2040** Supports Raspberry Pi RP2040-based boards. + +.. envvar:: ESP_VARIANT + + Some architectures support families of SOCs with different capabilities. + Set this value to the specific variant being targeted. .. envvar:: SMING_CPP_STD @@ -532,6 +538,11 @@ changed as required. Relative paths to dependent submodule directories for this Component. These will be fetched/patched automatically before building. + Default behaviour is to initialise submodules recursively. + To prevent this behaviour and initialise only the top-level submodule, + add a file to the parent directory with the same name as the submodule + and a ``.no-recursive`` extension. + .. envvar:: COMPONENT_SRCDIRS Locations for source code relative to COMPONENT_PATH (defaults to “. srcâ€) diff --git a/Sming/help.awk b/Sming/help.awk new file mode 100644 index 0000000000..1eb00312ee --- /dev/null +++ b/Sming/help.awk @@ -0,0 +1,21 @@ +# Extract commented target information from makefiles and display +# Based on code from https://suva.sh/posts/well-documented-makefiles/ +BEGIN { + FS = "(:.*##)|(##@)" + printf "Usage:\n make \033[1;36m\033[0m\n" +} /^##@/ { + group = $2 + groups[group] = group +} /^[a-zA-Z0-9_-]+:.*?##/ { + targets[$1, group] = $2 +} +END { + for (g in groups) { + printf "\n\033[1m%s\033[0m\n", g + for (t in targets) { + split(t, sep, SUBSEP) + if (sep[2] == g) + printf " \033[1;36m%-20s\033[0m %s\n", sep[1], targets[t] + } + } +} diff --git a/Tools/ci/build.sh b/Tools/ci/build.sh index d155155e36..cccaef29dc 100755 --- a/Tools/ci/build.sh +++ b/Tools/ci/build.sh @@ -29,6 +29,9 @@ make list-config $MAKE_PARALLEL +# HostTests should build and run on all architectures +$MAKE_PARALLEL -C "$SMING_PROJECTS_DIR/tests/HostTests" + # Run ARCH build/tests cd "$SMING_HOME" source "Arch/$SMING_ARCH/Tools/ci/build.run.sh" diff --git a/Tools/export.sh b/Tools/export.sh index 12365503f1..c0044806a1 100755 --- a/Tools/export.sh +++ b/Tools/export.sh @@ -39,3 +39,6 @@ export ESP_HOME=${ESP_HOME:=$EQT_ROOT} export IDF_PATH=${IDF_PATH:=/opt/esp-idf} export IDF_TOOLS_PATH=${IDF_TOOLS_PATH:=/opt/esp32} export ESP32_PYTHON_PATH=${ESP32_PYTHON_PATH:=/usr/bin} + +# Rp2040 +export PICO_TOOLCHAIN_PATH=${PICO_TOOLCHAIN_PATH:=/opt/rp2040} diff --git a/Tools/install.sh b/Tools/install.sh index 68b1f27c55..d0dbe15586 100755 --- a/Tools/install.sh +++ b/Tools/install.sh @@ -22,7 +22,7 @@ for opt in "$@"; do inst_esp32=1 ;; - host | doc | esp8266 | esp32) + host | doc | esp8266 | esp32 | rp2040) eval "inst_$opt=1" ;; @@ -39,6 +39,7 @@ if [[ $err -eq 1 ]] || [ $# -eq 0 ]; then echo ' doc Tools required to build documentation' echo ' esp8266 ESP8266 development tools' echo ' esp32 ESP32 development tools' + echo ' rp2040 RP2040 tools (Raspberry Pi Pico)' echo ' all Install everything' echo if [ $sourced = 1 ]; then @@ -176,6 +177,10 @@ if [ $inst_esp32 -eq 1 ]; then install Esp32 fi +if [ $inst_rp2040 -eq 1 ]; then + install Rp2040 +fi + if [ -z "$KEEP_DOWNLOADS" ]; then rm -f "$DOWNLOADS/*" fi diff --git a/appveyor.yml b/appveyor.yml index ed86a8b7fd..c7a0a82bd5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -23,6 +23,9 @@ environment: - SMING_ARCH: Esp32 INSTALL_OPTS: esp32 + - SMING_ARCH: Rp2040 + INSTALL_OPTS: rp2040 + DISABLE_NETWORK: 1 install: - ps: | @@ -45,6 +48,8 @@ install: $env:IDF_PATH = Join-Path $env:CI_BUILD_DIR "opt/esp-idf" $env:IDF_TOOLS_PATH = Join-Path $env:CI_BUILD_DIR "opt/tools/esp32" $env:IDF_BRANCH = "sming/dev/v4.3" + # Rp2040 + $env:PICO_TOOLCHAIN_PATH = Join-Path $env:CI_BUILD_DIR "opt/rp2040" # General $env:SMING_HOME = Join-Path $env:CI_BUILD_DIR "Sming" diff --git a/docs/source/arch/rp2040/debugging/index.rst b/docs/source/arch/rp2040/debugging/index.rst new file mode 100644 index 0000000000..2f85d51cfa --- /dev/null +++ b/docs/source/arch/rp2040/debugging/index.rst @@ -0,0 +1,6 @@ +Debugging on RP2040 +================== + +Currently debugging support on Sming is limited to serial output and inspecting disassembly using GDB. + +See https://www.raspberrypi.org/documentation/microcontrollers/raspberry-pi-pico.html diff --git a/docs/source/information/debugging.rst b/docs/source/information/debugging.rst index 00cd5b6f78..aa1b8ffdda 100644 --- a/docs/source/information/debugging.rst +++ b/docs/source/information/debugging.rst @@ -18,4 +18,4 @@ The links below will give you more information and step-by-step instructions. /arch/host/debugging/index /arch/esp8266/debugging/index /arch/esp32/debugging/index - + /arch/rp2040/debugging/index diff --git a/docs/source/information/develop/documentation.rst b/docs/source/information/develop/documentation.rst index d9164045e8..11857e7255 100644 --- a/docs/source/information/develop/documentation.rst +++ b/docs/source/information/develop/documentation.rst @@ -71,7 +71,7 @@ All Components, Libraries and Samples must include a ``README.rst`` or ``README. - **References**: Is this based on or does it use existing code? Please include details. - **Datasheets**: If appropriate, please include links to manufacturer's or external development websites. Note that any submodules or dependencies are automatically documented: see :doc:`/_inc/Sming/building` for details, - specifically `COMPONENT_SUBMODULES` and `COMPONENT_DEPENDS`. + specifically :envvar:`COMPONENT_SUBMODULES` and :envvar:`COMPONENT_DEPENDS`. You should also try to include any other information which could be useful information for a new developer. The purpose of samples projects is to demonstrate specific features or libraries, so please ensure this is adequately described. diff --git a/docs/source/information/develop/external-sources.rst b/docs/source/information/develop/external-sources.rst index 9f094edabb..065e52b1a2 100644 --- a/docs/source/information/develop/external-sources.rst +++ b/docs/source/information/develop/external-sources.rst @@ -113,7 +113,7 @@ Applications can use it by adding the name to their COMPONENT_DEPENDS or ARDUINO entries in component.mk as appropriate. Submodules contained within a Component must be declared by adding them to the -COMPONENT_SUBMODULES entry in component.mk. +:envvar:`COMPONENT_SUBMODULES` entry in component.mk. Moving submodules diff --git a/docs/source/link-roles.py b/docs/source/link-roles.py index 7ae29a2727..0ededa5141 100644 --- a/docs/source/link-roles.py +++ b/docs/source/link-roles.py @@ -48,6 +48,7 @@ def setup(app): app.add_role('component-esp8266', doclink('/_inc/Sming/Arch/Esp8266/Components/{}/index')) app.add_role('component-esp32', doclink('/_inc/Sming/Arch/Esp32/Components/{}/index')) app.add_role('component-host', doclink('/_inc/Sming/Arch/Host/Components/{}/index')) + app.add_role('component-rp2040', doclink('/_inc/Sming/Arch/Rp2040/Components/{}/index')) app.add_role('library', doclink('/_inc/Sming/Libraries/{}/index')) # Insert a link to a file or issue in the repo diff --git a/samples/Basic_Blink/app/application.cpp b/samples/Basic_Blink/app/application.cpp index 14d1fdf458..65422d0701 100644 --- a/samples/Basic_Blink/app/application.cpp +++ b/samples/Basic_Blink/app/application.cpp @@ -1,6 +1,10 @@ #include +#ifdef ARCH_RP2040 +#define LED_PIN PICO_DEFAULT_LED_PIN +#else #define LED_PIN 2 // GPIO2 +#endif Timer procTimer; bool state = true; diff --git a/samples/Basic_ProgMem/app/TestProgmem.cpp b/samples/Basic_ProgMem/app/TestProgmem.cpp index bfe023e568..90999f58eb 100644 --- a/samples/Basic_ProgMem/app/TestProgmem.cpp +++ b/samples/Basic_ProgMem/app/TestProgmem.cpp @@ -10,6 +10,11 @@ #include #include #include +#include + +#ifndef __INT32 +#define __INT32 +#endif // Note: contains nulls which won't display, but will be stored #define DEMO_TEST_TEXT "This is a flash string -\0Second -\0Third -\0Fourth." @@ -147,8 +152,8 @@ void testFSTR(Print& out) // Table entries may be accessed directly as they are word-aligned out.println(_F("FSTR tables -")); out.printf(_F(" fstr1 = '%s'\n"), String(table[0]).c_str()); - out.printf(_F(" fstr1.length() = %u\n"), table[0].length()); - out.printf(_F(" entries = %u\n"), table.length()); + out.printf(_F(" fstr1.length() = %" PRIu32 "\n"), table[0].length()); + out.printf(_F(" entries = %" PRIu32 "\n"), table.length()); out.println("< testFSTR() end\n"); } @@ -170,11 +175,11 @@ void testSpeed(Print& out) for(unsigned i = 0; i < iterations; ++i) tmp += sumBuffer(demoText, sizeof(demoText)); baseline = timer.elapsedTime(); - out.printf("Elapsed: %u\n", baseline); + out.printf("Elapsed: %" PRIu32 "\n", baseline); #define END() \ elapsed = timer.elapsedTime(); \ - out.printf("Elapsed: %u (baseline + %u)\n", elapsed, elapsed - baseline); + out.printf("Elapsed: %" PRIu32 " (baseline + %" PRIu32 ")\n", elapsed, elapsed - baseline); _FPUTS("Load PSTR into stack buffer..."); timer.start(); diff --git a/samples/Basic_Serial/component.mk b/samples/Basic_Serial/component.mk index 96def499d3..e0e940369a 100644 --- a/samples/Basic_Serial/component.mk +++ b/samples/Basic_Serial/component.mk @@ -1,4 +1,4 @@ -HWCONFIG := spiffs +HWCONFIG := spiffs-2m CUSTOM_TARGETS += files/README.md diff --git a/samples/Basic_Storage/app/application.cpp b/samples/Basic_Storage/app/application.cpp index 37ed842b1a..62241ca8a8 100644 --- a/samples/Basic_Storage/app/application.cpp +++ b/samples/Basic_Storage/app/application.cpp @@ -49,7 +49,7 @@ void listSpiffsPartitions() void printPart(Storage::Partition part) { - size_t bufSize = std::min(4096U, part.size()); + size_t bufSize = std::min(uint32_t(4096), part.size()); char buf[bufSize]; OneShotFastUs timer; if(!part.read(0, buf, bufSize)) { diff --git a/samples/Basic_Storage/basic_storage_2m.hw b/samples/Basic_Storage/basic_storage-2m.hw similarity index 85% rename from samples/Basic_Storage/basic_storage_2m.hw rename to samples/Basic_Storage/basic_storage-2m.hw index bdbae34b1c..64968c4c7c 100644 --- a/samples/Basic_Storage/basic_storage_2m.hw +++ b/samples/Basic_Storage/basic_storage-2m.hw @@ -1,11 +1,9 @@ { "name": "Basic Storage sample (2M)", "base_config": "basic_storage", - "devices": { - "spiFlash": { - "size": "2M" - } - }, + "options": [ + "2m" + ], "partitions": { "user0": { "address": "0x000fa000" diff --git a/samples/Basic_Storage/component.mk b/samples/Basic_Storage/component.mk index cfe43013d0..400f9865dc 100644 --- a/samples/Basic_Storage/component.mk +++ b/samples/Basic_Storage/component.mk @@ -1,5 +1,5 @@ # Use our custom hardware configuration -HWCONFIG := basic_storage +HWCONFIG := basic_storage-2m HOST_NETWORK_OPTIONS := --nonet diff --git a/tests/HostTests/Kconfig b/tests/HostTests/Kconfig new file mode 100644 index 0000000000..3a3127137b --- /dev/null +++ b/tests/HostTests/Kconfig @@ -0,0 +1,13 @@ +menu "HostTests application" + config TEST_GROUP_INTERVAL + int "Time in milliseconds to pause after a test group has completed" + default 500 + + config RESTART_DELAY + int "Time in milliseconds to wait before re-starting all tests" + default 0 if $SMING_ARCH = Host + default 10000 if $SMING_ARCH != Host + help + Set to 0 to perform a system restart after all tests have completed + +endmenu diff --git a/tests/HostTests/component.mk b/tests/HostTests/component.mk index 0518851e86..3975dc9a90 100644 --- a/tests/HostTests/component.mk +++ b/tests/HostTests/component.mk @@ -1,4 +1,9 @@ +ifeq ($(SMING_ARCH),Rp2040) +HWCONFIG = host-tests-1m +else HWCONFIG = host-tests +endif + DEBUG_VERBOSE_LEVEL = 2 COMPONENT_INCDIRS := include @@ -7,6 +12,12 @@ COMPONENT_SRCDIRS := \ modules \ Arch/$(SMING_ARCH) +ifneq ($(DISABLE_NETWORK),1) +COMPONENT_SRCDIRS += \ + modules/Network \ + modules/Network/Arch/$(SMING_ARCH) +endif + ARDUINO_LIBRARIES := \ SmingTest \ ArduinoJson5 \ @@ -54,4 +65,4 @@ $(SPIFFSGEN_BIN): clean: resource-clean .PHONY: resource-clean resource-clean: - rm -f $(SPIFFSGEN_BIN) + $(Q) rm -f $(SPIFFSGEN_BIN) diff --git a/tests/HostTests/host-tests-1m.hw b/tests/HostTests/host-tests-1m.hw new file mode 100644 index 0000000000..3ad4348efc --- /dev/null +++ b/tests/HostTests/host-tests-1m.hw @@ -0,0 +1,17 @@ +{ + "name": "Host Tests (1M flash)", + "base_config": "host-tests", + "options": [ + "1m" + ], + "partitions": { + "fwfs0": { + "address": "0x000c0000", + "size": "128K" + }, + "spiffs0": { + "size": "0x10000", + "address": "0x000e0000" + } + } +} diff --git a/tests/HostTests/include/modules.h b/tests/HostTests/include/modules.h index 676fea8fc8..9a87341b3f 100644 --- a/tests/HostTests/include/modules.h +++ b/tests/HostTests/include/modules.h @@ -1,16 +1,24 @@ // List of test modules to register +#ifdef DISABLE_NETWORK +#define XX_NET(test) +#else +#define XX_NET(test) XX(test) +#endif + // Architecture-specific test modules #ifdef ARCH_HOST #define ARCH_TEST_MAP(XX) \ - XX(Hosted) \ - XX(HttpRequest) \ - XX(TcpClient) + XX_NET(Hosted) \ + XX_NET(HttpRequest) \ + XX_NET(TcpClient) #else #define ARCH_TEST_MAP(XX) #endif #define TEST_MAP(XX) \ + XX(System) \ + XX(SpiFlash) \ XX(Libc) \ XX(PreCache) \ XX(BitSet) \ @@ -23,15 +31,14 @@ XX(TemplateStream) \ XX(Serial) \ XX(ObjectMap) \ - XX(Base64) \ + XX_NET(Base64) \ XX(DateTime) \ - XX(Http) \ - XX(Url) \ + XX_NET(Http) \ + XX_NET(Url) \ XX(ArduinoJson5) \ XX(ArduinoJson6) \ XX(Storage) \ XX(Files) \ - XX(SpiFlash) \ XX(Spiffs) \ XX(Rational) \ XX(Clocks) \ diff --git a/tests/HostTests/modules/BitSet.cpp b/tests/HostTests/modules/BitSet.cpp index 6833578771..4f0b85a85b 100644 --- a/tests/HostTests/modules/BitSet.cpp +++ b/tests/HostTests/modules/BitSet.cpp @@ -1,5 +1,6 @@ #include #include +#include #define FRUIT_ELEMENT_MAP(XX) \ XX(apple) \ diff --git a/tests/HostTests/modules/Clocks.cpp b/tests/HostTests/modules/Clocks.cpp index a1d251483a..f277cce590 100644 --- a/tests/HostTests/modules/Clocks.cpp +++ b/tests/HostTests/modules/Clocks.cpp @@ -423,9 +423,8 @@ template void testTimer1() // uint64_t refticks = timer1.timeToTicksRef(time); auto check = [time, refticks](const char* tag, TimeType ticks) { - if(refticks != ticks) { - int64_t diff = int64_t(ticks) - int64_t(refticks); - + int64_t diff = int64_t(ticks) - int64_t(refticks); + if(abs(diff) > 2) { Serial.print("time = "); Serial.print(time); Serial.print(", refticks = "); diff --git a/tests/HostTests/Arch/Host/Hosted.cpp b/tests/HostTests/modules/Network/Arch/Host/Hosted.cpp similarity index 100% rename from tests/HostTests/Arch/Host/Hosted.cpp rename to tests/HostTests/modules/Network/Arch/Host/Hosted.cpp diff --git a/tests/HostTests/Arch/Host/HttpRequest.cpp b/tests/HostTests/modules/Network/Arch/Host/HttpRequest.cpp similarity index 100% rename from tests/HostTests/Arch/Host/HttpRequest.cpp rename to tests/HostTests/modules/Network/Arch/Host/HttpRequest.cpp diff --git a/tests/HostTests/Arch/Host/TcpClient.cpp b/tests/HostTests/modules/Network/Arch/Host/TcpClient.cpp similarity index 100% rename from tests/HostTests/Arch/Host/TcpClient.cpp rename to tests/HostTests/modules/Network/Arch/Host/TcpClient.cpp diff --git a/tests/HostTests/modules/Base64.cpp b/tests/HostTests/modules/Network/Base64.cpp similarity index 80% rename from tests/HostTests/modules/Base64.cpp rename to tests/HostTests/modules/Network/Base64.cpp index 0c4cdd58b1..e97eee837e 100644 --- a/tests/HostTests/modules/Base64.cpp +++ b/tests/HostTests/modules/Network/Base64.cpp @@ -1,5 +1,7 @@ #include +#include +#include #include class Base64Test : public TestGroup @@ -10,6 +12,12 @@ class Base64Test : public TestGroup } void execute() override + { + libTests(); + streamTests(); + } + + void libTests() { String user("donkey"); String pass("kingpin"); @@ -69,6 +77,21 @@ class Base64Test : public TestGroup delete[] inbuf; } } + + void streamTests() + { + TEST_CASE("Base64OutputStream / StreamTransformer") + { + auto src = new FSTR::Stream(Resource::image_png); + Base64OutputStream base64stream(src); + MemoryDataStream output; + output.copyFrom(&base64stream); + String s; + REQUIRE(output.moveString(s)); + s = base64_decode(s); + REQUIRE(Resource::image_png == s); + } + } }; void REGISTER_TEST(Base64) diff --git a/tests/HostTests/modules/Http.cpp b/tests/HostTests/modules/Network/Http.cpp similarity index 100% rename from tests/HostTests/modules/Http.cpp rename to tests/HostTests/modules/Network/Http.cpp diff --git a/tests/HostTests/modules/Url.cpp b/tests/HostTests/modules/Network/Url.cpp similarity index 100% rename from tests/HostTests/modules/Url.cpp rename to tests/HostTests/modules/Network/Url.cpp diff --git a/tests/HostTests/modules/SpiFlash.cpp b/tests/HostTests/modules/SpiFlash.cpp index 210ab55a7f..827252553f 100644 --- a/tests/HostTests/modules/SpiFlash.cpp +++ b/tests/HostTests/modules/SpiFlash.cpp @@ -70,8 +70,16 @@ class SpiFlashTest : public TestGroup void execute() override { - TEST_CASE("Query Bootloader Info") + TEST_CASE("Query flash info") { + auto flash_id = spi_flash_get_id(); + REQUIRE_NEQ(flash_id, 0); + debug_i("flash_id = 0x%08x", flash_id); + + auto flash_size = flashmem_get_size_bytes(); + REQUIRE_NEQ(flash_size, 0); + debug_i("flash_size = 0x%08x bytes, %u MBytes", flash_size, flash_size / 0x100000); + auto info = flashmem_get_info(); auto modeStr = modeToString(SPIFlashMode(info.mode)); auto speedStr = speedToString(SPIFlashSpeed(info.speed)); diff --git a/tests/HostTests/modules/Stream.cpp b/tests/HostTests/modules/Stream.cpp index d77afc8f8e..65782a1030 100644 --- a/tests/HostTests/modules/Stream.cpp +++ b/tests/HostTests/modules/Stream.cpp @@ -1,13 +1,16 @@ #include #include #include -#include -#include +#include #include #include #include #include +#ifndef DISABLE_NETWORK +#include +#endif + DEFINE_FSTR_LOCAL(template1, "Stream containing {var1}, {var2} and {var3}. {} {{}} {{12345") DEFINE_FSTR_LOCAL(template1_1, "Stream containing value #1, value #2 and {var3}. {} {{}} {{12345") DEFINE_FSTR_LOCAL(template1_2, "Stream containing value #1, value #2 and [value #3]. {} {{}} {{12345") @@ -80,17 +83,7 @@ class StreamTest : public TestGroup REQUIRE(strlen(s.c_str()) == s.length()); } - TEST_CASE("Base64OutputStream / StreamTransformer") - { - auto src = new FSTR::Stream(Resource::image_png); - Base64OutputStream base64stream(src); - MemoryDataStream output; - output.copyFrom(&base64stream); - String s; - REQUIRE(output.moveString(s)); - s = base64_decode(s); - REQUIRE(Resource::image_png == s); - } +#ifndef DISABLE_NETWORK TEST_CASE("ChunkedStream / StreamTransformer") { @@ -132,6 +125,7 @@ class StreamTest : public TestGroup REQUIRE(mem.moveString(s)); REQUIRE(Resource::multipart_result == s); } +#endif TEST_CASE("XorOutputStream") { @@ -191,7 +185,7 @@ class StreamTest : public TestGroup } for(unsigned i = 0; i < list.count(); i++) { - size_t bufferSize = 5; + constexpr size_t bufferSize{5}; char buffer[bufferSize]{}; auto element = list[i]; diff --git a/tests/HostTests/modules/System.cpp b/tests/HostTests/modules/System.cpp new file mode 100644 index 0000000000..23f6fac395 --- /dev/null +++ b/tests/HostTests/modules/System.cpp @@ -0,0 +1,78 @@ +#include +#include + +/* + * Various system functions must be available for all architectures. + */ +class SystemTest : public TestGroup +{ +public: + SystemTest() : TestGroup(_F("System")) + { + } + + void execute() override + { + TEST_CASE("Heap") + { + REQUIRE_NEQ(system_get_free_heap_size(), 0); + } + + TEST_CASE("Identification") + { + REQUIRE_NEQ(String(system_get_sdk_version()), nullptr); + + auto chip_id = system_get_chip_id(); + REQUIRE_NEQ(chip_id, 0); + debug_i("chip_id = 0x%08x", chip_id); + } + + TEST_CASE("Clocks") + { + debug_i("CPU running at %u MHz", System.getCpuFrequency()); + } + + TEST_CASE("Watchdog functions available") + { + system_soft_wdt_stop(); + system_soft_wdt_restart(); + system_soft_wdt_feed(); + } + +#ifndef ARCH_HOST + TEST_CASE("System restart") + { + auto info = system_get_rst_info(); + REQUIRE_NEQ(uint32_t(info), 0); + debug_i("Reason 0x%08x", info->reason); + + switch(info->reason) { + case REASON_DEFAULT_RST: + debug_i("Hanging to check watchdog reboots system...\n\n"); + for(;;) { + } + break; + + case REASON_SOFT_WDT_RST: + debug_i("Reset by watchdog, rebooting...\n\n"); + System.restart(1000); + pending(); + break; + + case REASON_SOFT_RESTART: + debug_i("Reset by software. Continuing...\n\n"); + break; + + default: + // Could be exception, deep sleep, etc. + debug_i("Restarted for some other reason...\n\n"); + } + } +#endif + } +}; + +void REGISTER_TEST(System) +{ + registerGroup(); +} From a8170218393ce05648d529ecd962d3343652c049 Mon Sep 17 00:00:00 2001 From: Mike Date: Mon, 18 Oct 2021 08:04:30 +0100 Subject: [PATCH 080/130] Fix `readthedocs` build (#2394) * Build is broken, update everything Immediate problem appears to be `funcparserlib`. See https://github.com/vlasovskikh/funcparserlib/issues/70. Found mr2r (mr2 is out of date) and can now use sphinx 4. * Update theme - bullets missing --- .readthedocs.yml | 6 ++++-- docs/requirements.txt | 15 ++++++++------- docs/source/conf.py | 9 +++++---- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 801ca5ff31..d4b41b32a7 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -8,11 +8,13 @@ version: 2 # Optionally build your docs in additional formats such as PDF and ePub formats: - htmlzip -# - pdf + +sphinx: + configuration: docs/source/conf.py # Optionally set the version of Python and requirements required to build your docs python: - version: 3.7 + version: 3.8 install: - requirements: docs/requirements.txt diff --git a/docs/requirements.txt b/docs/requirements.txt index e65bfc6a39..82e9a08337 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,11 +1,12 @@ # Requirements file for pip # list of Python packages used in documentation build -sphinx==2.4.4 -sphinx-rtd-theme -m2r -breathe==4.13.0 +sphinx==4.2.0 +sphinx-rtd-theme==1.0.0 +m2r2 +breathe==4.31.0 sphinxcontrib-wavedrom sphinx-copybutton -sphinxcontrib-seqdiag>=0.8.5 -jinja2>=2.11.3 -setuptools==57.5.0 +sphinxcontrib-seqdiag==2.0.0 +jinja2==2.11.3 +setuptools>=57.5.0 +funcparserlib==1.0.0a0 diff --git a/docs/source/conf.py b/docs/source/conf.py index 2deb3a29db..b98da2f033 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -45,7 +45,7 @@ # so developers can more easily locate the appropriate reference documentation. # extensions = [ - 'm2r', + 'm2r2', 'breathe', 'sphinxcontrib.seqdiag', 'link-roles', @@ -101,6 +101,10 @@ # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] +html_css_files = [ + 'custom.css', +] + html_extra_path = [ '../api/html' ] @@ -134,6 +138,3 @@ subprocess.call('make -C ../../Sming submodules SMING_ARCH=Host', shell=True) subprocess.call('make -C .. setup api API_VERSION="' + version + '"', shell=True) - -def setup(app): - app.add_stylesheet('custom.css') From 308e7b123698bfa919c6ff53726636b2634aa3ee Mon Sep 17 00:00:00 2001 From: slaff Date: Wed, 20 Oct 2021 21:48:09 +0200 Subject: [PATCH 081/130] Updated Graphics library with support for ST7789V display and other MIPI displays. (#2396) --- Sming/Libraries/Graphics | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sming/Libraries/Graphics b/Sming/Libraries/Graphics index f272a5eade..768ad4f808 160000 --- a/Sming/Libraries/Graphics +++ b/Sming/Libraries/Graphics @@ -1 +1 @@ -Subproject commit f272a5eade55561dea6b959146fb630a57e58927 +Subproject commit 768ad4f80813f1336ce5941d7ba84bfcbb7ecc54 From 9e4c779b9466c1e292d393876203de41ad82d00a Mon Sep 17 00:00:00 2001 From: slaff Date: Thu, 21 Oct 2021 08:44:55 +0200 Subject: [PATCH 082/130] Make sure to clean-up before running coverity scan. (#2399) [scan:coverity] --- Sming/Arch/Host/Tools/ci/coverity-scan.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sming/Arch/Host/Tools/ci/coverity-scan.sh b/Sming/Arch/Host/Tools/ci/coverity-scan.sh index cd1a02f1e8..e2d3e6f88c 100755 --- a/Sming/Arch/Host/Tools/ci/coverity-scan.sh +++ b/Sming/Arch/Host/Tools/ci/coverity-scan.sh @@ -65,6 +65,9 @@ fi TOOL_DIR=$(find $TOOL_BASE -type d -name 'cov-analysis*') export PATH=$TOOL_DIR/bin:$PATH +# Clean-up +make -C $SMING_HOME dist-clean + # Build echo -e "\033[33;1mRunning Coverity Scan Analysis Tool...\033[0m" COV_BUILD_OPTIONS="" From d91eaede1f208666d0791ea174f492bf99487588 Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 24 Oct 2021 20:16:03 +0100 Subject: [PATCH 083/130] Improve TemplateStream tag recognition (#2400) Sometimes the occasional `{` is required in template sources, such as using CSS within HTML. Double-braces could be used (as for processing JSON templates) but is more complex than necessary. Example: ``` ``` This PR changes behaviour so that `td {padding` is interpreted as a tag but `td { padding` is not. Further, if the opening brace is immediately followed by whitespace or " then it is ignored and emitted as-is. This allows JSON to be templated without resorting to double braces. --- Sming/Core/Data/Stream/DataSourceStream.cpp | 16 +- .../Data/Stream/IFS/JsonDirectoryTemplate.h | 1 - Sming/Core/Data/Stream/SectionStream.cpp | 4 +- Sming/Core/Data/Stream/SectionStream.h | 52 ++++- Sming/Core/Data/Stream/SectionTemplate.cpp | 10 +- Sming/Core/Data/Stream/SectionTemplate.h | 57 ++++- Sming/Core/Data/Stream/TemplateStream.cpp | 13 +- Sming/Core/Data/Stream/TemplateStream.h | 26 +++ docs/source/framework/core/data/format.rst | 10 + .../framework/core/data/streams/templates.rst | 212 ++++++++++++++++++ docs/source/upgrading/4.4-4.5.rst | 16 ++ docs/source/upgrading/index.rst | 1 + samples/Basic_IFS/resource/listing.html | 3 + samples/Basic_IFS/resource/listing.json | 35 ++- tests/HostTests/modules/TemplateStream.cpp | 40 +++- 15 files changed, 442 insertions(+), 54 deletions(-) create mode 100644 docs/source/framework/core/data/format.rst create mode 100644 docs/source/framework/core/data/streams/templates.rst create mode 100644 docs/source/upgrading/4.4-4.5.rst diff --git a/Sming/Core/Data/Stream/DataSourceStream.cpp b/Sming/Core/Data/Stream/DataSourceStream.cpp index 3af18606f1..4a7e0e41bd 100644 --- a/Sming/Core/Data/Stream/DataSourceStream.cpp +++ b/Sming/Core/Data/Stream/DataSourceStream.cpp @@ -41,13 +41,17 @@ size_t IDataSourceStream::readBytes(char* buffer, size_t length) String IDataSourceStream::readString(size_t maxLen) { - size_t avail = available(); - size_t len = std::min(avail, maxLen); - String s; - if(s.setLength(len)) { - len = readBytes(s.begin(), len); - s.setLength(len); + size_t remain = maxLen; + while(remain != 0) { + char buffer[256]; + size_t len = readBytes(buffer, remain); + if(len == 0) { + break; + } + s.concat(buffer, len); + remain -= len; } + return s; } diff --git a/Sming/Core/Data/Stream/IFS/JsonDirectoryTemplate.h b/Sming/Core/Data/Stream/IFS/JsonDirectoryTemplate.h index 61ec5bc438..2b19349f75 100644 --- a/Sming/Core/Data/Stream/IFS/JsonDirectoryTemplate.h +++ b/Sming/Core/Data/Stream/IFS/JsonDirectoryTemplate.h @@ -25,7 +25,6 @@ class JsonDirectoryTemplate : public DirectoryTemplate public: JsonDirectoryTemplate(IDataSourceStream* source, Directory* dir) : DirectoryTemplate(source, dir) { - setDoubleBraces(true); setFormatter(Format::json); } }; diff --git a/Sming/Core/Data/Stream/SectionStream.cpp b/Sming/Core/Data/Stream/SectionStream.cpp index 7eef06378f..7da1a0f9a2 100644 --- a/Sming/Core/Data/Stream/SectionStream.cpp +++ b/Sming/Core/Data/Stream/SectionStream.cpp @@ -16,11 +16,13 @@ * Scan through entire source stream to map location and size of sections. * Called once by constructor. */ -void SectionStream::scanSource() +void SectionStream::scanSource(uint8_t maxSections) { constexpr size_t bufSize{512}; char buffer[bufSize]; + sections.reset(new Section[maxSections]{}); + size_t offset{0}; while(sectionCount < maxSections && !stream->isFinished()) { auto& sect = sections[sectionCount]; diff --git a/Sming/Core/Data/Stream/SectionStream.h b/Sming/Core/Data/Stream/SectionStream.h index b5b3fb2ee7..3cb0230570 100644 --- a/Sming/Core/Data/Stream/SectionStream.h +++ b/Sming/Core/Data/Stream/SectionStream.h @@ -13,21 +13,18 @@ #pragma once #include "DataSourceStream.h" +#include /** * @brief Presents each section within a source stream as a separate stream * - * Sections are marked {!SECTION} ... {/SECTION} + * Sections are (by default) marked {!SECTION} ... {/SECTION} * This is typically used with templating but can be used with any stream type provided * the tags do not conflict with content. - * - * TODO: Allow tags to be changed. */ class SectionStream : public IDataSourceStream { public: - static constexpr uint8_t maxSections = 5; - struct Section { uint32_t start; // Within stream uint32_t size; @@ -51,14 +48,25 @@ class SectionStream : public IDataSourceStream */ using NextRecord = Delegate; - SectionStream(IDataSourceStream* source) : stream(source), startTag(F("{SECTION}")), endTag(F("{/SECTION}")) + /** + * @brief Construct a section stream with default options + */ + SectionStream(IDataSourceStream* source, uint8_t maxSections = 5) + : SectionStream(source, maxSections, F("{SECTION}"), F("{/SECTION}")) { - scanSource(); } - ~SectionStream() + /** + * @brief Construct a section stream + * @param source Contains all section data, must support random seeking + * @param startTag Unique text used to mark start of a section + * @param endTag Marks end of a section + */ + SectionStream(IDataSourceStream* source, uint8_t maxSections, const String& startTag, const String& endTag) + : startTag(startTag), endTag(endTag) { - delete stream; + stream.reset(source); + scanSource(maxSections); } int available() override @@ -86,16 +94,27 @@ class SectionStream : public IDataSourceStream return section ? section->recordIndex : -1; } + /** + * @brief Get number of sections in this stream + */ size_t count() const { return sectionCount; } + /** + * @brief Get description of the current section + * @retval Section* The section information, or nullptr if there is no current section + */ const Section* getSection() const { return getSection(currentSectionIndex); } + /** + * @brief Get description for any section given its index + * @retval Section* The section information, or nullptr if section was not found + */ const Section* getSection(unsigned index) const { if(index < sectionCount) { @@ -105,11 +124,17 @@ class SectionStream : public IDataSourceStream } } + /** + * @brief Register a callback to be invoked when moving to a new section + */ void onNextSection(NextSection callback) { nextSectionCallback = callback; } + /** + * @brief Register a callback to be invoked when moving to a new record + */ void onNextRecord(NextRecord callback) { nextRecordCallback = callback; @@ -120,6 +145,9 @@ class SectionStream : public IDataSourceStream */ bool gotoSection(uint8_t index); + /** + * @brief Goto a new section after current tag has been processed + */ bool setNewSection(int8_t index) { if(index < 0 || index >= sectionCount) { @@ -150,14 +178,14 @@ class SectionStream : public IDataSourceStream } private: - void scanSource(); + void scanSource(uint8_t maxSections); - IDataSourceStream* stream{nullptr}; + std::unique_ptr stream; NextSection nextSectionCallback; NextRecord nextRecordCallback; String startTag; String endTag; - Section sections[maxSections]{}; + std::unique_ptr sections; uint32_t readOffset{0}; uint32_t sectionOffset{0}; uint8_t sectionCount{0}; diff --git a/Sming/Core/Data/Stream/SectionTemplate.cpp b/Sming/Core/Data/Stream/SectionTemplate.cpp index 999aad8cfb..e7403e7511 100644 --- a/Sming/Core/Data/Stream/SectionTemplate.cpp +++ b/Sming/Core/Data/Stream/SectionTemplate.cpp @@ -244,8 +244,8 @@ class ArgList } // namespace -SectionTemplate::SectionTemplate(IDataSourceStream* source) - : TemplateStream(§ionStream, false), sectionStream(source) +SectionTemplate::SectionTemplate(IDataSourceStream* source, uint8_t maxSections) + : TemplateStream(§ionStream, false), sectionStream(source, maxSections) { setFormatter(Format::standard); sectionStream.onNextSection([this]() { seekFrom(0, SeekOrigin::Start); }); @@ -272,7 +272,7 @@ String SectionTemplate::closeTag() enableOutput(enable); if(conditionalLevel == 0 && newSection >= 0) { - debug_e("NewSection: %d", newSection); + debug_d("NewSection: %d", newSection); sectionStream.setNewSection(newSection); newSection = -1; } @@ -283,7 +283,7 @@ String SectionTemplate::closeTag() String SectionTemplate::elseTag() { if(newSection >= 0) { - debug_e("NewSection: %d", newSection); + debug_d("NewSection: %d", newSection); sectionStream.setNewSection(newSection); newSection = -1; } @@ -457,7 +457,7 @@ String SectionTemplate::evaluate(char*& expr) if(unsigned(n) >= sectionStream.count()) { return nullptr; } - debug_e("goto: %d, recordIndex = %d", n, recordIndex()); + debug_d("goto: %d, recordIndex = %d", n, recordIndex()); newSection = n; } return ""; diff --git a/Sming/Core/Data/Stream/SectionTemplate.h b/Sming/Core/Data/Stream/SectionTemplate.h index d396952c98..77d82b0612 100644 --- a/Sming/Core/Data/Stream/SectionTemplate.h +++ b/Sming/Core/Data/Stream/SectionTemplate.h @@ -28,6 +28,8 @@ * Anything else is treated as a variable name. * Separator is : * + * Conditional if/else/endif statements may be nested. + * * @note Command tags are prefixed with 'Q' to allow use of reserved words * in the Command enumeration. This represents the ! prefix in actual use. */ @@ -42,8 +44,8 @@ "{!pad:A:B:C} Copy of A padded to at least B characters with C (default is space). Use -ve B to left-pad. C") \ XX(Qrepeat, "{!repeat:A:B} Repeat A, number of iterations is B") \ XX(Qkb, "{!kb:A} Convert A to KB") \ - XX(Qifdef, "{!ifdef:A}block{/if} emit block if A is not zero-length") \ - XX(Qifndef, "{!ifdef:A}block{/if} emit block if A is zero-length") \ + XX(Qifdef, "{!ifdef:A} emit block if A is not zero-length") \ + XX(Qifndef, "{!ifdef:A} emit block if A is zero-length") \ XX(Qifeq, "{!ifeq:A:B} emit block if A == B") \ XX(Qifneq, "{!ifneq:A:B} emit block if A != B") \ XX(Qifgt, "{!ifgt:A:B} emit block if A > B") \ @@ -55,7 +57,7 @@ XX(Qifnin, "{!ifin:A:B} emit block if A does not contain B") \ XX(Qelse, "{!else}") \ XX(Qendif, "{!endif}") \ - XX(Qadd, "{!add:A:B} A - B") \ + XX(Qadd, "{!add:A:B} A + B") \ XX(Qsub, "{!sub:A:B} A - B") \ XX(Qgoto, "{!goto:A} move to section A") \ XX(Qcount, "{!count:A} emit number of records in section A") \ @@ -85,7 +87,7 @@ class SectionTemplate : public TemplateStream #undef XX }; - SectionTemplate(IDataSourceStream* source); + SectionTemplate(IDataSourceStream* source, uint8_t maxSections = 5); /** * @brief Application callback to process additional fields @@ -93,54 +95,99 @@ class SectionTemplate : public TemplateStream * @param name Field name, never null * @retval String The field value * @note Applications should call `escape()` if required before returning content. - * Use `stream.state()` to determine the current section being processed. */ using GetValue = Delegate; using NextRecord = SectionStream::NextRecord; + /** + * @brief Set a callback to be invoked + * + * Alternative to subclassing. + */ void onGetValue(GetValue callback) { getValueCallback = callback; } + /** + * @brief Associate a text format with this template stream + * @param formatter Provide formatter so we can call escape(), etc. as required + */ void setFormatter(Formatter& formatter) { activeFormatter = &formatter; } + /** + * @brief Get the stream format + * @retval Formatter& The formatter in effect. Default is :cpp:class:Format::Standard. + */ Formatter& formatter() const { return *activeFormatter; } + /** + * @brief Get the MIME type associated with this template stream + * @retval MimeType As defined by the formatter. Default is MIME_TEXT. + */ MimeType getMimeType() const override { return activeFormatter->mimeType(); } + /** + * @brief Access the underlying section stream + * @retval SectionStream& Wraps source stream provided in constructor + * + * Provided for debugging and other purposes. + * Applications should not use this method. + */ const SectionStream& stream() const { return sectionStream; } + /** + * @brief Get the index for the current section + * @retval int Indices are 0-based, returns -1 if 'Before Start' + */ int sectionIndex() const { return sectionStream.sectionIndex(); } + /** + * @brief Get number of sections in source stream + * @retval uint8_t Source is scanned in constructor so this is always valid + */ uint8_t sectionCount() const { return sectionStream.count(); } + /** + * @brief Get current record index + * @retval int Indices are 0-based, returns -1 if 'Before Start' + */ int recordIndex() const { return sectionStream.recordIndex(); } + /** + * @brief Discard current output and change current section + * @param uint8_t Index of section to move to + * @retval bool true on success, false if section index invalid + */ bool gotoSection(uint8_t index); + /** + * @brief Set a callback to be invoked when a new record is required + * + * Can be used as alternative to subclassing. + */ void onNextRecord(NextRecord callback) { nextRecordCallback = callback; diff --git a/Sming/Core/Data/Stream/TemplateStream.cpp b/Sming/Core/Data/Stream/TemplateStream.cpp index a3fe30d7a1..48f47ebd18 100644 --- a/Sming/Core/Data/Stream/TemplateStream.cpp +++ b/Sming/Core/Data/Stream/TemplateStream.cpp @@ -66,9 +66,16 @@ uint16_t TemplateStream::readMemoryBlock(char* data, int bufSize) return 0; } - auto findStartTag = [this](char* buf) { - auto p = doubleBraces ? strstr(buf, "{{") : strchr(buf, '{'); - return static_cast(p); + auto findStartTag = [this](char* buf) -> char* { + if(doubleBraces) { + return strstr(buf, "{{"); + } + + char* p = buf; + while((p = strchr(p, '{')) != nullptr && (p[1] <= ' ' || p[1] == '"')) { + ++p; + } + return p; }; auto start = data; diff --git a/Sming/Core/Data/Stream/TemplateStream.h b/Sming/Core/Data/Stream/TemplateStream.h index 8133b4a1eb..67749a9af6 100644 --- a/Sming/Core/Data/Stream/TemplateStream.h +++ b/Sming/Core/Data/Stream/TemplateStream.h @@ -26,6 +26,15 @@ * @brief Stream which performs variable-value substitution on-the-fly * * Template uses {varname} style markers which are replaced as the stream is read. + * + * Note: There must be no whitespace after the opening brace. + * For example, `{ varname }` will be emitted as-is without modification. + * + * This allows inclusion of CSS fragments such as `td { padding: 0 10px; }` in HTML. + * + * If necessary, use double-braces `{{varname}}` in templates and enable by calling `setDoubleBraces(true)`. + * + * Invalid tags, such as `{"abc"}` will be ignored, so JSON templates do not require special treatment. * * @ingroup stream */ @@ -116,6 +125,11 @@ class TemplateStream : public IDataSourceStream enableNextState = enable; } + /** + * @brief Determine if stream output is active + * + * Used by SectionTemplate class when processing conditional tags. + */ bool isOutputEnabled() const { return outputEnabled; @@ -130,6 +144,18 @@ class TemplateStream : public IDataSourceStream doubleBraces = enable; } + /** + * @brief Evaluate a template expression + * @param expr IN: First character after the opening brace(s) + * OUT: First character after the closing brace(s) + * @retval String + * + * Called internally and an opening brace ("{" or "{{") has been found. + * Default behaviour is to locate the closing brace(s) and interpret the + * bounded text as a variable name, which is passsed to `getValue`. + * + * This method is overridden by SectionTemplate to support more complex expressions. + */ virtual String evaluate(char*& expr); /** diff --git a/docs/source/framework/core/data/format.rst b/docs/source/framework/core/data/format.rst new file mode 100644 index 0000000000..95b9fa138e --- /dev/null +++ b/docs/source/framework/core/data/format.rst @@ -0,0 +1,10 @@ +Data formatting +=============== + +A standard mechanism is provided for encoding and decoding text in commonly-used text formats +using a :cpp:class:`Format::Formatter` class implementation. + + +.. doxygennamespace:: Format + :members: + diff --git a/docs/source/framework/core/data/streams/templates.rst b/docs/source/framework/core/data/streams/templates.rst new file mode 100644 index 0000000000..eb2c7eb49c --- /dev/null +++ b/docs/source/framework/core/data/streams/templates.rst @@ -0,0 +1,212 @@ +Template Streams +================ + +.. highlight:: c++ + +Sming provides several classes to assist with serving dynamic content: + +Basic Templating +---------------- + +The :cpp:class:`TemplateStream` class is a stream which performs variable-value substitution using +``{varname}`` style markers, which are replaced as the stream is read. + +You can find a simple demonstration of how this class is used in the +:sample:`HttpServer_Bootstrap` sample application. + +.. note:: + + There must be no whitespace after the opening brace. + For example, ``{ varname }`` will be emitted as-is without modification. + + This allows inclusion of CSS fragments such as ``td { padding: 0 10px; }`` in HTML + without resorting to double-braces. + + If necessary, use double-braces ``{{varname}}`` in your template and + call :cpp:func:`TemplateStream::setDoubleBraces` ``(true)``. + + Invalid tags, such as ``{"abc"}`` will be ignored, so JSON templates do not require + special treatment. + + +Variable values can be set using :cpp:func:`TemplateStream::setVar` or :cpp:func:`TemplateStream::setVars`. +These are stored in a :cpp:class:`HashMap` which can be accessing directly via :cpp:func:`TemplateStream::variables`. + +To support for calculated values or external lookups an optional callback may +be provided via :cpp:func:`TemplateStream::onGetValue`. +This is invoked only if a variable is not found in the map. + +Another option is to use TemplateStream as a base class and override the :cpp:class:`TemplateStream::getValue` method. + +.. important:: + + If required, text must be escaped appropriately for the output format. + For example, encoding reserved HTML characters can be handled using :cpp:class:`Format::Html::escape`. + + +Advanced Templating +------------------- + +Introduction +~~~~~~~~~~~~ + +The :cpp:class:`SectionTemplate` class extends :cpp:class:`TemplateStream` to provide more advanced dataset processing capabilities. +It is intended to be used as the base class for a data provider. + +One such implementation is the :cpp:class:`IFS::DirectoryTemplate` class. +The :sample:`Basic_IFS` sample demonstrates how it can be used to provide a formatted directory +listing in multiple formats, using a different template for each format. + +If the output format requires escaping, create an instance of the appropriate :cpp:class:`Format::Formatter` +and call :cpp:class:`SectionTemplate::setFormatter`. +If providing custom values via callback, obtain the current formatter via :cpp:class:`SectionTemplate::formatter` +class and call the ``escape`` method. +Note that for performance reasons this is not done automatically as often variable values +do not require escaping. User-provided values or filenames must always be properly escaped. + + +Sections +~~~~~~~~ + +Templates typically contain multiple sections. +The :cpp:class:`IFS::DirectoryTemplate`, for example, uses 3 sections for header, content and footer. +The header and footer are emitted exactly once, but the content section is repeated for each available data record. + +The :cpp:class:`SectionStream` class is used internally so that all sections can be provided within a single file. + +Sections are (by default) marked ``{SECTION}`` ... ``{/SECTION}``. +Everything outside of these markers is ignored, so can contain comments. + + +Using SectionTemplate +~~~~~~~~~~~~~~~~~~~~~ + +Implementations should provide the following methods: + +nextRecord + This method is called before a new content record is about to be output. + Here's the annotated :cpp:class:`IFS::DirectoryTemplate` implementation:: + + // Return true if we have a new valid record, false if not + bool nextRecord() override + { + // Content section we fetch the next directory record, if there is one + if(sectionIndex() == 1) { + return directory->next(); + } + + // This code emits the header and footer sections exactly once + // Returning false suppresses their output completely + return recordIndex() < 0; + } + + This sets up the 'current' directory information record. + + +getValue + Lookup values for a given field:: + + String getValue(const char* name) override + { + // return ... + } + + .. important:: + + If required, text must be escaped appropriately for the output format. + Use :cpp:class:`SectionTemplate::formatter` to obtain the current + For example, encoding reserved HTML characters can be handled using :cpp:class:`Format::Html::escape`. + + +Control language +~~~~~~~~~~~~~~~~ + +A basic control language is implemented using ! escaped tags. +Commands may have zero or more arguments, separated by ``:``. + +- Numbers must be decimal and start with a digit, e.g. ``11`` or ``5.6`` +- Strings must be quoted "..." +- Sub-expressions must be contained in braces {...} + +Anything else is treated as a variable name. +Variable names beginning with $ are reserved for internal use. +The following values are currently defined: + +``$section`` The current section index +``$record`` The current record index + +Conditional if/else/endif statements may be nested. + +This is the current command list: + +- ``{!int:A}`` Output A as integer +- ``{!float:A}`` Output A as float +- ``{!string:A}`` Output A as quoted string +- ``{!mime_type:A}`` Get MIME type string for a filename +- ``{!replace:A:B:C}`` Copy of A with all occurrences of B replaced with C +- ``{!length:A}`` Number of characters in A +- ``{!pad:A:B:C}`` Copy of A padded to at least B characters with C (default is space). Use -ve B to left-pad. C +- ``{!repeat:A:B}`` Repeat A, number of iterations is B +- ``{!kb:A}`` Convert A to KB +- ``{!ifdef:A}`` emit block if A is not zero-length +- ``{!ifdef:A}`` emit block if A is zero-length +- ``{!ifeq:A:B}`` emit block if A == B +- ``{!ifneq:A:B}`` emit block if A != B +- ``{!ifgt:A:B}`` emit block if A > B +- ``{!iflt:A:B}`` emit block if A < B +- ``{!ifge:A:B}`` emit block if A >= B +- ``{!ifle:A:B}`` emit block if A <= B +- ``{!ifbtw:A:B:C}`` emit block if B <= A <= C +- ``{!ifin:A:B}`` emit block if A contains B +- ``{!ifin:A:B}`` emit block if A does not contain B +- ``{!else}`` +- ``{!endif}`` +- ``{!add:A:B}`` A + B +- ``{!sub:A:B}`` A - B +- ``{!goto:A}`` move to section A +- ``{!count:A}`` emit number of records in section A +- ``{!index:A}`` emit current record index for section A + +.. note:: + + See :source:`Sming/Core/Data/Streams/SectionTemplate.h` for an up-to-date list of commands and internal variables. + +Here's an excerpt from the Basic_IFS sample, displaying information for a single file: + +.. highlight:: html + + {!iflt:$record:100} + + {$record} + {file_id} + {icon} {name} + {!mime_type:name} + {modified} + {!ifin:attr:"D"} + Too many records {$record} + {!endif} + + + +API Reference +------------- + +.. doxygenclass:: TemplateStream + :members: + +.. doxygenclass:: SectionTemplate + :members: + +.. doxygenclass:: SectionStream + :members: + diff --git a/docs/source/upgrading/4.4-4.5.rst b/docs/source/upgrading/4.4-4.5.rst new file mode 100644 index 0000000000..038870a5bf --- /dev/null +++ b/docs/source/upgrading/4.4-4.5.rst @@ -0,0 +1,16 @@ +From v4.4 to v4.5 +================= + +.. highlight:: c++ + +Template Streams +---------------- + +The :cpp:class:`TemplateStream` class has been updated to improve tag recognition (:pull-request:`2400`). +This means regular ``{varname}`` tags are sufficient for most purposes, including JSON templates. + +The :cpp:class:`IFS::JsonDirectoryTemplate` previously used double-brace tags such as ``{{varname}}``. +It now uses regular tags by default, so if you use this class either: + +1. Add a call to :cpp:func:`TemplateStream::setDoubleBraces` in your code, or +2. Update your templates to use single braces diff --git a/docs/source/upgrading/index.rst b/docs/source/upgrading/index.rst index 0365edf5b5..5dde9f953d 100644 --- a/docs/source/upgrading/index.rst +++ b/docs/source/upgrading/index.rst @@ -7,6 +7,7 @@ For newer versions we have dedicated pages. .. toctree:: :maxdepth: 1 + 4.4-4.5 4.3-4.4 4.2-4.3 4.1-4.2 diff --git a/samples/Basic_IFS/resource/listing.html b/samples/Basic_IFS/resource/listing.html index dba33cb68f..271c1a7a8f 100644 --- a/samples/Basic_IFS/resource/listing.html +++ b/samples/Basic_IFS/resource/listing.html @@ -11,6 +11,9 @@ Index of '{path}' +

Basic IFS demo

diff --git a/samples/Basic_IFS/resource/listing.json b/samples/Basic_IFS/resource/listing.json index 262a56e2b8..be790e8afb 100644 --- a/samples/Basic_IFS/resource/listing.json +++ b/samples/Basic_IFS/resource/listing.json @@ -1,26 +1,25 @@ {SECTION} { - {{!ifdef:parent}}"parent":"{{parent}}", - {{!endif}}"files": [ +{!ifdef:parent}"parent":"{parent}", +{!endif}"files":[ {/SECTION} -{SECTION}{{!ifgt:$record:0}}, -{{!endif}} { - "record": {{!as_int:$record}}, - "id": "{{file_id}}", - "name": "{{name}}", - "modified": "{{modified}}", - "size": {{!as_int:size}}, - "original_size": {{!as_int:original_size}}, - "attr": "{{attr}}", - "access": "{{access}}", - "compression": "{{compression}}" - }{/SECTION} +{SECTION}{!ifgt:$record:0}, +{!endif}{"record": {!as_int:$record}, +"id":"{file_id}", +"name":"{name}", +"modified":"{modified}", +"size":{!as_int:size}, +"original_size":{!as_int:original_size}, +"attr":"{attr}", +"access":"{access}", +"compression":"{compression}" +}{/SECTION} {SECTION} - ], - "count": {{!as_int:{!count:1}}}, - "total_size": {{!as_int:total_size}}, - "last_error": "{{last_error}}" +], +"count":{!as_int:{!count:1}}, +"total_size":{!as_int:total_size}, +"last_error":"{last_error}" } {/SECTION} diff --git a/tests/HostTests/modules/TemplateStream.cpp b/tests/HostTests/modules/TemplateStream.cpp index 008930f978..514e85a60c 100644 --- a/tests/HostTests/modules/TemplateStream.cpp +++ b/tests/HostTests/modules/TemplateStream.cpp @@ -16,6 +16,12 @@ DEFINE_FSTR_LOCAL(template1_2, "Stream containing value #1, value #2 and [value DEFINE_FSTR_LOCAL(template2, "This text should {disable}not {var1} really {var2:hello} again {enable}be missing.") DEFINE_FSTR_LOCAL(template2_1, "This text should be missing.") +DEFINE_FSTR_LOCAL(template3, "{title}