diff --git a/cores/arduino/zephyrSerial.cpp b/cores/arduino/zephyrSerial.cpp index 0a9b2e6f2..97dc046cd 100644 --- a/cores/arduino/zephyrSerial.cpp +++ b/cores/arduino/zephyrSerial.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2022 Dhruva Gole + * Copyright (c) Arduino s.r.l. and/or its affiliated companies * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,6 +10,7 @@ #include #include +#include namespace { @@ -58,10 +60,14 @@ void arduino::ZephyrSerial::begin(unsigned long baud, uint16_t conf) { .flow_ctrl = UART_CFG_FLOW_CTRL_NONE, }; - uart->ops.init(uart); + _reinit_if_needed(); uart_configure(uart, &config); uart_irq_callback_user_data_set(uart, arduino::ZephyrSerial::IrqDispatch, this); + k_sem_take(&rx.sem, K_FOREVER); + ring_buf_reset(&rx.ringbuf); + k_sem_give(&rx.sem); + uart_irq_rx_enable(uart); } @@ -72,10 +78,6 @@ void arduino::ZephyrSerial::IrqHandler() { (void)uart_irq_update(uart); - if (ring_buf_size_get(&tx.ringbuf) == 0) { - uart_irq_tx_disable(uart); - } - k_sem_take(&rx.sem, K_NO_WAIT); while (uart_irq_rx_ready(uart) && ((length = uart_fifo_read(uart, buf, sizeof(buf))) > 0)) { length = min(sizeof(buf), static_cast(length)); @@ -88,6 +90,11 @@ void arduino::ZephyrSerial::IrqHandler() { k_sem_give(&rx.sem); k_sem_take(&tx.sem, K_NO_WAIT); + + if (ring_buf_size_get(&tx.ringbuf) == 0) { + uart_irq_tx_disable(uart); + } + while (uart_irq_tx_ready(uart) && ((length = ring_buf_size_get(&tx.ringbuf)) > 0)) { length = min(sizeof(buf), static_cast(length)); ring_buf_peek(&tx.ringbuf, &buf[0], length); @@ -103,6 +110,7 @@ void arduino::ZephyrSerial::IrqHandler() { } void arduino::ZephyrSerial::IrqDispatch(const struct device *dev, void *data) { + (void)dev; // unused reinterpret_cast(data)->IrqHandler(); } @@ -116,87 +124,93 @@ int arduino::ZephyrSerial::available() { return ret; } +int arduino::ZephyrSerial::availableForWrite() { + int ret; + + k_sem_take(&tx.sem, K_FOREVER); + ret = ring_buf_space_get(&tx.ringbuf); + k_sem_give(&tx.sem); + + return ret; +} + int arduino::ZephyrSerial::peek() { uint8_t data; k_sem_take(&rx.sem, K_FOREVER); - ring_buf_peek(&rx.ringbuf, &data, 1); + uint32_t cb_ret = ring_buf_peek(&rx.ringbuf, &data, 1); k_sem_give(&rx.sem); - return data; + return cb_ret ? data : -1; } int arduino::ZephyrSerial::read() { uint8_t data; k_sem_take(&rx.sem, K_FOREVER); - ring_buf_get(&rx.ringbuf, &data, 1); + uint32_t cb_ret = ring_buf_get(&rx.ringbuf, &data, 1); k_sem_give(&rx.sem); - return data; + return cb_ret ? data : -1; } size_t arduino::ZephyrSerial::write(const uint8_t *buffer, size_t size) { - int ret; + size_t idx = 0; - k_sem_take(&tx.sem, K_FOREVER); - ret = ring_buf_put(&tx.ringbuf, buffer, size); - k_sem_give(&tx.sem); - - if (ret < 0) { - return 0; + while (1) { + k_sem_take(&tx.sem, K_FOREVER); + auto ret = ring_buf_put(&tx.ringbuf, &buffer[idx], size - idx); + k_sem_give(&tx.sem); + if (ret < 0) { + return 0; + } + idx += ret; + if (ret == 0) { + uart_irq_tx_enable(uart); + yield(); + } + if (idx == size) { + break; + } } uart_irq_tx_enable(uart); - return ret; + return size; } -#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), serials) -arduino::ZephyrSerial Serial(DEVICE_DT_GET(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), serials, 0))); -#if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1) -#define ARDUINO_SERIAL_DEFINED_0 1 - -#define DECL_SERIAL_0(n, p, i) -#define DECL_SERIAL_N(n, p, i) \ - arduino::ZephyrSerial Serial##i(DEVICE_DT_GET(DT_PHANDLE_BY_IDX(n, p, i))); -#define DECLARE_SERIAL_N(n, p, i) \ - COND_CODE_1(ARDUINO_SERIAL_DEFINED_##i, (DECL_SERIAL_0(n, p, i)), (DECL_SERIAL_N(n, p, i))) - -#define CALL_EVENT_0(n, p, i) -#define CALL_EVENT_N(n, p, i) \ - if (_CONCAT(Serial, i).available()) \ - _CONCAT(_CONCAT(serial, i), Event)(); -#define CALL_SERIALEVENT_N(n, p, i) \ - COND_CODE_1(ARDUINO_SERIAL_DEFINED_##i, (CALL_EVENT_0(n, p, i)), (CALL_EVENT_N(n, p, i))); - -#define DECL_EVENT_0(n, p, i) -#define DECL_EVENT_N(n, p, i) \ - __attribute__((weak)) void serial##i##Event() { \ +void arduino::ZephyrSerial::flush() { + while (ring_buf_size_get(&tx.ringbuf) > 0) { + k_yield(); } -#define DECLARE_SERIALEVENT_N(n, p, i) \ - COND_CODE_1(ARDUINO_SERIAL_DEFINED_##i, (DECL_EVENT_0(n, p, i)), (DECL_EVENT_N(n, p, i))); - -DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, DECLARE_SERIAL_N) -#endif // PROP_LEN(serials) > 1 -#elif DT_NODE_HAS_STATUS(DT_NODELABEL(arduino_serial), okay) -/* If serials node is not defined, tries to use arduino_serial */ -arduino::ZephyrSerial Serial(DEVICE_DT_GET(DT_NODELABEL(arduino_serial))); -#else -arduino::ZephyrSerialStub Serial; + while (uart_irq_tx_complete(uart) == 0) { + k_yield(); + } +} + +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), serials) +#define DEFINE_SERIAL_N(n, p, i) \ + arduino::ZephyrSerial ZARD_SERIAL_NAME(i)(DEVICE_DT_GET(DT_PHANDLE_BY_IDX(n, p, i))); +DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, DEFINE_SERIAL_N) + +#elif DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(arduino_serial)) +arduino::ZephyrSerial ZARD_SERIAL_NAME(0)(DEVICE_DT_GET(DT_NODELABEL(arduino_serial))); #endif -__attribute__((weak)) void serialEvent() { -} -#if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1) -DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, DECLARE_SERIALEVENT_N) +#if ZARD_FIRST_SERIAL_IS_STUB +arduino::ZephyrSerialStub Serial; #endif -void arduino::serialEventRun(void) { - if (Serial.available()) { - serialEvent(); +#define DEFINE_WEAK_SERIALEVENT_N(n, s) \ + __attribute__((weak)) void CONCAT(serial, ZARD_SERIAL_STEM(n), Event)() { \ } -#if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1) - DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, CALL_SERIALEVENT_N) -#endif + +#define CALL_SERIALEVENT_N(n, s) \ + if (ZARD_SERIAL_NAME(n).available()) \ + CONCAT(serial, ZARD_SERIAL_STEM(n), Event)(); + +LISTIFY(ZARD_GENERIC_SERIAL_COUNT, DEFINE_WEAK_SERIALEVENT_N, ()) + +void arduino::serialEventRun(void) { + LISTIFY(ZARD_GENERIC_SERIAL_COUNT, CALL_SERIALEVENT_N, ()) } diff --git a/cores/arduino/zephyrSerial.h b/cores/arduino/zephyrSerial.h index 57cc44ac6..b0bb3de6d 100644 --- a/cores/arduino/zephyrSerial.h +++ b/cores/arduino/zephyrSerial.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2022 Dhruva Gole + * Copyright (c) Arduino s.r.l. and/or its affiliated companies * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,18 +8,18 @@ #pragma once #include - -#include +#include #include namespace arduino { class ZephyrSerialStub : public HardwareSerial { public: - void begin(unsigned long baudRate) { + void begin(__attribute__((unused)) unsigned long baudRate) { } - void begin(unsigned long baudrate, uint16_t config) { + void begin(__attribute__((unused)) unsigned long baudrate, + __attribute__((unused)) uint16_t config) { } void end() { @@ -72,8 +73,7 @@ class ZephyrSerial : public HardwareSerial { begin(baudrate, SERIAL_8N1); } - void flush() { - } + void flush(); void end() { #ifdef CONFIG_DEVICE_DEINIT_SUPPORT @@ -89,7 +89,9 @@ class ZephyrSerial : public HardwareSerial { return write(&data, 1); } + using Print::write; // pull in write(str) and write(buf, size) from Print int available(); + int availableForWrite(); int peek(); int read(); @@ -97,6 +99,8 @@ class ZephyrSerial : public HardwareSerial { return true; } + friend class SerialUSB_; + protected: void IrqHandler(); static void IrqDispatch(const struct device *dev, void *data); @@ -104,26 +108,98 @@ class ZephyrSerial : public HardwareSerial { const struct device *uart; ZephyrSerialBuffer tx; ZephyrSerialBuffer rx; + + virtual void _reinit_if_needed() { + uart->ops.init(uart); + } }; } // namespace arduino +/* Return the index of it if matched, oterwise return an empty string. */ +#define ZARD_SERIAL_MATCH(n, p, i, node) \ + COND_CODE_1(DT_SAME_NODE(DT_PHANDLE_BY_IDX(n, p, i), node), (i), ()) + +/* Only matched device returns non-empty value, so the overall expansion is + * matched device's index. + */ +#define ZARD_SERIAL_INDEXOF(node) \ + DT_FOREACH_PROP_ELEM_VARGS(DT_PATH(zephyr_user), serials, ZARD_SERIAL_MATCH, node) + +/* Serial object associated with the Zephyr console. */ +#define ARDUINO_CONSOLE_SERIAL ZARD_SERIAL_NAME(ZARD_SERIAL_INDEXOF(DT_CHOSEN(zephyr_console))) + +/* Serial object associated with the first HW serial (usually on D0/D1). */ +#define ARDUINO_HARDWARE_SERIAL ZARD_SERIAL_NAME(0) + +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm_serial) +/* Devicetree requires a SerialUSB object for 'Serial'. */ +#define ZARD_SKIP_FIRST_SERIAL 1 +#if (CONFIG_USB_CDC_ACM || CONFIG_USBD_CDC_ACM_CLASS) +/* SerialUSB can be compiled in the project. */ +#define ZARD_FIRST_SERIAL_IS_SERIALUSB 1 +#else +/* SerialUSB is required but no driver was enabled for the USB CDC ACM device. + * Define a stub Serial object to avoid build errors. + */ +#define ZARD_FIRST_SERIAL_IS_STUB 1 +#endif +#endif + +#if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), arduino_router_serial) +/* If the board has an arduino,router-serial node, and the currently used + * library has the support, then the 'Serial' object is actually the same as + * the Monitor object in the Arduino_RouterBridge library. + */ +#define ZARD_SKIP_FIRST_SERIAL 1 +#define ZARD_FIRST_SERIAL_IS_ARDUINO_ROUTER 1 +#define ARDUINO_ROUTER_PHANDLE DT_PROP(DT_PATH(zephyr_user), arduino_router_serial) +#define ARDUINO_ROUTER_SERIAL ZARD_SERIAL_NAME(ZARD_SERIAL_INDEXOF(ARDUINO_ROUTER_PHANDLE)) +#endif + +/* Name of a Serial object for a given index. */ +#define ZARD_SERIAL_NAME(n) CONCAT(Serial, ZARD_SERIAL_STEM(n)) + +/* + * Determine the number of generic ZephyrSerial objects to instantiate. + */ + #if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), serials) -extern arduino::ZephyrSerial Serial; -#if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1) -#define SERIAL_DEFINED_0 1 -#define EXTERN_SERIAL_N(i) extern arduino::ZephyrSerial Serial##i; -#define DECLARE_EXTERN_SERIAL_N(n, p, i) COND_CODE_1(SERIAL_DEFINED_##i, (), (EXTERN_SERIAL_N(i))) - -/* Declare Serial1, Serial2, ... */ -DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, DECLARE_EXTERN_SERIAL_N) - -#undef DECLARE_EXTERN_SERIAL_N -#undef EXTERN_SERIAL_N -#undef SERIAL_DEFINED_0 +/* The array property; the total number is the length of the array. */ +#define ZARD_GENERIC_SERIAL_COUNT DT_PROP_LEN(DT_PATH(zephyr_user), serials) + +#elif DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(arduino_serial)) +/* A single node with compatible "arduino,serial" is present. */ +#define ZARD_GENERIC_SERIAL_COUNT 1 + +#elif ZARD_SKIP_FIRST_SERIAL +/* Only a special Serial is present. */ +#define ZARD_GENERIC_SERIAL_COUNT 0 + +#else +/* Neither 'serials' property, nor 'arduino,serial' node, nor a special Serial + * is present. Define a stub Serial object to avoid build errors. + */ +#define ZARD_GENERIC_SERIAL_COUNT 0 +#define ZARD_SKIP_FIRST_SERIAL 1 +#define ZARD_FIRST_SERIAL_IS_STUB 1 #endif -#elif DT_NODE_HAS_STATUS(DT_NODELABEL(arduino_serial), okay) -extern arduino::ZephyrSerial Serial; + +#if ZARD_SKIP_FIRST_SERIAL +// Map index 0 to 'Serial1', index 1 to 'Serial2', etc. +#define ZARD_SERIAL_STEM(i) UTIL_INC(i) #else +// Map index 0 to 'Serial', index 1 to 'Serial1', etc. +#define ZARD_SERIAL_STEM(i) COND_CODE_0(i, (), (i)) +#endif + +#if ZARD_FIRST_SERIAL_IS_STUB extern arduino::ZephyrSerialStub Serial; #endif + +#if ZARD_GENERIC_SERIAL_COUNT > 0 +/* Declare all generic ZephyrSerial objects */ +#define DECLARE_SERIAL_N(n, s) extern arduino::ZephyrSerial ZARD_SERIAL_NAME(n); +LISTIFY(ZARD_GENERIC_SERIAL_COUNT, DECLARE_SERIAL_N, ()) +#undef DECLARE_SERIAL_N +#endif