Skip to content

Commit

Permalink
Merge 1841437 into 5af3c04
Browse files Browse the repository at this point in the history
  • Loading branch information
dpgeorge committed Nov 21, 2020
2 parents 5af3c04 + 1841437 commit a861813
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 1 deletion.
113 changes: 113 additions & 0 deletions lib/utils/pyexec.c
Expand Up @@ -56,6 +56,7 @@ STATIC bool repl_display_debugging_info = 0;
#define EXEC_FLAG_SOURCE_IS_RAW_CODE (8)
#define EXEC_FLAG_SOURCE_IS_VSTR (16)
#define EXEC_FLAG_SOURCE_IS_FILENAME (32)
#define EXEC_FLAG_SOURCE_IS_READER (64)

// parses, compiles and executes the code in the lexer
// frees the lexer before returning
Expand Down Expand Up @@ -91,6 +92,8 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) {
const vstr_t *vstr = source;
lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0);
} else if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) {
lex = mp_lexer_new(MP_QSTR__lt_stdin_gt_, *(mp_reader_t *)source);
} else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) {
lex = mp_lexer_new_from_file(source);
} else {
Expand Down Expand Up @@ -122,6 +125,12 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
// uncaught exception
mp_hal_set_interrupt_char(-1); // disable interrupt
mp_handle_pending(false); // clear any pending exceptions (and run any callbacks)

if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) {
const mp_reader_t *reader = source;
reader->close(reader->data);
}

// print EOF after normal output
if (exec_flags & EXEC_FLAG_PRINT_EOF) {
mp_hal_stdout_tx_strn("\x04", 1);
Expand Down Expand Up @@ -170,6 +179,94 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
}

#if MICROPY_ENABLE_COMPILER

#ifndef MICROPY_REPL_STDIN_BUFFER_MAX
#define MICROPY_REPL_STDIN_BUFFER_MAX (256)
#endif

typedef struct _mp_reader_stdin_t {
bool eof;
uint16_t window_max;
uint16_t window_remain;
} mp_reader_stdin_t;

STATIC mp_uint_t mp_reader_stdin_readbyte(void *data) {
mp_reader_stdin_t *reader = (mp_reader_stdin_t *)data;

if (reader->eof) {
return MP_READER_EOF;
}

int c = mp_hal_stdin_rx_chr();

if (c == CHAR_CTRL_C || c == CHAR_CTRL_D) {
reader->eof = true;
mp_hal_stdout_tx_strn("\x04", 1); // indicate end to host
if (c == CHAR_CTRL_C) {
#if MICROPY_KBD_EXCEPTION
MP_STATE_VM(mp_kbd_exception).traceback_data = NULL;
nlr_raise(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)));
#else
mp_raise_type(&mp_type_KeyboardInterrupt);
#endif
} else {
return MP_READER_EOF;
}
}

if (--reader->window_remain == 0) {
mp_hal_stdout_tx_strn("\x01", 1); // indicate window available to host
reader->window_remain = reader->window_max;
}

return c;
}

STATIC void mp_reader_stdin_close(void *data) {
mp_reader_stdin_t *reader = (mp_reader_stdin_t *)data;
if (!reader->eof) {
reader->eof = true;
mp_hal_stdout_tx_strn("\x04", 1); // indicate end to host
for (;;) {
int c = mp_hal_stdin_rx_chr();
if (c == CHAR_CTRL_C || c == CHAR_CTRL_D) {
break;
}
}
}
}

STATIC void mp_reader_new_stdin(mp_reader_t *reader, mp_reader_stdin_t *reader_stdin, size_t buf_max) {
// Make flow-control window half the buffer size, and indicate to the host that 2x windows are free.
size_t window = buf_max / 2;
char reply[3] = { window & 0xff, window >> 8, 0x01 };
mp_hal_stdout_tx_strn(reply, sizeof(reply));

reader_stdin->eof = false;
reader_stdin->window_max = window;
reader_stdin->window_remain = window;
reader->data = reader_stdin;
reader->readbyte = mp_reader_stdin_readbyte;
reader->close = mp_reader_stdin_close;
}

STATIC int do_reader_stdin(int c) {
if (c != 'A') {
// Unsupported command.
mp_hal_stdout_tx_strn("R\x00", 2);
return 0;
}

// Indicate reception of command.
mp_hal_stdout_tx_strn("R\x01", 2);

mp_reader_t reader;
mp_reader_stdin_t reader_stdin;
mp_reader_new_stdin(&reader, &reader_stdin, MICROPY_REPL_STDIN_BUFFER_MAX);
int exec_flags = EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_READER;
return parse_compile_execute(&reader, MP_PARSE_FILE_INPUT, exec_flags);
}

#if MICROPY_REPL_EVENT_DRIVEN

typedef struct _repl_t {
Expand Down Expand Up @@ -203,6 +300,13 @@ void pyexec_event_repl_init(void) {
STATIC int pyexec_raw_repl_process_char(int c) {
if (c == CHAR_CTRL_A) {
// reset raw REPL
if (vstr_len(MP_STATE_VM(repl_line)) == 2 && vstr_str(MP_STATE_VM(repl_line))[0] == CHAR_CTRL_E) {
int ret = do_reader_stdin(vstr_str(MP_STATE_VM(repl_line))[1]);
if (ret & PYEXEC_FORCED_EXIT) {
return ret;
}
goto reset;
}
mp_hal_stdout_tx_str("raw REPL; CTRL-B to exit\r\n");
goto reset;
} else if (c == CHAR_CTRL_B) {
Expand Down Expand Up @@ -388,6 +492,15 @@ int pyexec_raw_repl(void) {
int c = mp_hal_stdin_rx_chr();
if (c == CHAR_CTRL_A) {
// reset raw REPL
if (vstr_len(&line) == 2 && vstr_str(&line)[0] == CHAR_CTRL_E) {
int ret = do_reader_stdin(vstr_str(&line)[1]);
if (ret & PYEXEC_FORCED_EXIT) {
return ret;
}
vstr_reset(&line);
mp_hal_stdout_tx_str(">");
continue;
}
goto raw_repl_reset;
} else if (c == CHAR_CTRL_B) {
// change to friendly REPL
Expand Down
55 changes: 54 additions & 1 deletion tools/pyboard.py
Expand Up @@ -253,6 +253,7 @@ def inWaiting(self):

class Pyboard:
def __init__(self, device, baudrate=115200, user="micro", password="python", wait=0):
self.use_raw_paste = True
if device.startswith("exec:"):
self.serial = ProcessToSerial(device[len("exec:") :])
elif device.startswith("execpty:"):
Expand Down Expand Up @@ -359,6 +360,41 @@ def follow(self, timeout, data_consumer=None):
# return normal and error output
return data, data_err

def raw_paste_write(self, command_bytes):
# Read initial header, with window size.
data = self.serial.read(2)
window_size = data[0] | data[1] << 8
window_remain = window_size

# Write out the command_bytes data.
i = 0
while i < len(command_bytes):
while window_remain == 0 or self.serial.inWaiting():
data = self.serial.read(1)
if data == b"\x01":
# Device indicated that a new window of data can be sent.
window_remain += window_size
elif data == b"\x04":
# Device indicated abrupt end. Acknowledge it and finish.
self.serial.write(b"\x04")
return
else:
# Unexpected data from device.
raise PyboardError("unexpected read during raw paste: {}".format(data))
# Send out as much data as possible that fits within the allowed window.
b = command_bytes[i : min(i + window_remain, len(command_bytes))]
self.serial.write(b)
window_remain -= len(b)
i += len(b)

# Indicate end of data.
self.serial.write(b"\x04")

# Wait for device to acknowledge end of data.
data = self.read_until(1, b"\x04")
if not data.endswith(b"\x04"):
raise PyboardError("could not complete raw paste: {}".format(data))

def exec_raw_no_follow(self, command):
if isinstance(command, bytes):
command_bytes = command
Expand All @@ -370,7 +406,24 @@ def exec_raw_no_follow(self, command):
if not data.endswith(b">"):
raise PyboardError("could not enter raw repl")

# write command
if self.use_raw_paste:
# Try to enter raw-paste mode.
self.serial.write(b"\x05A\x01")
data = self.serial.read(2)
if data == b"R\x00":
# Device understood raw-paste command but doesn't support it.
pass
elif data == b"R\x01":
# Device supports raw-paste mode, write out the command using this mode.
return self.raw_paste_write(command_bytes)
else:
# Device doesn't support raw-paste, fall back to normal raw REPL.
data = self.read_until(1, b"w REPL; CTRL-B to exit\r\n>")
if not data.endswith(b"w REPL; CTRL-B to exit\r\n>"):
print(data)
raise PyboardError("could not enter raw repl")

# Write command using standard raw REPL, 256 bytes every 10ms.
for i in range(0, len(command_bytes), 256):
self.serial.write(command_bytes[i : min(i + 256, len(command_bytes))])
time.sleep(0.01)
Expand Down

0 comments on commit a861813

Please sign in to comment.