Keep updater USB connections clear of boot and modem controls

This commit is contained in:
mikecarper committed 2026-10-26 19:54:27 -07:00
1 parent ebf2717171
commit 64251f9d9b
9 files changed
+472 -16

No files matched your search

+2 -2
View File
@@ -177,8 +177,8 @@ class HostKissIngressTest(unittest.TestCase):
signature = ('static void __attribute__((noinline)) serviceCommandInterfaces()'
if role == 'simple_repeater' else 'void loop()')
pump = body(source, signature)
self.assertLess(pump.index('mesh::hostkiss::usbInputReserved(console.peek())'),
pump.index('console.read()'))
read = 'const int value = console_read()' if role == 'simple_repeater' else 'console.read()'
self.assertLess(pump.index('mesh::hostkiss::usbInputReserved(console.peek())'), pump.index(read))
if __name__ == '__main__':
+42 -5
View File
@@ -15,12 +15,16 @@ HARNESS = r'''
#include <helpers/ota/MotaSourceSerial.h>
#include <helpers/ota/MotaSeederProto.h>
using namespace mesh::ota;
namespace mesh {
bool kiss_owned=false;
bool isUsbBinaryStreamOwned(){return kiss_owned;}
}
struct Wire:Stream {
std::deque<uint8_t> input;
std::vector<uint8_t> next_response;
int writes=0;
int writes=0,reads=0;
int available()override{return input.size();}
int read()override{if(input.empty())return -1;auto b=input.front();input.pop_front();return b;}
int read()override{++reads;if(input.empty())return -1;auto b=input.front();input.pop_front();return b;}
size_t write(const uint8_t* data,size_t n)override{
assert(n>=4&&data[0]=='M'&&data[1]=='S');++writes;
input.insert(input.end(),next_response.begin(),next_response.end());next_response.clear();return n;
@@ -39,24 +43,33 @@ static std::string controls(SerialMotaSource& source){
}
return output;
}
static bool kissClaimAllowed(SerialMotaSource& control_source){
@KISS_GATE@
return kiss_can_read();
}
int main(){
const std::string command="ota.peer cmd "+std::string(64,'1')+" ota key add "+std::string(64,'A')+"\r";
const std::string dangerous="\rota.peer cmd "+std::string(64,'2')+" reboot\r\nota folder off\r";
const std::vector<uint8_t> payload(dangerous.begin(),dangerous.end());
std::vector<uint8_t> payload={0xc0,0,0x41,0xc0};
payload.insert(payload.end(),dangerous.begin(),dangerous.end());
const auto response=frame(payload);
for(size_t prefix=0;prefix<response.size();++prefix){
g_mock_millis=0;Wire wire;
SerialMotaSource source(wire,MotaStreamWritePolicy::NoFlush,5);
source.enableSharedTextControl();
assert(kissClaimAllowed(source));
wire.next_response.assign(response.begin(),response.begin()+prefix);
std::vector<uint8_t> target(payload.size());
assert(!source.read(0,0,target.data(),target.size()));
assert(source.hasPendingResponse());
assert(!kissClaimAllowed(source));
for(size_t i=prefix;i<response.size();++i){
wire.input.push_back(response[i]);
assert(controls(source).empty());
if(i+1<response.size())assert(!kissClaimAllowed(source));
}
assert(!source.hasPendingResponse());
assert(kissClaimAllowed(source));
wire.push(command);
assert(controls(source)==command);
}
@@ -68,7 +81,9 @@ int main(){
wire.push(command);
wire.next_response=frame({42});
uint8_t value=0;assert(source.read(0,0,&value,1)&&value==42);
assert(!kissClaimAllowed(source)); // queued complete CLI still belongs to console
assert(controls(source)==command);
assert(kissClaimAllowed(source));
// ASCII records can arrive after the binary request has started. Passwords
// and command arguments containing the response magic remain whole text,
// including when the console already consumed the beginning of the line.
@@ -78,11 +93,13 @@ int main(){
SerialMotaSource shared(racing,MotaStreamWritePolicy::NoFlush,5);shared.enableSharedTextControl();
racing.push(login.substr(0,split));
const auto prefix=controls(shared);
if(split && login[split-1]!='\r' && login[split-1]!='\n')assert(!kissClaimAllowed(shared));
racing.next_response.assign(login.begin()+split,login.end());
const auto binary=frame({44});
racing.next_response.insert(racing.next_response.end(),binary.begin(),binary.end());
assert(shared.read(0,0,&value,1)&&value==44);
assert(prefix+controls(shared)==login);
assert(kissClaimAllowed(shared));
}
// Over-capacity controls invalidate the existing prefix and discard to its
// delimiter, so the suffix never becomes a second mutating command.
@@ -95,6 +112,21 @@ int main(){
wire.push("ota.peer result\r");
assert(controls(source)=="ota.peer result\r");
source.resetSessionState();assert(!source.hasPendingResponse());
// An attached shared USB folder pauses immediately while KISS owns the
// port; neither stale-drain reads nor a new MS request may touch its frame.
Wire owned;SerialMotaSource paused(owned,MotaStreamWritePolicy::NoFlush,600);
paused.enableSharedTextControl();owned.input={0xc0,0,0x41,0xc0};
mesh::kiss_owned=true;const auto before_clock=g_mock_millis;
assert(!paused.read(0,0,&value,1));
assert(!owned.reads&&!owned.writes&&owned.input.size()==4&&g_mock_millis==before_clock);
assert(!paused.hasPendingResponse());
owned.input.clear();mesh::kiss_owned=false;owned.next_response=frame({45});
assert(paused.read(0,0,&value,1)&&value==45); // same attached source resumes
// Dedicated UART/BLE sources do not share the physical KISS stream.
Wire dedicated;SerialMotaSource separate(dedicated,MotaStreamWritePolicy::NoFlush,5);
dedicated.next_response=frame({46});mesh::kiss_owned=true;
assert(separate.read(0,0,&value,1)&&value==46);
mesh::kiss_owned=false;
}
'''
@@ -104,11 +136,16 @@ class RepeaterOtaSerialControlTest(unittest.TestCase):
with tempfile.TemporaryDirectory(prefix='meshcore-repeater-mota-control-') as folder:
cpp = Path(folder)/'test.cpp'
binary = Path(folder)/('test.exe' if os.name == 'nt' else 'test')
cpp.write_text(HARNESS)
main = (ROOT/'examples/simple_repeater/main.cpp').read_text()
start = main.index('const auto kiss_can_read = [&]()')
end = main.index('\n };', start)+len('\n };')
cpp.write_text(HARNESS.replace('@KISS_GATE@', main[start:end]))
sanitizers = [] if os.name == 'nt' else [
'-fsanitize=address,undefined', '-fno-sanitize-recover=all', '-fno-pie', '-no-pie']
built=subprocess.run([
'g++','-std=c++17','-DESP32_PLATFORM=1',
'g++','-std=c++17','-DESP32_PLATFORM=1','-DMESH_ENABLE_DIRECT_OTA_ADMIN=1',
'-DMESH_OTA_FOLDER_SERIAL_SHARED_CONSOLE=1',
'-DMESH_ENABLE_HOST_KISS=1','-DARDUINO=1',
*sanitizers,
'-I'+str(ROOT/'test/mocks'),'-I'+str(ROOT/'src'),str(cpp),
str(ROOT/'src/helpers/ota/MotaSourceSerial.cpp'),'-o',str(binary)],