freedos-micro-python 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- freedos_micro_python/__init__.py +8 -0
- freedos_micro_python/cli.py +106 -0
- freedos_micro_python/gen_qstrdefs.py +275 -0
- freedos_micro_python/port/arch/bpstruct.h +2 -0
- freedos_micro_python/port/arch/cc.h +4 -0
- freedos_micro_python/port/arch/epstruct.h +1 -0
- freedos_micro_python/port/base64_uc386dos.c +164 -0
- freedos_micro_python/port/file_uc386dos.c +228 -0
- freedos_micro_python/port/lib/axtls/crypto/crypto.h +45 -0
- freedos_micro_python/port/lwip-arch-cc.h +46 -0
- freedos_micro_python/port/lwip_uc386dos.c +248 -0
- freedos_micro_python/port/lwipopts.h +117 -0
- freedos_micro_python/port/math_gamma.c +63 -0
- freedos_micro_python/port/modtime_uc386dos.c +60 -0
- freedos_micro_python/port/modtls_axtls_uc386dos.c +461 -0
- freedos_micro_python/port/mpconfigport.h +358 -0
- freedos_micro_python/port/mphal_uc386dos.c +103 -0
- freedos_micro_python/port/mphalport.h +11 -0
- freedos_micro_python/port/os_uc386dos.c +264 -0
- freedos_micro_python/port/path_uc386dos.c +307 -0
- freedos_micro_python/port/pktdrv_uc386dos.c +650 -0
- freedos_micro_python/port/qstrdefsport.h +2 -0
- freedos_micro_python/port/shutil_uc386dos.c +111 -0
- freedos_micro_python/port/tempfile_uc386dos.c +129 -0
- freedos_micro_python/port/time_real_uc386dos.c +77 -0
- freedos_micro_python/port/uc386_net_uc386dos.c +126 -0
- freedos_micro_python/port/urllib_parse_uc386dos.c +360 -0
- freedos_micro_python/port/urllib_uc386dos.c +29 -0
- freedos_micro_python/scripts/build.sh +641 -0
- freedos_micro_python/scripts/build_port.sh +241 -0
- freedos_micro_python/scripts/fetch.sh +238 -0
- freedos_micro_python-0.1.0.dist-info/METADATA +131 -0
- freedos_micro_python-0.1.0.dist-info/RECORD +37 -0
- freedos_micro_python-0.1.0.dist-info/WHEEL +5 -0
- freedos_micro_python-0.1.0.dist-info/entry_points.txt +2 -0
- freedos_micro_python-0.1.0.dist-info/licenses/LICENSE +25 -0
- freedos_micro_python-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// uc386-dos MicroPython file I/O — port-supplied `open()` /
|
|
2
|
+
// `mp_import_stat()` / `mp_lexer_new_from_file()` backed by uc386's
|
|
3
|
+
// libc INT 21h file syscalls. Mirrors the shape of
|
|
4
|
+
// `extmod/vfs_posix_file.c` but without the VFS plumbing — we
|
|
5
|
+
// implement the file-object type directly and define
|
|
6
|
+
// `mp_builtin_open_obj` as the user-visible entry point.
|
|
7
|
+
//
|
|
8
|
+
// `open()` modes:
|
|
9
|
+
// "r" read-only (default)
|
|
10
|
+
// "w" write, truncate, create
|
|
11
|
+
// "a" write, append, create
|
|
12
|
+
// "+" read-write
|
|
13
|
+
// "b" binary (FileIO; default if no "t")
|
|
14
|
+
// "t" text (TextIOWrapper, but we do bytes ↔ str)
|
|
15
|
+
|
|
16
|
+
#include <fcntl.h>
|
|
17
|
+
#include <unistd.h>
|
|
18
|
+
#include <sys/stat.h>
|
|
19
|
+
#include <string.h>
|
|
20
|
+
#include <errno.h>
|
|
21
|
+
|
|
22
|
+
#include "py/builtin.h"
|
|
23
|
+
#include "py/lexer.h"
|
|
24
|
+
#include "py/mperrno.h"
|
|
25
|
+
#include "py/mphal.h"
|
|
26
|
+
#include "py/runtime.h"
|
|
27
|
+
#include "py/stream.h"
|
|
28
|
+
#include "py/objstr.h"
|
|
29
|
+
|
|
30
|
+
typedef struct _mp_obj_uc386dos_file_t {
|
|
31
|
+
mp_obj_base_t base;
|
|
32
|
+
int fd; // -1 = closed
|
|
33
|
+
} mp_obj_uc386dos_file_t;
|
|
34
|
+
|
|
35
|
+
extern const mp_obj_type_t mp_type_uc386dos_textio;
|
|
36
|
+
extern const mp_obj_type_t mp_type_uc386dos_fileio;
|
|
37
|
+
|
|
38
|
+
// `mp_lexer_new_from_file` — read entire file into a buffer and feed
|
|
39
|
+
// to the str-len lexer. Used by `import xxx` to load `xxx.py`.
|
|
40
|
+
mp_lexer_t *mp_lexer_new_from_file(qstr filename) {
|
|
41
|
+
const char *fname = qstr_str(filename);
|
|
42
|
+
int fd = open(fname, O_RDONLY);
|
|
43
|
+
if (fd < 0) {
|
|
44
|
+
mp_raise_OSError(MP_ENOENT);
|
|
45
|
+
}
|
|
46
|
+
// Stat to get the file size. Avoids a grow-the-buffer loop.
|
|
47
|
+
struct stat st;
|
|
48
|
+
if (fstat(fd, &st) < 0) {
|
|
49
|
+
close(fd);
|
|
50
|
+
mp_raise_OSError(MP_EIO);
|
|
51
|
+
}
|
|
52
|
+
size_t size = (size_t)st.st_size;
|
|
53
|
+
char *buf = m_new(char, size + 1);
|
|
54
|
+
size_t got = 0;
|
|
55
|
+
while (got < size) {
|
|
56
|
+
int n = read(fd, buf + got, size - got);
|
|
57
|
+
if (n <= 0) {
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
got += (size_t)n;
|
|
61
|
+
}
|
|
62
|
+
close(fd);
|
|
63
|
+
buf[got] = '\0';
|
|
64
|
+
return mp_lexer_new_from_str_len(filename, buf, got, 0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// `mp_import_stat` — does `path` resolve to a file, dir, or
|
|
68
|
+
// nothing? `import xxx` walks `sys.path` calling this for each
|
|
69
|
+
// candidate path.
|
|
70
|
+
mp_import_stat_t mp_import_stat(const char *path) {
|
|
71
|
+
struct stat st;
|
|
72
|
+
if (stat(path, &st) != 0) {
|
|
73
|
+
return MP_IMPORT_STAT_NO_EXIST;
|
|
74
|
+
}
|
|
75
|
+
if (S_ISDIR(st.st_mode)) {
|
|
76
|
+
return MP_IMPORT_STAT_DIR;
|
|
77
|
+
}
|
|
78
|
+
return MP_IMPORT_STAT_FILE;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// File-object methods.
|
|
82
|
+
static void uc386dos_file_print(const mp_print_t *print, mp_obj_t self_in,
|
|
83
|
+
mp_print_kind_t kind) {
|
|
84
|
+
(void)kind;
|
|
85
|
+
mp_obj_uc386dos_file_t *self = MP_OBJ_TO_PTR(self_in);
|
|
86
|
+
mp_printf(print, "<io.%s %d>", mp_obj_get_type_str(self_in), self->fd);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
static mp_uint_t uc386dos_file_read(mp_obj_t o_in, void *buf, mp_uint_t size,
|
|
90
|
+
int *errcode) {
|
|
91
|
+
mp_obj_uc386dos_file_t *o = MP_OBJ_TO_PTR(o_in);
|
|
92
|
+
if (o->fd < 0) {
|
|
93
|
+
*errcode = MP_EBADF;
|
|
94
|
+
return MP_STREAM_ERROR;
|
|
95
|
+
}
|
|
96
|
+
int r = read(o->fd, buf, size);
|
|
97
|
+
if (r < 0) {
|
|
98
|
+
*errcode = MP_EIO;
|
|
99
|
+
return MP_STREAM_ERROR;
|
|
100
|
+
}
|
|
101
|
+
return (mp_uint_t)r;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static mp_uint_t uc386dos_file_write(mp_obj_t o_in, const void *buf,
|
|
105
|
+
mp_uint_t size, int *errcode) {
|
|
106
|
+
mp_obj_uc386dos_file_t *o = MP_OBJ_TO_PTR(o_in);
|
|
107
|
+
if (o->fd < 0) {
|
|
108
|
+
*errcode = MP_EBADF;
|
|
109
|
+
return MP_STREAM_ERROR;
|
|
110
|
+
}
|
|
111
|
+
int r = write(o->fd, buf, size);
|
|
112
|
+
if (r < 0) {
|
|
113
|
+
*errcode = MP_EIO;
|
|
114
|
+
return MP_STREAM_ERROR;
|
|
115
|
+
}
|
|
116
|
+
return (mp_uint_t)r;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static mp_uint_t uc386dos_file_ioctl(mp_obj_t o_in, mp_uint_t request,
|
|
120
|
+
uintptr_t arg, int *errcode) {
|
|
121
|
+
mp_obj_uc386dos_file_t *o = MP_OBJ_TO_PTR(o_in);
|
|
122
|
+
if (request == MP_STREAM_SEEK) {
|
|
123
|
+
struct mp_stream_seek_t *s = (struct mp_stream_seek_t *)(uintptr_t)arg;
|
|
124
|
+
if (o->fd < 0) {
|
|
125
|
+
*errcode = MP_EBADF;
|
|
126
|
+
return MP_STREAM_ERROR;
|
|
127
|
+
}
|
|
128
|
+
int whence = (s->whence == 0) ? SEEK_SET
|
|
129
|
+
: (s->whence == 1) ? SEEK_CUR
|
|
130
|
+
: SEEK_END;
|
|
131
|
+
long pos = lseek(o->fd, (long)s->offset, whence);
|
|
132
|
+
if (pos < 0) {
|
|
133
|
+
*errcode = MP_EIO;
|
|
134
|
+
return MP_STREAM_ERROR;
|
|
135
|
+
}
|
|
136
|
+
s->offset = (mp_off_t)pos;
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
if (request == MP_STREAM_FLUSH) {
|
|
140
|
+
// No-op: DOS write() is unbuffered at the libc layer.
|
|
141
|
+
return 0;
|
|
142
|
+
}
|
|
143
|
+
if (request == MP_STREAM_CLOSE) {
|
|
144
|
+
if (o->fd >= 0) {
|
|
145
|
+
close(o->fd);
|
|
146
|
+
o->fd = -1;
|
|
147
|
+
}
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
*errcode = MP_EINVAL;
|
|
151
|
+
return MP_STREAM_ERROR;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static const mp_rom_map_elem_t uc386dos_file_locals_dict_table[] = {
|
|
155
|
+
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
|
|
156
|
+
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
|
|
157
|
+
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
|
|
158
|
+
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
|
|
159
|
+
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
|
|
160
|
+
{ MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) },
|
|
161
|
+
{ MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) },
|
|
162
|
+
{ MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) },
|
|
163
|
+
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
|
|
164
|
+
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&mp_stream___exit___obj) },
|
|
165
|
+
};
|
|
166
|
+
static MP_DEFINE_CONST_DICT(uc386dos_file_locals_dict, uc386dos_file_locals_dict_table);
|
|
167
|
+
|
|
168
|
+
static const mp_stream_p_t uc386dos_fileio_stream_p = {
|
|
169
|
+
.read = uc386dos_file_read,
|
|
170
|
+
.write = uc386dos_file_write,
|
|
171
|
+
.ioctl = uc386dos_file_ioctl,
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
MP_DEFINE_CONST_OBJ_TYPE(
|
|
175
|
+
mp_type_uc386dos_fileio, MP_QSTR_FileIO,
|
|
176
|
+
MP_TYPE_FLAG_NONE,
|
|
177
|
+
print, uc386dos_file_print,
|
|
178
|
+
protocol, &uc386dos_fileio_stream_p,
|
|
179
|
+
locals_dict, &uc386dos_file_locals_dict
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
static const mp_stream_p_t uc386dos_textio_stream_p = {
|
|
183
|
+
.read = uc386dos_file_read,
|
|
184
|
+
.write = uc386dos_file_write,
|
|
185
|
+
.ioctl = uc386dos_file_ioctl,
|
|
186
|
+
.is_text = true,
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
MP_DEFINE_CONST_OBJ_TYPE(
|
|
190
|
+
mp_type_uc386dos_textio, MP_QSTR_TextIOWrapper,
|
|
191
|
+
MP_TYPE_FLAG_NONE,
|
|
192
|
+
print, uc386dos_file_print,
|
|
193
|
+
protocol, &uc386dos_textio_stream_p,
|
|
194
|
+
locals_dict, &uc386dos_file_locals_dict
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
// `open(filename, mode="r")` — port-supplied entry point bound to
|
|
198
|
+
// the `MP_QSTR_open` builtin via `mp_builtin_open_obj` below.
|
|
199
|
+
static mp_obj_t uc386dos_builtin_open(size_t n_args, const mp_obj_t *args,
|
|
200
|
+
mp_map_t *kwargs) {
|
|
201
|
+
(void)kwargs;
|
|
202
|
+
const char *fname = mp_obj_str_get_str(args[0]);
|
|
203
|
+
const char *mode_s = "r";
|
|
204
|
+
if (n_args >= 2) {
|
|
205
|
+
mode_s = mp_obj_str_get_str(args[1]);
|
|
206
|
+
}
|
|
207
|
+
int mode_rw = O_RDONLY;
|
|
208
|
+
int mode_x = 0;
|
|
209
|
+
const mp_obj_type_t *type = &mp_type_uc386dos_textio;
|
|
210
|
+
while (*mode_s) {
|
|
211
|
+
switch (*mode_s++) {
|
|
212
|
+
case 'r': mode_rw = O_RDONLY; break;
|
|
213
|
+
case 'w': mode_rw = O_WRONLY; mode_x = O_CREAT | O_TRUNC; break;
|
|
214
|
+
case 'a': mode_rw = O_WRONLY; mode_x = O_CREAT | O_APPEND; break;
|
|
215
|
+
case '+': mode_rw = O_RDWR; break;
|
|
216
|
+
case 'b': type = &mp_type_uc386dos_fileio; break;
|
|
217
|
+
case 't': type = &mp_type_uc386dos_textio; break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
int fd = open(fname, mode_rw | mode_x, 0644);
|
|
221
|
+
if (fd < 0) {
|
|
222
|
+
mp_raise_OSError(MP_ENOENT);
|
|
223
|
+
}
|
|
224
|
+
mp_obj_uc386dos_file_t *f = mp_obj_malloc(mp_obj_uc386dos_file_t, type);
|
|
225
|
+
f->fd = fd;
|
|
226
|
+
return MP_OBJ_FROM_PTR(f);
|
|
227
|
+
}
|
|
228
|
+
MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, uc386dos_builtin_open);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// uc386-dos shim header that mimics the subset of axtls's crypto.h
|
|
2
|
+
// used by upstream/extmod/modhashlib.c (when MICROPY_SSL_AXTLS=1).
|
|
3
|
+
//
|
|
4
|
+
// We don't actually link axtls — instead we route the AXTLS-shaped
|
|
5
|
+
// API to Brad Conte's public-domain reference implementations
|
|
6
|
+
// (`upstream/lib/crypto-algorithms/{md5,sha1}.[ch]`, fetched
|
|
7
|
+
// alongside the existing sha256 sources). The B-Con API is nearly
|
|
8
|
+
// identical: same MD5_CTX / SHA1_CTX typedef names, same Update
|
|
9
|
+
// argument order. Only `*_Final` differs — axtls takes
|
|
10
|
+
// `(digest, ctx)` while B-Con takes `(ctx, digest)` — so we wrap.
|
|
11
|
+
//
|
|
12
|
+
// Setting MICROPY_SSL_AXTLS=1 in mpconfigport.h tells modhashlib.c
|
|
13
|
+
// to take the AXTLS branch of its #if cascade. We don't enable the
|
|
14
|
+
// rest of axtls (TLS, RSA, AES, ...); the single include below is
|
|
15
|
+
// sufficient for hashlib.md5 and hashlib.sha1.
|
|
16
|
+
#ifndef UCDOS_AXTLS_CRYPTO_SHIM_H
|
|
17
|
+
#define UCDOS_AXTLS_CRYPTO_SHIM_H
|
|
18
|
+
|
|
19
|
+
#include "lib/crypto-algorithms/md5.h"
|
|
20
|
+
#include "lib/crypto-algorithms/sha1.h"
|
|
21
|
+
|
|
22
|
+
#define MD5_SIZE 16
|
|
23
|
+
#define SHA1_SIZE 20
|
|
24
|
+
|
|
25
|
+
static inline void MD5_Init(MD5_CTX *ctx) {
|
|
26
|
+
md5_init(ctx);
|
|
27
|
+
}
|
|
28
|
+
static inline void MD5_Update(MD5_CTX *ctx, const unsigned char *msg, int len) {
|
|
29
|
+
md5_update(ctx, msg, (size_t)len);
|
|
30
|
+
}
|
|
31
|
+
static inline void MD5_Final(unsigned char *digest, MD5_CTX *ctx) {
|
|
32
|
+
md5_final(ctx, digest);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static inline void SHA1_Init(SHA1_CTX *ctx) {
|
|
36
|
+
sha1_init(ctx);
|
|
37
|
+
}
|
|
38
|
+
static inline void SHA1_Update(SHA1_CTX *ctx, const unsigned char *msg, int len) {
|
|
39
|
+
sha1_update(ctx, msg, (size_t)len);
|
|
40
|
+
}
|
|
41
|
+
static inline void SHA1_Final(unsigned char *digest, SHA1_CTX *ctx) {
|
|
42
|
+
sha1_final(ctx, digest);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#endif // UCDOS_AXTLS_CRYPTO_SHIM_H
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// uc386-dos lwIP `arch/cc.h` shim. Included via `-I` rather than
|
|
2
|
+
// the conventional `arch/cc.h` path because uc386's preprocessor
|
|
3
|
+
// doesn't have a per-port arch include directory convention.
|
|
4
|
+
// build_port.sh adds `uc386-dos/` to the include path AND aliases
|
|
5
|
+
// the conventional name via a stub at uc386-dos/arch/cc.h that
|
|
6
|
+
// just `#include`s this file.
|
|
7
|
+
|
|
8
|
+
#ifndef LWIP_ARCH_CC_H
|
|
9
|
+
#define LWIP_ARCH_CC_H
|
|
10
|
+
|
|
11
|
+
#include <stdint.h>
|
|
12
|
+
#include <stddef.h>
|
|
13
|
+
#include <stdlib.h>
|
|
14
|
+
|
|
15
|
+
// Endianness — i386 is little-endian.
|
|
16
|
+
#define BYTE_ORDER LITTLE_ENDIAN
|
|
17
|
+
#ifndef LITTLE_ENDIAN
|
|
18
|
+
#define LITTLE_ENDIAN 1234
|
|
19
|
+
#define BIG_ENDIAN 4321
|
|
20
|
+
#endif
|
|
21
|
+
|
|
22
|
+
// Packed structs — uc386 honors `__attribute__((packed))` (member
|
|
23
|
+
// byte-alignment, struct alignment 1). lwIP's init.c self-tests the
|
|
24
|
+
// layout via `sizeof(struct { u8; u32; } __packed) == 5` at boot;
|
|
25
|
+
// without real packing this fails because u32 lands at offset 4.
|
|
26
|
+
// The other PACK_STRUCT_* slots (BEGIN/END/FIELD) remain empty —
|
|
27
|
+
// upstream's lwip/arch.h defines them that way for GCC/clang.
|
|
28
|
+
#define PACK_STRUCT_FIELD(x) x
|
|
29
|
+
#define PACK_STRUCT_STRUCT __attribute__((packed))
|
|
30
|
+
#define PACK_STRUCT_BEGIN
|
|
31
|
+
#define PACK_STRUCT_END
|
|
32
|
+
|
|
33
|
+
// Diagnostics — route lwIP's debug printf through the regular
|
|
34
|
+
// printf chain. LWIP_DEBUG=0 in lwipopts.h makes these no-ops in
|
|
35
|
+
// the common case.
|
|
36
|
+
#include <stdio.h>
|
|
37
|
+
#define LWIP_PLATFORM_DIAG(x) do { printf x; } while (0)
|
|
38
|
+
|
|
39
|
+
// sys_prot_t — a critical-section level. We're NO_SYS=1 with no
|
|
40
|
+
// real interrupts to mask, so use a stub type and let lwipopts.h's
|
|
41
|
+
// SYS_ARCH_* macros expand to nothing.
|
|
42
|
+
typedef uint32_t sys_prot_t;
|
|
43
|
+
|
|
44
|
+
// strerror — uc386's libc has it.
|
|
45
|
+
|
|
46
|
+
#endif // LWIP_ARCH_CC_H
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// uc386-dos lwIP port glue. Provides:
|
|
2
|
+
// - sys_now() — millisecond tick from BIOS (INT 1Ah AH=0).
|
|
3
|
+
// - lwip_uc386dos_init() — calls lwip_init() once + adds the
|
|
4
|
+
// loopback netif. Called from main.c at boot.
|
|
5
|
+
// - mp_module_socket — re-export of lwIP's mp_module_lwip under
|
|
6
|
+
// `MP_QSTR_socket` so `import socket` lights up the BSD-style
|
|
7
|
+
// surface modlwip.c provides.
|
|
8
|
+
//
|
|
9
|
+
// We don't ship modnetwork.c — that's the higher-level network
|
|
10
|
+
// configuration interface used by ports with WLAN/Ethernet drivers.
|
|
11
|
+
// For loopback-only Phase 1 testing, the raw socket surface
|
|
12
|
+
// (modlwip.c → MP_QSTR_socket) is sufficient.
|
|
13
|
+
|
|
14
|
+
#include <string.h>
|
|
15
|
+
|
|
16
|
+
#include "py/runtime.h"
|
|
17
|
+
#include "lwip/init.h"
|
|
18
|
+
#include "lwip/timeouts.h"
|
|
19
|
+
#include "lwip/netif.h"
|
|
20
|
+
#include "lwip/dhcp.h"
|
|
21
|
+
#include "lwip/etharp.h"
|
|
22
|
+
#include "lwip/pbuf.h"
|
|
23
|
+
#include "netif/ethernet.h"
|
|
24
|
+
#if LWIP_HAVE_LOOPIF
|
|
25
|
+
#include "lwip/sys.h"
|
|
26
|
+
#endif
|
|
27
|
+
|
|
28
|
+
// INT 0x83 packet-driver shim — see lib/i386_dos_libc.asm. dos_emu
|
|
29
|
+
// intercepts and routes to a NetworkSimulator (sim mode).
|
|
30
|
+
extern int ethdrv_init(unsigned char mac[6]);
|
|
31
|
+
extern int ethdrv_send(const unsigned char *buf, unsigned int len);
|
|
32
|
+
extern unsigned int ethdrv_recv(unsigned char *buf, unsigned int maxlen);
|
|
33
|
+
|
|
34
|
+
// Crynwr packet-driver bindings (real-DOS path) — pktdrv_uc386dos.c.
|
|
35
|
+
// Returns 0 on success when a "PKT DRVR" signature is found in the
|
|
36
|
+
// IVT 0x60–0x7F range; otherwise we fall back to the INT 0x83 sim.
|
|
37
|
+
extern int pktdrv_init(unsigned char mac[6]);
|
|
38
|
+
extern int pktdrv_send(const unsigned char *buf, unsigned int len);
|
|
39
|
+
extern unsigned int pktdrv_recv(unsigned char *buf, unsigned int maxlen);
|
|
40
|
+
extern int pktdrv_is_active(void);
|
|
41
|
+
|
|
42
|
+
// `bios_ticks()` from lib/i386_dos_libc.asm — INT 1Ah AH=0 read of
|
|
43
|
+
// the BIOS tick counter (~18.2 Hz). Multiply by 55 for an
|
|
44
|
+
// approximate millisecond clock; lwIP's timeouts only need
|
|
45
|
+
// monotonic ms, not absolute wall time, so the small drift is fine.
|
|
46
|
+
extern unsigned bios_ticks(void);
|
|
47
|
+
|
|
48
|
+
uint32_t sys_now(void) {
|
|
49
|
+
return bios_ticks() * 55u;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#if LWIP_HAVE_LOOPIF
|
|
53
|
+
// One-shot lwIP init. Call from `main()` after mp_init.
|
|
54
|
+
// Loopback netif is auto-added by lwip_init() when LWIP_HAVE_LOOPIF
|
|
55
|
+
// is on, so we just call lwip_init() and we're done.
|
|
56
|
+
void lwip_uc386dos_init(void) {
|
|
57
|
+
lwip_init();
|
|
58
|
+
}
|
|
59
|
+
#endif
|
|
60
|
+
|
|
61
|
+
// Drive periodic timer checks. Call from the REPL idle loop or a
|
|
62
|
+
// dedicated tick. modlwip.c expects sys_check_timeouts() to be
|
|
63
|
+
// pumped regularly so TCP retransmits / DNS timeouts fire.
|
|
64
|
+
void lwip_uc386dos_poll(void) {
|
|
65
|
+
sys_check_timeouts();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Stub for `mp_mod_network_prefer_dns_use_ip_version` — modlwip.c
|
|
69
|
+
// reads it inside getaddrinfo() to pick AF_INET vs AF_INET6 ordering.
|
|
70
|
+
// modnetwork.c (which we don't compile) defines it as a real global;
|
|
71
|
+
// we ship the stub here defaulting to IPv4 preferred. If we ever
|
|
72
|
+
// turn LWIP_IPV6 on, switch this to 6 or wire modnetwork properly.
|
|
73
|
+
int mp_mod_network_prefer_dns_use_ip_version = 4;
|
|
74
|
+
|
|
75
|
+
// modlwip.c also references the hostname buffer that modnetwork.c
|
|
76
|
+
// otherwise owns. Provide a stub matching the symbol shape.
|
|
77
|
+
char mod_network_hostname_data[16 + 1] = "uc386-dos";
|
|
78
|
+
|
|
79
|
+
// ---- Virtual ethernet netif (uc386dos_eth) ---------------------
|
|
80
|
+
//
|
|
81
|
+
// A second netif on top of the INT 0x83 packet-driver shim. Used
|
|
82
|
+
// for DHCP discovery and any non-loopback IPv4 traffic the program
|
|
83
|
+
// wants. Stays separate from the loopback netif so 127.0.0.1 traffic
|
|
84
|
+
// doesn't bounce off the wire.
|
|
85
|
+
//
|
|
86
|
+
// Frame path:
|
|
87
|
+
// TX: lwIP → etharp_output → ethdrv_send → INT 0x83 AH=1
|
|
88
|
+
// → dos_emu hook
|
|
89
|
+
// → NetworkSimulator
|
|
90
|
+
// RX: ethdrv_recv (INT 0x83 AH=2) → pbuf_alloc + take
|
|
91
|
+
// → ethernet_input → ip_input
|
|
92
|
+
// Pumped from `uc386dos_loopback_poll` (which is wired in as
|
|
93
|
+
// modlwip's poll-list hook), so `lwip.callback()` drives both
|
|
94
|
+
// loopback delivery and eth RX in one shot.
|
|
95
|
+
|
|
96
|
+
static struct netif uc386dos_eth_netif;
|
|
97
|
+
static int uc386dos_eth_active = 0;
|
|
98
|
+
|
|
99
|
+
// Static TX scratch — one outgoing frame at a time, no concurrency
|
|
100
|
+
// since NO_SYS=1 + single-threaded REPL. Sized to lwIP MTU + eth hdr.
|
|
101
|
+
static unsigned char uc386dos_eth_tx_buf[1500 + 14];
|
|
102
|
+
static unsigned char uc386dos_eth_rx_buf[1500 + 14];
|
|
103
|
+
|
|
104
|
+
static err_t uc386dos_eth_linkoutput(struct netif *netif, struct pbuf *p) {
|
|
105
|
+
(void)netif;
|
|
106
|
+
if (p->tot_len > sizeof(uc386dos_eth_tx_buf)) {
|
|
107
|
+
return ERR_IF;
|
|
108
|
+
}
|
|
109
|
+
pbuf_copy_partial(p, uc386dos_eth_tx_buf, p->tot_len, 0);
|
|
110
|
+
int rc;
|
|
111
|
+
if (pktdrv_is_active()) {
|
|
112
|
+
rc = pktdrv_send(uc386dos_eth_tx_buf, p->tot_len);
|
|
113
|
+
} else {
|
|
114
|
+
rc = ethdrv_send(uc386dos_eth_tx_buf, p->tot_len);
|
|
115
|
+
}
|
|
116
|
+
return (rc != 0) ? ERR_IF : ERR_OK;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static err_t uc386dos_eth_netif_init_cb(struct netif *netif) {
|
|
120
|
+
netif->name[0] = 'e';
|
|
121
|
+
netif->name[1] = 'n';
|
|
122
|
+
netif->output = etharp_output;
|
|
123
|
+
netif->linkoutput = uc386dos_eth_linkoutput;
|
|
124
|
+
netif->mtu = 1500;
|
|
125
|
+
netif->hwaddr_len = 6;
|
|
126
|
+
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP
|
|
127
|
+
| NETIF_FLAG_ETHERNET;
|
|
128
|
+
// hwaddr is filled in by uc386dos_eth_start() before netif_add.
|
|
129
|
+
return ERR_OK;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Pull queued frames from the RX path and feed them into lwIP.
|
|
133
|
+
// When Crynwr is the active driver, drain `pktdrv_recv` — under
|
|
134
|
+
// dos_emu the harness fills the buffer through AH=0x99 polling
|
|
135
|
+
// registration; on real DOS this requires a DPMI INT 31h fn 0x0303
|
|
136
|
+
// trampoline (see pktdrv_uc386dos.c) that's still TODO. With INT
|
|
137
|
+
// 0x83 sim active, drain `ethdrv_recv` instead. The two never
|
|
138
|
+
// coexist in a single boot.
|
|
139
|
+
static void uc386dos_eth_pump_rx(void) {
|
|
140
|
+
if (!uc386dos_eth_active) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
for (;;) {
|
|
144
|
+
unsigned int len = pktdrv_is_active()
|
|
145
|
+
? pktdrv_recv(uc386dos_eth_rx_buf,
|
|
146
|
+
sizeof(uc386dos_eth_rx_buf))
|
|
147
|
+
: ethdrv_recv(uc386dos_eth_rx_buf,
|
|
148
|
+
sizeof(uc386dos_eth_rx_buf));
|
|
149
|
+
if (len == 0) {
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
struct pbuf *p = pbuf_alloc(PBUF_RAW, (u16_t)len, PBUF_POOL);
|
|
153
|
+
if (p == NULL) {
|
|
154
|
+
// Drop the frame; lwIP will time out and retransmit.
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
pbuf_take(p, uc386dos_eth_rx_buf, (u16_t)len);
|
|
158
|
+
if (uc386dos_eth_netif.input(p, &uc386dos_eth_netif) != ERR_OK) {
|
|
159
|
+
pbuf_free(p);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Bring the eth netif up. Calls INT 0x83 to fetch the host-assigned
|
|
165
|
+
// MAC, registers the netif, and (if dhcp_start_now) kicks off DHCP
|
|
166
|
+
// discovery. Idempotent — second call is a no-op.
|
|
167
|
+
int uc386dos_eth_start(int dhcp_start_now) {
|
|
168
|
+
if (uc386dos_eth_active) {
|
|
169
|
+
return 0;
|
|
170
|
+
}
|
|
171
|
+
unsigned char mac[6];
|
|
172
|
+
// Try a real Crynwr packet driver first (so the same binary
|
|
173
|
+
// lights up on hardware when one's loaded). Fall back to the
|
|
174
|
+
// INT 0x83 sim — what dos_emu provides without a fake Crynwr.
|
|
175
|
+
if (pktdrv_init(mac) != 0 && ethdrv_init(mac) != 0) {
|
|
176
|
+
return -1;
|
|
177
|
+
}
|
|
178
|
+
ip4_addr_t ip0, nm0, gw0;
|
|
179
|
+
ip0.addr = 0;
|
|
180
|
+
nm0.addr = 0;
|
|
181
|
+
gw0.addr = 0;
|
|
182
|
+
if (netif_add(&uc386dos_eth_netif, &ip0, &nm0, &gw0, NULL,
|
|
183
|
+
uc386dos_eth_netif_init_cb, ethernet_input) == NULL) {
|
|
184
|
+
return -2;
|
|
185
|
+
}
|
|
186
|
+
memcpy(uc386dos_eth_netif.hwaddr, mac, 6);
|
|
187
|
+
netif_set_default(&uc386dos_eth_netif);
|
|
188
|
+
netif_set_up(&uc386dos_eth_netif);
|
|
189
|
+
netif_set_link_up(&uc386dos_eth_netif);
|
|
190
|
+
uc386dos_eth_active = 1;
|
|
191
|
+
if (dhcp_start_now) {
|
|
192
|
+
if (dhcp_start(&uc386dos_eth_netif) != ERR_OK) {
|
|
193
|
+
return -3;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Static-IP override path for tests / environments where DHCP isn't
|
|
200
|
+
// available. Caller has already run uc386dos_eth_start(False) so the
|
|
201
|
+
// netif exists; this just clears any DHCP state and writes the
|
|
202
|
+
// addresses directly.
|
|
203
|
+
void uc386dos_eth_set_addr(unsigned int ip, unsigned int mask, unsigned int gw) {
|
|
204
|
+
if (!uc386dos_eth_active) return;
|
|
205
|
+
dhcp_release_and_stop(&uc386dos_eth_netif);
|
|
206
|
+
ip4_addr_t a, n, g;
|
|
207
|
+
a.addr = ip; n.addr = mask; g.addr = gw;
|
|
208
|
+
netif_set_addr(&uc386dos_eth_netif, &a, &n, &g);
|
|
209
|
+
netif_set_up(&uc386dos_eth_netif);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Read-back helpers for the Python-facing module to inspect state.
|
|
213
|
+
unsigned int uc386dos_eth_ip(void) { return uc386dos_eth_netif.ip_addr.addr; }
|
|
214
|
+
unsigned int uc386dos_eth_netmask(void) { return uc386dos_eth_netif.netmask.addr; }
|
|
215
|
+
unsigned int uc386dos_eth_gateway(void) { return uc386dos_eth_netif.gw.addr; }
|
|
216
|
+
int uc386dos_eth_is_up(void) { return uc386dos_eth_active && netif_is_up(&uc386dos_eth_netif); }
|
|
217
|
+
|
|
218
|
+
// 0 = no eth init yet, 1 = INT 0x83 sim, 2 = Crynwr packet driver.
|
|
219
|
+
int uc386dos_eth_driver(void) {
|
|
220
|
+
if (!uc386dos_eth_active) return 0;
|
|
221
|
+
return pktdrv_is_active() ? 2 : 1;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Loopback packet pump. With LWIP_NETIF_LOOPBACK=1 + NO_SYS=1, lwIP
|
|
225
|
+
// queues outgoing-to-127.0.0.1 packets in netif->loop_first/last and
|
|
226
|
+
// only delivers them when netif_poll(netif) is called. The loopback
|
|
227
|
+
// netif (`loop_netif`) is created by netif_init() but isn't promoted
|
|
228
|
+
// to `netif_default`, so use netif_poll_all() — it walks every netif
|
|
229
|
+
// on `netif_list` and is provided when LWIP_NETIF_LOOPBACK_MULTITHREADING
|
|
230
|
+
// is 0 (our case). Registered from mod_lwip_reset (patched in
|
|
231
|
+
// fetch.sh) so each `lwip.reset()` re-arms the loopback pump
|
|
232
|
+
// alongside the usual sys_check_timeouts() cadence. Also drains the
|
|
233
|
+
// virtual-eth RX queue so DHCP responses + ARP replies land.
|
|
234
|
+
void uc386dos_loopback_poll(void *arg) {
|
|
235
|
+
(void)arg;
|
|
236
|
+
extern int write(int fd, const void *buf, unsigned int n);
|
|
237
|
+
write(1, "[lp:np]", 7);
|
|
238
|
+
netif_poll_all();
|
|
239
|
+
write(1, "[lp:rx]", 7);
|
|
240
|
+
uc386dos_eth_pump_rx();
|
|
241
|
+
write(1, "[lp:done]", 9);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Expose `mp_module_lwip` as `socket` too. modlwip.c registers
|
|
245
|
+
// itself under MP_QSTR_lwip and MP_QSTR_socket via `MP_REGISTER_*`
|
|
246
|
+
// markers — but our hand-rolled moduledefs.h doesn't process those.
|
|
247
|
+
// The `socket` registration goes through UCDOS_MOD_ENTRY_SOCKET in
|
|
248
|
+
// build.sh's heredoc; we don't need any C-side glue for that.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// uc386-dos lwipopts.h — minimum-viable config for IPv4-only TCP/UDP
|
|
2
|
+
// with loopback testing. No threading (NO_SYS=1), no DHCP for now,
|
|
3
|
+
// no IPv6, no stats. Just enough surface so MICROPY_PY_LWIP=1 can
|
|
4
|
+
// expose `socket` and a Python program can `socket.socket()`,
|
|
5
|
+
// `bind`, `listen`, `connect`, `send`, `recv` over 127.0.0.1.
|
|
6
|
+
//
|
|
7
|
+
// Tuning will follow once we have real packet I/O wired up — DHCP
|
|
8
|
+
// + DNS + a real netif driver are deferred until the dos-emu fake
|
|
9
|
+
// packet driver (or real-DOS testing) lands.
|
|
10
|
+
|
|
11
|
+
#ifndef MICROPY_INCLUDED_LWIPOPTS_H
|
|
12
|
+
#define MICROPY_INCLUDED_LWIPOPTS_H
|
|
13
|
+
|
|
14
|
+
// MicroPython's modlwip.c uses the raw API (tcp_new etc.) on a
|
|
15
|
+
// single-threaded foreground loop. NO_SYS=1 disables the
|
|
16
|
+
// thread-aware netconn/socket API and the rtos abstractions —
|
|
17
|
+
// matches the rp2 / esp32-port shape for raw-mode lwip.
|
|
18
|
+
#define NO_SYS 1
|
|
19
|
+
#define LWIP_RAW 1
|
|
20
|
+
#define LWIP_NETCONN 0
|
|
21
|
+
#define LWIP_SOCKET 0
|
|
22
|
+
#define LWIP_NETIF_API 0
|
|
23
|
+
|
|
24
|
+
// Core protocols. ARP+IP for the link layer, ICMP+IGMP for control,
|
|
25
|
+
// TCP+UDP for transport, DNS for name resolution, AUTOIP for fallback
|
|
26
|
+
// addressing when DHCP isn't running.
|
|
27
|
+
#define LWIP_ARP 1
|
|
28
|
+
#define LWIP_ETHERNET 1
|
|
29
|
+
#define LWIP_IPV4 1
|
|
30
|
+
#define LWIP_IPV6 0
|
|
31
|
+
#define LWIP_ICMP 1
|
|
32
|
+
#define LWIP_IGMP 1
|
|
33
|
+
#define LWIP_TCP 1
|
|
34
|
+
#define LWIP_UDP 1
|
|
35
|
+
#define LWIP_DNS 1
|
|
36
|
+
#define LWIP_DHCP 1
|
|
37
|
+
#define LWIP_AUTOIP 0
|
|
38
|
+
#define LWIP_ACD 0
|
|
39
|
+
#define LWIP_DHCP_DOES_ACD_CHECK 0 // ACD off → don't probe
|
|
40
|
+
#define LWIP_DHCP_BOOTP_FILE 0
|
|
41
|
+
#define LWIP_DHCP_GET_NTP_SRV 0
|
|
42
|
+
#define LWIP_DHCP_PROVIDE_DNS_SERVERS 1 // accept DNS from option 6
|
|
43
|
+
// dhcp.c includes <stddef.h> via lwIP's def.h, then time.h via
|
|
44
|
+
// timeouts.h's chain — both typedef size_t under _SIZE_T_DEFINED.
|
|
45
|
+
// stddef.h's separate _PTRDIFF_T_DEFINED guard (fixed in lib/include)
|
|
46
|
+
// keeps ptrdiff_t reachable.
|
|
47
|
+
|
|
48
|
+
// Loopback — lets unit tests bind/connect 127.0.0.1 to itself
|
|
49
|
+
// without any real netif driver. Phase 1 testing relies entirely
|
|
50
|
+
// on this; phase 2 will add a packet-driver netif alongside.
|
|
51
|
+
#define LWIP_HAVE_LOOPIF 1
|
|
52
|
+
#define LWIP_NETIF_LOOPBACK 1
|
|
53
|
+
#define LWIP_LOOPBACK_MAX_PBUFS 8
|
|
54
|
+
|
|
55
|
+
// Memory. uc386's GC heap is 256 KB total — keep lwIP's footprint
|
|
56
|
+
// modest. These knobs roughly mirror the minimal-port config from
|
|
57
|
+
// upstream lwIP examples.
|
|
58
|
+
#define MEM_ALIGNMENT 4
|
|
59
|
+
#define MEM_SIZE 4096
|
|
60
|
+
#define MEMP_NUM_PBUF 16
|
|
61
|
+
#define MEMP_NUM_RAW_PCB 4
|
|
62
|
+
#define MEMP_NUM_UDP_PCB 4
|
|
63
|
+
#define MEMP_NUM_TCP_PCB 4
|
|
64
|
+
#define MEMP_NUM_TCP_PCB_LISTEN 4
|
|
65
|
+
#define MEMP_NUM_TCP_SEG 16
|
|
66
|
+
#define MEMP_NUM_REASSDATA 4
|
|
67
|
+
#define MEMP_NUM_FRAG_PBUF 4
|
|
68
|
+
#define MEMP_NUM_ARP_QUEUE 2
|
|
69
|
+
#define MEMP_NUM_IGMP_GROUP 2
|
|
70
|
+
#define MEMP_NUM_SYS_TIMEOUT 8
|
|
71
|
+
#define PBUF_POOL_SIZE 16
|
|
72
|
+
#define PBUF_POOL_BUFSIZE 256
|
|
73
|
+
#define TCP_MSS 536
|
|
74
|
+
#define TCP_WND (4 * TCP_MSS)
|
|
75
|
+
#define TCP_SND_BUF (4 * TCP_MSS)
|
|
76
|
+
#define TCP_SND_QUEUELEN (4 * (TCP_SND_BUF / TCP_MSS))
|
|
77
|
+
|
|
78
|
+
// Statistics off — saves a few KB.
|
|
79
|
+
#define LWIP_STATS 0
|
|
80
|
+
|
|
81
|
+
// LWIP_DEBUG is INTENTIONALLY left undefined here. err.h gates
|
|
82
|
+
// `extern const char *lwip_strerr(err_t)` on `#ifdef LWIP_DEBUG`,
|
|
83
|
+
// so even `#define LWIP_DEBUG 0` is enough to pull in the extern
|
|
84
|
+
// reference (which isn't satisfied because lwip_strerr's
|
|
85
|
+
// definition lives behind LWIP_DEBUG too). Leaving it undefined
|
|
86
|
+
// makes err.h fall through to `#define lwip_strerr(x) ""`.
|
|
87
|
+
|
|
88
|
+
// Critical-section primitives. NO_SYS=1 with no real interrupts
|
|
89
|
+
// to mask, so make these all no-ops. modlwip.c's tcpip thread
|
|
90
|
+
// abstraction uses both macro and function forms — provide both.
|
|
91
|
+
#define SYS_ARCH_DECL_PROTECT(lev) do { } while (0)
|
|
92
|
+
#define SYS_ARCH_PROTECT(lev) do { } while (0)
|
|
93
|
+
#define SYS_ARCH_UNPROTECT(lev) do { } while (0)
|
|
94
|
+
#define sys_arch_protect() ((sys_prot_t)0)
|
|
95
|
+
#define sys_arch_unprotect(lev) do { (void)(lev); } while (0)
|
|
96
|
+
|
|
97
|
+
// We don't have an OS to call. Print the assert message + abort()
|
|
98
|
+
// so the dos_emu test path surfaces which assertion tripped (the
|
|
99
|
+
// message + filename + line make it down to stdout before exit).
|
|
100
|
+
#include <stdio.h>
|
|
101
|
+
#define LWIP_PLATFORM_ASSERT(x) \
|
|
102
|
+
do { printf("lwIP assert: %s at %s:%d\n", (x), __FILE__, __LINE__); \
|
|
103
|
+
abort(); } while (0)
|
|
104
|
+
|
|
105
|
+
// Use the standard lwip_htons/lwip_htonl from def.c — uc386 doesn't
|
|
106
|
+
// ship a system byteorder.h.
|
|
107
|
+
// (Don't define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS — that path
|
|
108
|
+
// expects htons/htonl etc. to come from elsewhere.)
|
|
109
|
+
|
|
110
|
+
// Random — lwIP's DNS uses LWIP_RAND() for transaction IDs / port
|
|
111
|
+
// allocation. Route through libc's `random()` (LCG, seeded by
|
|
112
|
+
// MICROPY_PY_RANDOM_SEED_INIT_FUNC's bios_ticks() at boot). Not
|
|
113
|
+
// crypto-strength, but transaction-ID strength is fine.
|
|
114
|
+
extern long random(void);
|
|
115
|
+
#define LWIP_RAND() ((unsigned long)random())
|
|
116
|
+
|
|
117
|
+
#endif // MICROPY_INCLUDED_LWIPOPTS_H
|