blob: 2c3dac92012a0b9ef14527eafc827bc541b490a5 [file] [log] [blame]
Damien Georgebc952d32024-03-13 17:09:51 +11001/*
2 * This file is part of the MicroPython project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2024 Damien P. George
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
Daniël van de Giessen3b1e22c2024-05-27 14:25:23 +020027// This file is intended to closely match ports/esp32/network_ppp.c. Changes can
28// and should probably be applied to both files. Compare them directly by using:
29// git diff --no-index extmod/network_ppp_lwip.c ports/esp32/network_ppp.c
30
Damien Georgebc952d32024-03-13 17:09:51 +110031#include "py/runtime.h"
32#include "py/mphal.h"
33#include "py/stream.h"
34#include "extmod/modnetwork.h"
35
36#if MICROPY_PY_NETWORK_PPP_LWIP
37
38#include "lwip/dns.h"
39#include "netif/ppp/ppp.h"
40#include "netif/ppp/pppapi.h"
41#include "netif/ppp/pppos.h"
42
43// Enable this to see the serial data going between the PPP layer.
44#define PPP_TRACE_IN_OUT (0)
45
46typedef enum {
47 STATE_INACTIVE,
48 STATE_ACTIVE,
49 STATE_ERROR,
50 STATE_CONNECTING,
51 STATE_CONNECTED,
52} network_ppp_state_t;
53
54typedef struct _network_ppp_obj_t {
55 mp_obj_base_t base;
56 network_ppp_state_t state;
57 int error_code;
58 mp_obj_t stream;
59 ppp_pcb *pcb;
60 struct netif netif;
61} network_ppp_obj_t;
62
63const mp_obj_type_t mp_network_ppp_lwip_type;
64
65static mp_obj_t network_ppp___del__(mp_obj_t self_in);
66
Daniël van de Giessen77406b42024-02-28 12:35:37 +010067static void network_ppp_stream_uart_irq_disable(network_ppp_obj_t *self) {
68 if (self->stream == mp_const_none) {
69 return;
70 }
71
72 // Disable UART IRQ.
73 mp_obj_t dest[3];
74 mp_load_method(self->stream, MP_QSTR_irq, dest);
75 dest[2] = mp_const_none;
76 mp_call_method_n_kw(1, 0, dest);
77}
78
Damien Georgebc952d32024-03-13 17:09:51 +110079static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
80 network_ppp_obj_t *self = ctx;
81 switch (err_code) {
82 case PPPERR_NONE:
83 self->state = STATE_CONNECTED;
84 break;
85 case PPPERR_USER:
86 if (self->state >= STATE_ERROR) {
Daniël van de Giessen77406b42024-02-28 12:35:37 +010087 // Indicate that we are no longer connected and thus
88 // only need to free the PPP PCB, not close it.
Damien Georgebc952d32024-03-13 17:09:51 +110089 self->state = STATE_ACTIVE;
90 }
91 // Clean up the PPP PCB.
92 network_ppp___del__(MP_OBJ_FROM_PTR(self));
93 break;
94 default:
95 self->state = STATE_ERROR;
96 self->error_code = err_code;
97 break;
98 }
99}
100
101static mp_obj_t network_ppp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
102 mp_arg_check_num(n_args, n_kw, 1, 1, false);
103
104 mp_obj_t stream = all_args[0];
105
Daniël van de Giessen77406b42024-02-28 12:35:37 +0100106 if (stream != mp_const_none) {
107 mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
108 }
Damien Georgebc952d32024-03-13 17:09:51 +1100109
110 network_ppp_obj_t *self = mp_obj_malloc_with_finaliser(network_ppp_obj_t, type);
111 self->state = STATE_INACTIVE;
112 self->stream = stream;
113 self->pcb = NULL;
114
115 return MP_OBJ_FROM_PTR(self);
116}
117
118static mp_obj_t network_ppp___del__(mp_obj_t self_in) {
119 network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in);
120 if (self->state >= STATE_ACTIVE) {
121 if (self->state >= STATE_ERROR) {
Daniël van de Giessen77406b42024-02-28 12:35:37 +0100122 // Still connected over the stream.
Damien Georgebc952d32024-03-13 17:09:51 +1100123 // Force the connection to close, with nocarrier=1.
124 self->state = STATE_INACTIVE;
125 ppp_close(self->pcb, 1);
126 }
Daniël van de Giessen3b1e22c2024-05-27 14:25:23 +0200127 network_ppp_stream_uart_irq_disable(self);
Damien Georgebc952d32024-03-13 17:09:51 +1100128 // Free PPP PCB and reset state.
129 self->state = STATE_INACTIVE;
130 ppp_free(self->pcb);
131 self->pcb = NULL;
132 }
133 return mp_const_none;
134}
135static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp___del___obj, network_ppp___del__);
136
137static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) {
138 network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
139
140 if (self->state <= STATE_ERROR) {
141 return MP_OBJ_NEW_SMALL_INT(-MP_EPERM);
142 }
143
144 mp_int_t total_len = 0;
Daniël van de Giessen77406b42024-02-28 12:35:37 +0100145 mp_obj_t stream = self->stream;
146 while (stream != mp_const_none) {
Damien Georgebc952d32024-03-13 17:09:51 +1100147 uint8_t buf[256];
148 int err;
Daniël van de Giessen77406b42024-02-28 12:35:37 +0100149 mp_uint_t len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0);
Damien Georgebc952d32024-03-13 17:09:51 +1100150 if (len == 0) {
151 break;
152 }
153 #if PPP_TRACE_IN_OUT
154 mp_printf(&mp_plat_print, "ppp_in(n=%u,data=", len);
155 for (size_t i = 0; i < len; ++i) {
156 mp_printf(&mp_plat_print, "%02x:", buf[i]);
157 }
158 mp_printf(&mp_plat_print, ")\n");
159 #endif
160 pppos_input(self->pcb, (u8_t *)buf, len);
161 total_len += len;
162 }
163
164 return MP_OBJ_NEW_SMALL_INT(total_len);
165}
166static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_poll_obj, 1, 2, network_ppp_poll);
167
Daniël van de Giessen77406b42024-02-28 12:35:37 +0100168static void network_ppp_stream_uart_irq_enable(network_ppp_obj_t *self) {
169 if (self->stream == mp_const_none) {
170 return;
171 }
172
173 // Enable UART IRQ to call PPP.poll() when incoming data is ready.
174 mp_obj_t dest[4];
175 mp_load_method(self->stream, MP_QSTR_irq, dest);
176 dest[2] = mp_obj_new_bound_meth(MP_OBJ_FROM_PTR(&network_ppp_poll_obj), MP_OBJ_FROM_PTR(self));
177 dest[3] = mp_load_attr(self->stream, MP_QSTR_IRQ_RXIDLE);
178 mp_call_method_n_kw(2, 0, dest);
179}
180
Damien Georgebc952d32024-03-13 17:09:51 +1100181static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
182 if (n_args != 1 && kwargs->used != 0) {
183 mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed"));
184 }
Daniël van de Giessen161e2bd2023-08-09 10:31:29 +0200185 network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
Damien Georgebc952d32024-03-13 17:09:51 +1100186
187 if (kwargs->used != 0) {
188 for (size_t i = 0; i < kwargs->alloc; i++) {
189 if (mp_map_slot_is_filled(kwargs, i)) {
190 switch (mp_obj_str_get_qstr(kwargs->table[i].key)) {
Daniël van de Giessen161e2bd2023-08-09 10:31:29 +0200191 case MP_QSTR_stream: {
Daniël van de Giessen77406b42024-02-28 12:35:37 +0100192 if (kwargs->table[i].value != mp_const_none) {
193 mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE);
194 }
195 if (self->state >= STATE_ACTIVE) {
196 network_ppp_stream_uart_irq_disable(self);
197 }
Daniël van de Giessen161e2bd2023-08-09 10:31:29 +0200198 self->stream = kwargs->table[i].value;
Daniël van de Giessen77406b42024-02-28 12:35:37 +0100199 if (self->state >= STATE_ACTIVE) {
200 network_ppp_stream_uart_irq_enable(self);
201 }
Daniël van de Giessen161e2bd2023-08-09 10:31:29 +0200202 break;
203 }
Damien Georgebc952d32024-03-13 17:09:51 +1100204 default:
205 break;
206 }
207 }
208 }
209 return mp_const_none;
210 }
211
212 if (n_args != 2) {
213 mp_raise_TypeError(MP_ERROR_TEXT("can query only one param"));
214 }
215
216 mp_obj_t val = mp_const_none;
217
218 switch (mp_obj_str_get_qstr(args[1])) {
Daniël van de Giessen161e2bd2023-08-09 10:31:29 +0200219 case MP_QSTR_stream: {
220 val = self->stream;
221 break;
222 }
Damien Georgebc952d32024-03-13 17:09:51 +1100223 default:
224 mp_raise_ValueError(MP_ERROR_TEXT("unknown config param"));
225 }
226
227 return val;
228}
229static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_config_obj, 1, network_ppp_config);
230
231static mp_obj_t network_ppp_status(mp_obj_t self_in) {
232 network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in);
233 if (self->state == STATE_ERROR) {
234 return MP_OBJ_NEW_SMALL_INT(-self->error_code);
235 } else {
236 return MP_OBJ_NEW_SMALL_INT(self->state);
237 }
238}
239static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_status_obj, network_ppp_status);
240
241static u32_t network_ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t len, void *ctx) {
242 network_ppp_obj_t *self = ctx;
243 #if PPP_TRACE_IN_OUT
244 mp_printf(&mp_plat_print, "ppp_out(n=%u,data=", len);
245 for (size_t i = 0; i < len; ++i) {
246 mp_printf(&mp_plat_print, "%02x:", ((const uint8_t *)data)[i]);
247 }
248 mp_printf(&mp_plat_print, ")\n");
249 #endif
Daniël van de Giessen77406b42024-02-28 12:35:37 +0100250 mp_obj_t stream = self->stream;
251 if (stream == mp_const_none) {
252 return 0;
253 }
Damien Georgebc952d32024-03-13 17:09:51 +1100254 int err;
255 // The return value from this output callback is the number of bytes written out.
256 // If it's less than the requested number of bytes then lwIP will propagate out an error.
Daniël van de Giessen77406b42024-02-28 12:35:37 +0100257 return mp_stream_rw(stream, (void *)data, len, &err, MP_STREAM_RW_WRITE);
Damien Georgebc952d32024-03-13 17:09:51 +1100258}
259
260static mp_obj_t network_ppp_connect(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
261 enum { ARG_security, ARG_user, ARG_key };
262 static const mp_arg_t allowed_args[] = {
263 { MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PPPAUTHTYPE_NONE} },
264 { MP_QSTR_user, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
265 { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
266 };
267
268 mp_arg_val_t parsed_args[MP_ARRAY_SIZE(allowed_args)];
269 mp_arg_parse_all(n_args - 1, args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, parsed_args);
270
271 network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
272
273 if (self->state == STATE_INACTIVE) {
274 self->pcb = pppos_create(&self->netif, network_ppp_output_callback, network_ppp_status_cb, self);
275 if (self->pcb == NULL) {
276 mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("pppos_create failed"));
277 }
278 self->state = STATE_ACTIVE;
279
Daniël van de Giessen77406b42024-02-28 12:35:37 +0100280 network_ppp_stream_uart_irq_enable(self);
Damien Georgebc952d32024-03-13 17:09:51 +1100281 }
282
283 if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) {
284 mp_raise_OSError(MP_EALREADY);
285 }
286
287 switch (parsed_args[ARG_security].u_int) {
288 case PPPAUTHTYPE_NONE:
289 case PPPAUTHTYPE_PAP:
290 case PPPAUTHTYPE_CHAP:
291 break;
292 default:
293 mp_raise_ValueError(MP_ERROR_TEXT("invalid auth"));
294 }
295
296 if (parsed_args[ARG_security].u_int != PPPAUTHTYPE_NONE) {
297 const char *user_str = mp_obj_str_get_str(parsed_args[ARG_user].u_obj);
298 const char *key_str = mp_obj_str_get_str(parsed_args[ARG_key].u_obj);
299 ppp_set_auth(self->pcb, parsed_args[ARG_security].u_int, user_str, key_str);
300 }
301
Daniël van de Giessen3b1e22c2024-05-27 14:25:23 +0200302 ppp_set_default(self->pcb);
303
Damien Georgebc952d32024-03-13 17:09:51 +1100304 ppp_set_usepeerdns(self->pcb, true);
305
306 if (ppp_connect(self->pcb, 0) != ERR_OK) {
307 mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ppp_connect failed"));
308 }
309
310 self->state = STATE_CONNECTING;
311
312 // Do a poll in case there is data waiting on the input stream.
313 network_ppp_poll(1, args);
314
315 return mp_const_none;
316}
317static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_connect_obj, 1, network_ppp_connect);
318
319static mp_obj_t network_ppp_disconnect(mp_obj_t self_in) {
320 network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in);
321 if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) {
322 // Initiate close and wait for PPPERR_USER callback.
323 ppp_close(self->pcb, 0);
324 }
325 return mp_const_none;
326}
327static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_disconnect_obj, network_ppp_disconnect);
328
329static mp_obj_t network_ppp_isconnected(mp_obj_t self_in) {
330 network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in);
331 return mp_obj_new_bool(self->state == STATE_CONNECTED);
332}
333static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_isconnected_obj, network_ppp_isconnected);
334
335static mp_obj_t network_ppp_ifconfig(size_t n_args, const mp_obj_t *args) {
336 network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
337 return mod_network_nic_ifconfig(&self->netif, n_args - 1, args + 1);
338}
339static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_ifconfig_obj, 1, 2, network_ppp_ifconfig);
340
341static mp_obj_t network_ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
342 network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]);
343 return mod_network_nic_ipconfig(&self->netif, n_args - 1, args + 1, kwargs);
344}
345static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_ipconfig_obj, 1, network_ppp_ipconfig);
346
347static const mp_rom_map_elem_t network_ppp_locals_dict_table[] = {
348 { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&network_ppp___del___obj) },
349 { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_ppp_config_obj) },
350 { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_ppp_status_obj) },
351 { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&network_ppp_connect_obj) },
352 { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&network_ppp_disconnect_obj) },
353 { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_ppp_isconnected_obj) },
354 { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_ppp_ifconfig_obj) },
355 { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&network_ppp_ipconfig_obj) },
356 { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&network_ppp_poll_obj) },
357
358 { MP_ROM_QSTR(MP_QSTR_SEC_NONE), MP_ROM_INT(PPPAUTHTYPE_NONE) },
359 { MP_ROM_QSTR(MP_QSTR_SEC_PAP), MP_ROM_INT(PPPAUTHTYPE_PAP) },
360 { MP_ROM_QSTR(MP_QSTR_SEC_CHAP), MP_ROM_INT(PPPAUTHTYPE_CHAP) },
361};
362static MP_DEFINE_CONST_DICT(network_ppp_locals_dict, network_ppp_locals_dict_table);
363
364MP_DEFINE_CONST_OBJ_TYPE(
365 mp_network_ppp_lwip_type,
366 MP_QSTR_PPP,
367 MP_TYPE_FLAG_NONE,
368 make_new, network_ppp_make_new,
369 locals_dict, &network_ppp_locals_dict
370 );
371
372#endif