Branch data Line data Source code
1 : : /*
2 : : * This file is part of the MicroPython project, http://micropython.org/
3 : : *
4 : : * The MIT License (MIT)
5 : : *
6 : : * Copyright (c) 2015 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 : :
27 : : #include <unistd.h>
28 : : #include <stdlib.h>
29 : : #include <string.h>
30 : : #include <time.h>
31 : : #include <sys/time.h>
32 : : #include <fcntl.h>
33 : :
34 : : #include "py/mphal.h"
35 : : #include "py/mpthread.h"
36 : : #include "py/runtime.h"
37 : : #include "extmod/misc.h"
38 : :
39 : : #if defined(__GLIBC__) && defined(__GLIBC_PREREQ)
40 : : #if __GLIBC_PREREQ(2, 25)
41 : : #include <sys/random.h>
42 : : #define _HAVE_GETRANDOM
43 : : #endif
44 : : #endif
45 : :
46 : : #ifndef _WIN32
47 : : #include <signal.h>
48 : :
49 : 0 : static void sighandler(int signum) {
50 [ # # ]: 0 : if (signum == SIGINT) {
51 : : #if MICROPY_ASYNC_KBD_INTR
52 : : #if MICROPY_PY_THREAD_GIL
53 : : // Since signals can occur at any time, we may not be holding the GIL when
54 : : // this callback is called, so it is not safe to raise an exception here
55 : : #error "MICROPY_ASYNC_KBD_INTR and MICROPY_PY_THREAD_GIL are not compatible"
56 : : #endif
57 : 0 : mp_obj_exception_clear_traceback(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)));
58 : 0 : sigset_t mask;
59 : 0 : sigemptyset(&mask);
60 : : // On entry to handler, its signal is blocked, and unblocked on
61 : : // normal exit. As we instead perform longjmp, unblock it manually.
62 : 0 : sigprocmask(SIG_SETMASK, &mask, NULL);
63 : 0 : nlr_raise(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)));
64 : : #else
65 : : if (MP_STATE_MAIN_THREAD(mp_pending_exception) == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))) {
66 : : // this is the second time we are called, so die straight away
67 : : exit(1);
68 : : }
69 : : mp_sched_keyboard_interrupt();
70 : : #endif
71 : : }
72 : 0 : }
73 : : #endif
74 : :
75 : 4496 : void mp_hal_set_interrupt_char(char c) {
76 : : // configure terminal settings to (not) let ctrl-C through
77 [ + + ]: 4496 : if (c == CHAR_CTRL_C) {
78 : : #ifndef _WIN32
79 : : // enable signal handler
80 : 2250 : struct sigaction sa;
81 : 2250 : sa.sa_flags = 0;
82 : 2250 : sa.sa_handler = sighandler;
83 : 2250 : sigemptyset(&sa.sa_mask);
84 : 2250 : sigaction(SIGINT, &sa, NULL);
85 : : #endif
86 : : } else {
87 : : #ifndef _WIN32
88 : : // disable signal handler
89 : 2246 : struct sigaction sa;
90 : 2246 : sa.sa_flags = 0;
91 : 2246 : sa.sa_handler = SIG_DFL;
92 : 2246 : sigemptyset(&sa.sa_mask);
93 : 2246 : sigaction(SIGINT, &sa, NULL);
94 : : #endif
95 : : }
96 : 4496 : }
97 : :
98 : : #if MICROPY_USE_READLINE == 1
99 : :
100 : : #include <termios.h>
101 : :
102 : : static struct termios orig_termios;
103 : :
104 : 239 : void mp_hal_stdio_mode_raw(void) {
105 : : // save and set terminal settings
106 : 239 : tcgetattr(0, &orig_termios);
107 : 239 : static struct termios termios;
108 : 239 : termios = orig_termios;
109 : 239 : termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
110 : 239 : termios.c_cflag = (termios.c_cflag & ~(CSIZE | PARENB)) | CS8;
111 : 239 : termios.c_lflag = 0;
112 : 239 : termios.c_cc[VMIN] = 1;
113 : 239 : termios.c_cc[VTIME] = 0;
114 : 239 : tcsetattr(0, TCSAFLUSH, &termios);
115 : 239 : }
116 : :
117 : 239 : void mp_hal_stdio_mode_orig(void) {
118 : : // restore terminal settings
119 : 239 : tcsetattr(0, TCSAFLUSH, &orig_termios);
120 : 239 : }
121 : :
122 : : #endif
123 : :
124 : : #if MICROPY_PY_OS_DUPTERM
125 : : static int call_dupterm_read(size_t idx) {
126 : : nlr_buf_t nlr;
127 : : if (nlr_push(&nlr) == 0) {
128 : : mp_obj_t read_m[3];
129 : : mp_load_method(MP_STATE_VM(dupterm_objs[idx]), MP_QSTR_read, read_m);
130 : : read_m[2] = MP_OBJ_NEW_SMALL_INT(1);
131 : : mp_obj_t res = mp_call_method_n_kw(1, 0, read_m);
132 : : if (res == mp_const_none) {
133 : : return -2;
134 : : }
135 : : mp_buffer_info_t bufinfo;
136 : : mp_get_buffer_raise(res, &bufinfo, MP_BUFFER_READ);
137 : : if (bufinfo.len == 0) {
138 : : mp_printf(&mp_plat_print, "dupterm: EOF received, deactivating\n");
139 : : MP_STATE_VM(dupterm_objs[idx]) = MP_OBJ_NULL;
140 : : return -1;
141 : : }
142 : : nlr_pop();
143 : : return *(byte *)bufinfo.buf;
144 : : } else {
145 : : // Temporarily disable dupterm to avoid infinite recursion
146 : : mp_obj_t save_term = MP_STATE_VM(dupterm_objs[idx]);
147 : : MP_STATE_VM(dupterm_objs[idx]) = NULL;
148 : : mp_printf(&mp_plat_print, "dupterm: ");
149 : : mp_obj_print_exception(&mp_plat_print, nlr.ret_val);
150 : : MP_STATE_VM(dupterm_objs[idx]) = save_term;
151 : : }
152 : :
153 : : return -1;
154 : : }
155 : : #endif
156 : :
157 : 4471 : int mp_hal_stdin_rx_chr(void) {
158 : : #if MICROPY_PY_OS_DUPTERM
159 : : // TODO only support dupterm one slot at the moment
160 : : if (MP_STATE_VM(dupterm_objs[0]) != MP_OBJ_NULL) {
161 : : int c;
162 : : do {
163 : : c = call_dupterm_read(0);
164 : : } while (c == -2);
165 : : if (c == -1) {
166 : : goto main_term;
167 : : }
168 : : if (c == '\n') {
169 : : c = '\r';
170 : : }
171 : : return c;
172 : : }
173 : : main_term:;
174 : : #endif
175 : :
176 : 4471 : unsigned char c;
177 : 4471 : ssize_t ret;
178 [ - + - - ]: 4471 : MP_HAL_RETRY_SYSCALL(ret, read(STDIN_FILENO, &c, 1), {});
179 [ - + ]: 4471 : if (ret == 0) {
180 : 0 : c = 4; // EOF, ctrl-D
181 [ + + ]: 4471 : } else if (c == '\n') {
182 : 263 : c = '\r';
183 : : }
184 : 4471 : return c;
185 : : }
186 : :
187 : 29548 : mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
188 : 29548 : ssize_t ret;
189 [ - + - - ]: 29548 : MP_HAL_RETRY_SYSCALL(ret, write(STDOUT_FILENO, str, len), {});
190 : 29548 : mp_uint_t written = ret < 0 ? 0 : ret;
191 : 29548 : int dupterm_res = mp_os_dupterm_tx_strn(str, len);
192 : 29548 : if (dupterm_res >= 0) {
193 : : written = MIN((mp_uint_t)dupterm_res, written);
194 : : }
195 : 29548 : return written;
196 : : }
197 : :
198 : : // cooked is same as uncooked because the terminal does some postprocessing
199 : 20582 : void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) {
200 : 20582 : mp_hal_stdout_tx_strn(str, len);
201 : 20582 : }
202 : :
203 : 638 : void mp_hal_stdout_tx_str(const char *str) {
204 : 638 : mp_hal_stdout_tx_strn(str, strlen(str));
205 : 638 : }
206 : :
207 : : #ifndef mp_hal_ticks_ms
208 : 9670123 : mp_uint_t mp_hal_ticks_ms(void) {
209 : : #if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) && defined(_POSIX_MONOTONIC_CLOCK)
210 : 9670123 : struct timespec tv;
211 : 9670123 : clock_gettime(CLOCK_MONOTONIC, &tv);
212 : 9668939 : return tv.tv_sec * 1000 + tv.tv_nsec / 1000000;
213 : : #else
214 : : struct timeval tv;
215 : : gettimeofday(&tv, NULL);
216 : : return tv.tv_sec * 1000 + tv.tv_usec / 1000;
217 : : #endif
218 : : }
219 : : #endif
220 : :
221 : : #ifndef mp_hal_ticks_us
222 : 310 : mp_uint_t mp_hal_ticks_us(void) {
223 : : #if (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0) && defined(_POSIX_MONOTONIC_CLOCK)
224 : 310 : struct timespec tv;
225 : 310 : clock_gettime(CLOCK_MONOTONIC, &tv);
226 : 310 : return tv.tv_sec * 1000000 + tv.tv_nsec / 1000;
227 : : #else
228 : : struct timeval tv;
229 : : gettimeofday(&tv, NULL);
230 : : return tv.tv_sec * 1000000 + tv.tv_usec;
231 : : #endif
232 : : }
233 : : #endif
234 : :
235 : : #ifndef mp_hal_time_ns
236 : 1212 : uint64_t mp_hal_time_ns(void) {
237 : 1212 : struct timeval tv;
238 : 1212 : gettimeofday(&tv, NULL);
239 : 1212 : return (uint64_t)tv.tv_sec * 1000000000ULL + (uint64_t)tv.tv_usec * 1000ULL;
240 : : }
241 : : #endif
242 : :
243 : : #ifndef mp_hal_delay_ms
244 : 6066 : void mp_hal_delay_ms(mp_uint_t ms) {
245 : 6066 : mp_uint_t start = mp_hal_ticks_ms();
246 [ + + ]: 124791 : while (mp_hal_ticks_ms() - start < ms) {
247 : 118727 : mp_event_wait_ms(1);
248 : : }
249 : 6066 : }
250 : : #endif
251 : :
252 : 18 : void mp_hal_get_random(size_t n, void *buf) {
253 : : #ifdef _HAVE_GETRANDOM
254 [ - + ]: 18 : RAISE_ERRNO(getrandom(buf, n, 0), errno);
255 : : #else
256 : : int fd = open("/dev/random", O_RDONLY);
257 : : RAISE_ERRNO(fd, errno);
258 : : RAISE_ERRNO(read(fd, buf, n), errno);
259 : : close(fd);
260 : : #endif
261 : 18 : }
|