21
21
#include <sdkconfig.h>
22
22
#ifdef CONFIG_AVM_ENABLE_UART_PORT_DRIVER
23
23
24
+ #include <assert.h>
24
25
#include <string.h>
25
26
26
27
#include <driver/uart.h>
27
-
28
28
#include <esp_log.h>
29
- #include <freertos/FreeRTOS.h>
30
- #include <freertos/task.h>
29
+ #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL (5 , 2 , 0 )
30
+ #include <soc/gpio_num.h>
31
+ #endif
31
32
32
33
#include "atom.h"
33
34
#include "bif.h"
38
39
#include "interop.h"
39
40
#include "mailbox.h"
40
41
#include "module.h"
41
- #include "port.h"
42
42
#include "platform_defaultatoms.h"
43
+ #include "port.h"
43
44
#include "scheduler.h"
45
+ #include "smp.h"
44
46
#include "term.h"
45
47
#include "utils.h"
46
48
50
52
#include "sys.h"
51
53
52
54
static Context * uart_driver_create_port (GlobalContext * global , term opts );
53
-
54
- static const char * const ealready_atom = ATOM_STR ("\x8" , "ealready" );
55
- static const char * const no_proc_atom = ATOM_STR ("\x7" , "no_proc" );
56
55
static NativeHandlerResult uart_driver_consume_mailbox (Context * ctx );
57
56
58
57
#define TAG "uart_driver"
59
58
#define UART_BUF_SIZE 256
59
+ #define NO_REF 0
60
+ #define NO_READER term_invalid_term()
61
+ #define PIN_ERROR -2
62
+
63
+ #ifndef GPIO_NUM_MAX // remove conditional after ESP_IDF v5.1 is deprecated
64
+ #define GPIO_NUM_MAX SOC_GPIO_PIN_COUNT
65
+ #endif
60
66
61
67
struct UARTData
62
68
{
@@ -65,6 +71,9 @@ struct UARTData
65
71
term reader_process_pid ;
66
72
uint64_t reader_ref_ticks ;
67
73
uint8_t uart_num ;
74
+ #ifndef AVM_NO_SMP
75
+ Mutex * reader_lock ;
76
+ #endif
68
77
};
69
78
70
79
static const AtomStringIntPair parity_table [] = {
@@ -86,7 +95,8 @@ enum uart_cmd
86
95
UARTInvalidCmd = 0 ,
87
96
UARTReadCmd = 1 ,
88
97
UARTWriteCmd = 2 ,
89
- UARTCloseCmd = 3
98
+ UARTCloseCmd = 3 ,
99
+ UARTCancelCmd = 4
90
100
};
91
101
92
102
static const AtomStringIntPair cmd_table [] = {
@@ -96,13 +106,22 @@ static const AtomStringIntPair cmd_table[] = {
96
106
SELECT_INT_DEFAULT (UARTInvalidCmd )
97
107
};
98
108
109
+ static void safe_update_reader_data (struct UARTData * uart_data , term pid , uint64_t ref_ticks )
110
+ {
111
+ SMP_MUTEX_LOCK (uart_data -> reader_lock );
112
+ uart_data -> reader_process_pid = pid ;
113
+ uart_data -> reader_ref_ticks = ref_ticks ;
114
+ SMP_MUTEX_UNLOCK (uart_data -> reader_lock );
115
+ }
116
+
99
117
EventListener * uart_interrupt_callback (GlobalContext * glb , EventListener * listener )
100
118
{
101
119
struct UARTData * uart_data = GET_LIST_ENTRY (listener , struct UARTData , listener );
102
120
103
121
uart_event_t event ;
104
- if (xQueueReceive (uart_data -> rxqueue , (void * ) & event , (TickType_t ) portMAX_DELAY )) {
122
+ if (xQueueReceive (uart_data -> rxqueue , (void * ) & event , (TickType_t ) 0 )) {
105
123
switch (event .type ) {
124
+ case UART_DATA_BREAK :
106
125
case UART_DATA :
107
126
if (uart_data -> reader_process_pid != term_invalid_term ()) {
108
127
int bin_size = term_binary_heap_size (event .size );
@@ -131,40 +150,50 @@ EventListener *uart_interrupt_callback(GlobalContext *glb, EventListener *listen
131
150
globalcontext_send_message (glb , local_pid , result_tuple );
132
151
133
152
memory_destroy_heap (& heap , glb );
134
-
135
- uart_data -> reader_process_pid = term_invalid_term ();
136
- uart_data -> reader_ref_ticks = 0 ;
153
+ safe_update_reader_data (uart_data , NO_READER , NO_REF );
137
154
}
138
155
break ;
139
156
case UART_FIFO_OVF :
157
+ ESP_LOGE (TAG , "FIFO overflow!" );
140
158
break ;
141
159
case UART_BUFFER_FULL :
160
+ ESP_LOGW (TAG , "buffer is full!" );
142
161
break ;
143
162
case UART_BREAK :
163
+ // TODO: handle single NULL char event?, or treat this like UART_DATA & UART_DATA_BREAK?
144
164
break ;
145
165
case UART_PARITY_ERR :
166
+ ESP_LOGE (TAG , "parity error detected!" );
146
167
break ;
147
168
case UART_FRAME_ERR :
169
+ ESP_LOGE (TAG , "frame error detected!" );
148
170
break ;
149
171
case UART_PATTERN_DET :
172
+ // MAYBE: add pattern detection for incoming uart messages
150
173
break ;
151
174
default :
152
175
break ;
153
176
}
154
177
}
178
+
155
179
return listener ;
156
180
}
157
181
158
182
static int get_uart_pin_opt (term opts , term pin_name )
159
183
{
184
+ _Static_assert (PIN_ERROR < UART_PIN_NO_CHANGE );
160
185
term value = interop_proplist_get_value_default (opts , pin_name , DEFAULT_ATOM );
161
186
if (value == DEFAULT_ATOM ) {
162
187
return UART_PIN_NO_CHANGE ;
163
188
} else if (!term_is_integer (value )) {
164
- // TODO: let's return -2;
165
- fprintf (stderr , "abort() at %s:%i.\n" , __FILE__ , __LINE__ );
166
- abort ();
189
+ ESP_LOGE (TAG , "pin must be an integer!" );
190
+ return PIN_ERROR ;
167
191
} else {
192
+ int pin_num = term_to_int (value );
193
+ if (UNLIKELY (pin_num < UART_PIN_NO_CHANGE || pin_num > GPIO_NUM_MAX )) {
194
+ ESP_LOGE (TAG , "pin number %i is out of range [default | -1..%i]" , pin_num , GPIO_NUM_MAX );
195
+ return PIN_ERROR ;
196
+ }
168
197
return term_to_int (value );
169
198
}
170
199
}
@@ -182,22 +211,28 @@ Context *uart_driver_create_port(GlobalContext *global, term opts)
182
211
term flow_control_term = interop_proplist_get_value_default (opts , FLOW_CONTROL_ATOM , NONE_ATOM );
183
212
term parity_term = interop_proplist_get_value_default (opts , PARITY_ATOM , NONE_ATOM );
184
213
185
- term tx_pin = get_uart_pin_opt (opts , TX_ATOM );
186
- term rx_pin = get_uart_pin_opt (opts , RX_ATOM );
187
- term rts_pin = get_uart_pin_opt (opts , RTS_ATOM );
188
- term cts_pin = get_uart_pin_opt (opts , CTS_ATOM );
214
+ int tx_pin = get_uart_pin_opt (opts , TX_ATOM );
215
+ int rx_pin = get_uart_pin_opt (opts , RX_ATOM );
216
+ int rts_pin = get_uart_pin_opt (opts , RTS_ATOM );
217
+ int cts_pin = get_uart_pin_opt (opts , CTS_ATOM );
218
+ if (UNLIKELY (tx_pin == PIN_ERROR || rx_pin == PIN_ERROR
219
+ || rts_pin == PIN_ERROR || cts_pin == PIN_ERROR )) {
220
+ // error already logged in get_uart_pin_opts
221
+ return NULL ;
222
+ }
189
223
190
224
term event_queue_len_term = interop_proplist_get_value_default (opts , EVENT_QUEUE_LEN_ATOM , term_from_int (16 ));
191
225
if (!term_is_integer (event_queue_len_term )) {
192
- fprintf ( stderr , "abort() at %s:%i.\n" , __FILE__ , __LINE__ );
193
- abort () ;
226
+ ESP_LOGE ( TAG , "event_queue_len must be an integer" );
227
+ return NULL ;
194
228
}
195
229
int event_queue_len = term_to_int (event_queue_len_term );
196
230
197
231
int ok ;
198
232
char * uart_name = interop_term_to_string (uart_name_term , & ok );
199
233
if (!uart_name || !ok ) {
200
- AVM_ABORT ();
234
+ ESP_LOGE (TAG , "name must be character or binary string" );
235
+ return NULL ;
201
236
}
202
237
203
238
uint8_t uart_num ;
@@ -206,13 +241,15 @@ Context *uart_driver_create_port(GlobalContext *global, term opts)
206
241
} else if (!strcmp (uart_name , "UART1" )) {
207
242
uart_num = UART_NUM_1 ;
208
243
}
209
- #if defined(CONFIG_IDF_TARGET_ESP32 ) || defined(CONFIG_IDF_TARGET_ESP32S3 )
210
- else if (!strcmp (uart_name , "UART2 ")) {
244
+ #if defined(CONFIG_IDF_TARGET_ESP32 ) || defined(CONFIG_IDF_TARGET_ESP32S3 )
245
+ else if (!strcmp (uart_name , "UART2 ")) {
211
246
uart_num = UART_NUM_2 ;
212
247
}
213
- #endif
248
+ #endif
214
249
else {
215
- AVM_ABORT ();
250
+ free (uart_name );
251
+ ESP_LOGE (TAG , "invalid uart bus name!" );
252
+ return NULL ;
216
253
}
217
254
free (uart_name );
218
255
@@ -233,7 +270,8 @@ Context *uart_driver_create_port(GlobalContext *global, term opts)
233
270
data_bits = UART_DATA_5_BITS ;
234
271
break ;
235
272
default :
236
- AVM_ABORT ();
273
+ ESP_LOGE (TAG , "invalid data_bits!" );
274
+ return NULL ;
237
275
}
238
276
239
277
int stop_bits ;
@@ -245,17 +283,20 @@ Context *uart_driver_create_port(GlobalContext *global, term opts)
245
283
stop_bits = UART_STOP_BITS_2 ;
246
284
break ;
247
285
default :
248
- AVM_ABORT ();
286
+ ESP_LOGE (TAG , "invalid stop_bits!" );
287
+ return NULL ;
249
288
}
250
289
251
290
uart_hw_flowcontrol_t flow_control = interop_atom_term_select_int (flow_control_table , flow_control_term , ctx -> global );
252
291
if (flow_control < 0 ) {
253
- AVM_ABORT ();
292
+ ESP_LOGE (TAG , "invalid flow_control!" );
293
+ return NULL ;
254
294
}
255
295
256
296
uart_parity_t parity = interop_atom_term_select_int (parity_table , parity_term , ctx -> global );
257
297
if (parity < 0 ) {
258
- AVM_ABORT ();
298
+ ESP_LOGE (TAG , "invalid parity!" );
299
+ return NULL ;
259
300
}
260
301
261
302
uart_config_t uart_config = {
@@ -272,9 +313,10 @@ Context *uart_driver_create_port(GlobalContext *global, term opts)
272
313
273
314
uart_set_pin (uart_num , tx_pin , rx_pin , rts_pin , cts_pin );
274
315
275
- struct UARTData * uart_data = malloc (sizeof (struct UARTData ));
316
+ size_t alloc_size = sizeof (struct UARTData );
317
+ struct UARTData * uart_data = malloc (alloc_size );
276
318
if (IS_NULL_PTR (uart_data )) {
277
- fprintf (stderr , "Failed to allocate memory: %s:%i.\n" , __FILE__ , __LINE__ );
319
+ fprintf (stderr , "Failed to allocate memory size %i : %s:%i.\n" , ( int ) alloc_size , __FILE__ , __LINE__ );
278
320
AVM_ABORT ();
279
321
}
280
322
uart_data -> listener .handler = uart_interrupt_callback ;
@@ -286,15 +328,21 @@ Context *uart_driver_create_port(GlobalContext *global, term opts)
286
328
ctx -> platform_data = uart_data ;
287
329
288
330
if (uart_driver_install (uart_num , UART_BUF_SIZE , 0 , event_queue_len , & uart_data -> rxqueue , 0 ) != ESP_OK ) {
289
- fprintf (stderr , "abort() at %s:%i.\n" , __FILE__ , __LINE__ );
290
- abort ();
331
+ ESP_LOGE (TAG , "failed to install uart driver." );
332
+ free (uart_data );
333
+ return NULL ;
291
334
}
292
335
uart_data -> listener .sender = uart_data -> rxqueue ;
293
336
if (xQueueAddToSet (uart_data -> rxqueue , event_set ) != pdPASS ) {
294
- fprintf (stderr , "abort() at %s:%i.\n" , __FILE__ , __LINE__ );
295
- abort ();
337
+ ESP_LOGE (TAG , "failed to establish uart queue." );
338
+ free (uart_data );
339
+ return NULL ;
296
340
}
297
341
342
+ #ifndef AVM_NO_SMP
343
+ uart_data -> reader_lock = smp_mutex_create ();
344
+ #endif
345
+
298
346
return ctx ;
299
347
}
300
348
@@ -309,13 +357,13 @@ static void uart_driver_do_read(Context *ctx, GenMessage gen_message)
309
357
int local_pid = term_to_local_process_id (pid );
310
358
311
359
if (uart_data -> reader_process_pid != term_invalid_term ()) {
312
- if (UNLIKELY (memory_ensure_free_with_roots (ctx , TUPLE_SIZE (2 ) * 2 , 1 , & ref , MEMORY_CAN_SHRINK ) != MEMORY_GC_OK )) {
360
+ if (UNLIKELY (memory_ensure_free_with_roots (ctx , TUPLE_SIZE (2 ) * 2 , 1 , & ref , MEMORY_CAN_SHRINK ) != MEMORY_GC_OK )) {
313
361
ESP_LOGE (TAG , "[uart_driver_do_read] Failed to allocate space for error tuple" );
314
- globalcontext_send_message (glb , local_pid , MEMORY_ATOM );
362
+ globalcontext_send_message (glb , local_pid , OUT_OF_MEMORY_ATOM );
315
363
return ;
316
364
}
317
365
318
- term ealready = globalcontext_make_atom (glb , ealready_atom );
366
+ term ealready = globalcontext_make_atom (glb , ATOM_STR ( "\x8" , "ealready" ) );
319
367
term error_tuple = port_create_error_tuple (ctx , ealready );
320
368
port_send_reply (ctx , pid , ref , error_tuple );
321
369
return ;
@@ -328,7 +376,7 @@ static void uart_driver_do_read(Context *ctx, GenMessage gen_message)
328
376
int bin_size = term_binary_heap_size (count );
329
377
if (UNLIKELY (memory_ensure_free_with_roots (ctx , bin_size + TUPLE_SIZE (2 ) * 2 , 1 , & ref , MEMORY_CAN_SHRINK ) != MEMORY_GC_OK )) {
330
378
ESP_LOGE (TAG , "[uart_driver_do_read] Failed to allocate space for return value" );
331
- globalcontext_send_message (glb , local_pid , MEMORY_ATOM );
379
+ globalcontext_send_message (glb , local_pid , OUT_OF_MEMORY_ATOM );
332
380
}
333
381
334
382
term bin = term_create_uninitialized_binary (count , & ctx -> heap , glb );
@@ -342,8 +390,7 @@ static void uart_driver_do_read(Context *ctx, GenMessage gen_message)
342
390
port_send_reply (ctx , pid , ref , ok_tuple );
343
391
344
392
} else {
345
- uart_data -> reader_process_pid = pid ;
346
- uart_data -> reader_ref_ticks = ref_ticks ;
393
+ safe_update_reader_data (uart_data , pid , ref_ticks );
347
394
}
348
395
}
349
396
@@ -389,7 +436,7 @@ static void uart_driver_do_write(Context *ctx, GenMessage gen_message)
389
436
int local_pid = term_to_local_process_id (pid );
390
437
if (UNLIKELY (memory_ensure_free_with_roots (ctx , TUPLE_SIZE (2 ), 1 , & ref , MEMORY_CAN_SHRINK ) != MEMORY_GC_OK )) {
391
438
ESP_LOGE (TAG , "[uart_driver_do_write] Failed to allocate space for return value" );
392
- globalcontext_send_message (glb , local_pid , MEMORY_ATOM );
439
+ globalcontext_send_message (glb , local_pid , OUT_OF_MEMORY_ATOM );
393
440
}
394
441
395
442
port_send_reply (ctx , pid , ref , OK_ATOM );
@@ -402,21 +449,26 @@ static void uart_driver_do_close(Context *ctx, GenMessage gen_message)
402
449
term pid = gen_message .pid ;
403
450
term ref = gen_message .ref ;
404
451
405
- int local_pid = term_to_local_process_id (pid );
406
-
407
452
sys_unregister_listener (glb , & uart_data -> listener );
453
+ #ifndef AVM_NO_SMP
454
+ smp_mutex_destroy (uart_data -> reader_lock );
455
+ #endif
456
+
457
+ term result ;
458
+ esp_err_t err = uart_driver_delete (uart_data -> uart_num );
459
+ if (UNLIKELY (err != ESP_OK )) {
460
+ ESP_LOGE (TAG , "Failed to delete UART driver. Error: %s" , esp_err_to_name (err ));
461
+ result = ERROR_ATOM ;
462
+ } else {
463
+ result = OK_ATOM ;
464
+ }
408
465
409
466
if (UNLIKELY (memory_ensure_free_with_roots (ctx , TUPLE_SIZE (2 ), 1 , & ref , MEMORY_CAN_SHRINK ) != MEMORY_GC_OK )) {
410
467
ESP_LOGE (TAG , "[uart_driver_do_close] Failed to allocate space for return value" );
411
- globalcontext_send_message (glb , local_pid , MEMORY_ATOM );
468
+ globalcontext_send_message (glb , term_to_local_process_id ( pid ), OUT_OF_MEMORY_ATOM );
412
469
}
413
470
414
- port_send_reply (ctx , pid , ref , OK_ATOM );
415
-
416
- esp_err_t err = uart_driver_delete (uart_data -> uart_num );
417
- if (UNLIKELY (err != ESP_OK )) {
418
- ESP_LOGW (TAG , "Failed to delete UART driver. err=%i\n" , err );
419
- }
471
+ port_send_reply (ctx , pid , ref , result );
420
472
421
473
free (uart_data );
422
474
ctx -> platform_data = NULL ;
@@ -443,13 +495,12 @@ static NativeHandlerResult uart_driver_consume_mailbox(Context *ctx)
443
495
if (is_closed ) {
444
496
if (UNLIKELY (memory_ensure_free (ctx , TUPLE_SIZE (2 ) * 2 + REF_SIZE ) != MEMORY_GC_OK )) {
445
497
ESP_LOGE (TAG , "[uart_driver_consume_mailbox] Failed to allocate space for error tuple" );
446
- globalcontext_send_message (glb , local_pid , MEMORY_ATOM );
498
+ globalcontext_send_message (glb , local_pid , OUT_OF_MEMORY_ATOM );
447
499
}
448
500
449
- term no_proc = globalcontext_make_atom (glb , no_proc_atom );
450
501
term error_tuple = term_alloc_tuple (2 , & ctx -> heap );
451
502
term_put_tuple_element (error_tuple , 0 , ERROR_ATOM );
452
- term_put_tuple_element (error_tuple , 1 , no_proc );
503
+ term_put_tuple_element (error_tuple , 1 , NOPROC_ATOM );
453
504
454
505
term result_tuple = term_alloc_tuple (2 , & ctx -> heap );
455
506
term_put_tuple_element (result_tuple , 0 , term_from_ref_ticks (ref_ticks , & ctx -> heap ));
0 commit comments