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