Skip to content

Commit

Permalink
extmod/network_ppp: Allow stream=None to suspend PPP.
Browse files Browse the repository at this point in the history
This allows the stream to be set to `None`, which essentially stops all PPP
communication without disconnecting the session.

This allows replacing the stream on-the-fly to suspend it, for example to
send AT commands to a modem without completely disconnecting and
re-establishing the PPP connection:

    uart = ppp.config('stream')
    ppp.config(stream=None)
    uart.write(b'+++')
    # do some AT commands
    uart.write(b'ATO\r\n')
    ppp.config(stream=uart)

Any attempted communication by PPP while the stream is not connected will
register as simple packet loss to the LwIP stack because we return 0 for
any write calls, and protocols like TCP will then automatically handle
retrying.

Signed-off-by: Daniël van de Giessen <daniel@dvdgiessen.nl>
  • Loading branch information
DvdGiessen authored and dpgeorge committed Nov 13, 2024
1 parent 161e2bd commit 77406b4
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 25 deletions.
68 changes: 50 additions & 18 deletions extmod/network_ppp_lwip.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ const mp_obj_type_t mp_network_ppp_lwip_type;

static mp_obj_t network_ppp___del__(mp_obj_t self_in);

static void network_ppp_stream_uart_irq_disable(network_ppp_obj_t *self) {
if (self->stream == mp_const_none) {
return;
}

// Disable UART IRQ.
mp_obj_t dest[3];
mp_load_method(self->stream, MP_QSTR_irq, dest);
dest[2] = mp_const_none;
mp_call_method_n_kw(1, 0, dest);
}

static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
network_ppp_obj_t *self = ctx;
switch (err_code) {
Expand All @@ -68,12 +80,9 @@ static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
break;
case PPPERR_USER:
if (self->state >= STATE_ERROR) {
// Disable UART IRQ.
mp_obj_t dest[3];
mp_load_method(self->stream, MP_QSTR_irq, dest);
dest[2] = mp_const_none;
mp_call_method_n_kw(1, 0, dest);
// Indicate that the IRQ is disabled.
network_ppp_stream_uart_irq_disable(self);
// Indicate that we are no longer connected and thus
// only need to free the PPP PCB, not close it.
self->state = STATE_ACTIVE;
}
// Clean up the PPP PCB.
Expand All @@ -91,7 +100,9 @@ static mp_obj_t network_ppp_make_new(const mp_obj_type_t *type, size_t n_args, s

mp_obj_t stream = all_args[0];

mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
if (stream != mp_const_none) {
mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
}

network_ppp_obj_t *self = mp_obj_malloc_with_finaliser(network_ppp_obj_t, type);
self->state = STATE_INACTIVE;
Expand All @@ -105,7 +116,7 @@ static mp_obj_t network_ppp___del__(mp_obj_t self_in) {
network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in);
if (self->state >= STATE_ACTIVE) {
if (self->state >= STATE_ERROR) {
// Still connected over the UART stream.
// Still connected over the stream.
// Force the connection to close, with nocarrier=1.
self->state = STATE_INACTIVE;
ppp_close(self->pcb, 1);
Expand All @@ -127,10 +138,11 @@ static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) {
}

mp_int_t total_len = 0;
for (;;) {
mp_obj_t stream = self->stream;
while (stream != mp_const_none) {
uint8_t buf[256];
int err;
mp_uint_t len = mp_stream_rw(self->stream, buf, sizeof(buf), &err, 0);
mp_uint_t len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0);
if (len == 0) {
break;
}
Expand All @@ -149,6 +161,19 @@ static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) {
}
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_poll_obj, 1, 2, network_ppp_poll);

static void network_ppp_stream_uart_irq_enable(network_ppp_obj_t *self) {
if (self->stream == mp_const_none) {
return;
}

// Enable UART IRQ to call PPP.poll() when incoming data is ready.
mp_obj_t dest[4];
mp_load_method(self->stream, MP_QSTR_irq, dest);
dest[2] = mp_obj_new_bound_meth(MP_OBJ_FROM_PTR(&network_ppp_poll_obj), MP_OBJ_FROM_PTR(self));
dest[3] = mp_load_attr(self->stream, MP_QSTR_IRQ_RXIDLE);
mp_call_method_n_kw(2, 0, dest);
}

static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
if (n_args != 1 && kwargs->used != 0) {
mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed"));
Expand All @@ -160,8 +185,16 @@ static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t
if (mp_map_slot_is_filled(kwargs, i)) {
switch (mp_obj_str_get_qstr(kwargs->table[i].key)) {
case MP_QSTR_stream: {
mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
if (kwargs->table[i].value != mp_const_none) {
mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
}
if (self->state >= STATE_ACTIVE) {
network_ppp_stream_uart_irq_disable(self);
}
self->stream = kwargs->table[i].value;
if (self->state >= STATE_ACTIVE) {
network_ppp_stream_uart_irq_enable(self);
}
break;
}
default:
Expand Down Expand Up @@ -210,10 +243,14 @@ static u32_t network_ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t l
}
mp_printf(&mp_plat_print, ")\n");
#endif
mp_obj_t stream = self->stream;
if (stream == mp_const_none) {
return 0;
}
int err;
// The return value from this output callback is the number of bytes written out.
// If it's less than the requested number of bytes then lwIP will propagate out an error.
return mp_stream_rw(self->stream, (void *)data, len, &err, MP_STREAM_RW_WRITE);
return mp_stream_rw(stream, (void *)data, len, &err, MP_STREAM_RW_WRITE);
}

static mp_obj_t network_ppp_connect(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
Expand All @@ -236,12 +273,7 @@ static mp_obj_t network_ppp_connect(size_t n_args, const mp_obj_t *args, mp_map_
}
self->state = STATE_ACTIVE;

// Enable UART IRQ to call PPP.poll() when incoming data is ready.
mp_obj_t dest[4];
mp_load_method(self->stream, MP_QSTR_irq, dest);
dest[2] = mp_obj_new_bound_meth(MP_OBJ_FROM_PTR(&network_ppp_poll_obj), MP_OBJ_FROM_PTR(self));
dest[3] = mp_load_attr(self->stream, MP_QSTR_IRQ_RXIDLE);
mp_call_method_n_kw(2, 0, dest);
network_ppp_stream_uart_irq_enable(self);
}

if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) {
Expand Down
29 changes: 22 additions & 7 deletions ports/esp32/network_ppp.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
}

static mp_obj_t ppp_make_new(mp_obj_t stream) {
mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
if (stream != mp_const_none) {
mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
}

ppp_if_obj_t *self = mp_obj_malloc_with_finaliser(ppp_if_obj_t, &ppp_if_type);
self->stream = stream;
Expand All @@ -100,8 +102,14 @@ MP_DEFINE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj, ppp_make_new);

static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) {
ppp_if_obj_t *self = ctx;

mp_obj_t stream = self->stream;
if (stream == mp_const_none) {
return 0;
}

int err;
return mp_stream_rw(self->stream, data, len, &err, MP_STREAM_RW_WRITE);
return mp_stream_rw(stream, data, len, &err, MP_STREAM_RW_WRITE);
}

static void pppos_client_task(void *self_in) {
Expand All @@ -110,10 +118,15 @@ static void pppos_client_task(void *self_in) {

int len = 0;
while (ulTaskNotifyTake(pdTRUE, len <= 0) == 0) {
int err;
len = mp_stream_rw(self->stream, buf, sizeof(buf), &err, 0);
if (len > 0) {
pppos_input_tcpip(self->pcb, (u8_t *)buf, len);
mp_obj_t stream = self->stream;
if (stream == mp_const_none) {
len = 0;
} else {
int err;
len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0);
if (len > 0) {
pppos_input_tcpip(self->pcb, (u8_t *)buf, len);
}
}
}

Expand Down Expand Up @@ -324,7 +337,9 @@ static mp_obj_t ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs
if (mp_map_slot_is_filled(kwargs, i)) {
switch (mp_obj_str_get_qstr(kwargs->table[i].key)) {
case MP_QSTR_stream: {
mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
if (kwargs->table[i].value != mp_const_none) {
mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
}
self->stream = kwargs->table[i].value;
break;
}
Expand Down

0 comments on commit 77406b4

Please sign in to comment.