diff --git a/comm/msg_responses.c b/comm/msg_responses.c index 3e6128b..a59b0d8 100644 --- a/comm/msg_responses.c +++ b/comm/msg_responses.c @@ -5,6 +5,7 @@ #include "platform.h" #include "messages.h" #include "msg_responses.h" +#include "payload_builder.h" void com_respond_snprintf(TF_ID frame_id, TF_TYPE type, const char *format, ...) { @@ -37,6 +38,12 @@ void com_respond_ok(TF_ID frame_id) com_respond_buf(frame_id, MSG_SUCCESS, NULL, 0); } +void com_send_pb(TF_TYPE type, PayloadBuilder *pb) +{ + uint32_t len; + uint8_t *buf = pb_close(pb, &len); + com_send_buf(type, buf, len); +} void com_send_buf(TF_TYPE type, const uint8_t *buf, uint32_t len) { diff --git a/comm/msg_responses.h b/comm/msg_responses.h index e773504..f91f2c9 100644 --- a/comm/msg_responses.h +++ b/comm/msg_responses.h @@ -9,6 +9,8 @@ #error "Include messages.h instead!" #endif +#include "payload_builder.h" + /** * Respond to a TF message using printf-like formatting. * @@ -54,6 +56,14 @@ void com_respond_error(TF_ID frame_id, error_t error); */ void com_send_buf(TF_TYPE type, const uint8_t *buf, uint32_t len); +/** + * Send a payload builder's content + * + * @param type - response type byte + * @param pb - builder + */ +void com_send_pb(TF_TYPE type, PayloadBuilder *pb); + /** * Same like tf_respond_buf(), but the buffer length is measured with strlen. * Used to sending ASCII string responses. diff --git a/units/usart/unit_usart.c b/units/usart/unit_usart.c index cb1b84a..e4684f6 100644 --- a/units/usart/unit_usart.c +++ b/units/usart/unit_usart.c @@ -6,10 +6,36 @@ #include "comm/messages.h" #include "unit_base.h" #include "unit_usart.h" +#include "tasks/task_msg.h" #define UUSART_INTERNAL #include "_internal.h" +static void UUSART_SendReceivedDataToMaster(Job *job) +{ + Unit *unit = job->data1; + struct priv *priv = unit->data; + + uint32_t readpos = job->d32; + uint32_t count = job->len; + + // Debug: print to debug port +// PUTS("Job rx >"); +// PUTSN((char *) priv->rx_buffer + readpos, (uint16_t) count); +// PUTS("<\r\n"); + + // Debug: Write out +// UU_USART_Write(unit, (const uint8_t *) (priv->rx_buffer + readpos), count); + + // TODO modify TF to allow writing in multiple chunks to avoid this useless buffer copying + PayloadBuilder pb = pb_start(unit_tmp512, UNIT_TMP_LEN, NULL); + pb_u8(&pb, unit->callsign); + pb_u8(&pb, 0x00); // report type "Data received" + pb_buf(&pb, (uint8_t *) (priv->rx_buffer + readpos), count); + assert_param(pb.ok); + com_send_pb(MSG_UNIT_REPORT, &pb); +} + /** * Handle received data (we're inside the IRQ) * @@ -23,55 +49,90 @@ void UUSART_DMA_HandleRxFromIRQ(Unit *unit, uint16_t endpos) assert_param(priv); uint16_t readpos = priv->rx_buf_readpos; - assert_param(endpos > readpos); uint16_t count = (endpos - readpos); - uint8_t *start = (uint8_t *) (priv->rx_buffer + readpos); - // Do something with the data... - PUTSN((char *) start, count); - PUTNL(); + // We defer it to the job queue + // FIXME this can starve the shared queue if full duplex is used, we need a second higher priority queue for those report jobs + Job j = { + .data1 = unit, + .d32 = priv->rx_buf_readpos, + .len = count, + .cb = UUSART_SendReceivedDataToMaster + }; + scheduleJob(&j); // Move the read cursor, wrap around if needed if (endpos == UUSART_RXBUF_LEN) endpos = 0; priv->rx_buf_readpos = endpos; } -enum PinCmd_ { - CMD_WRITE = 0, -}; -/** Handle a request message */ -static error_t UUSART_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp) +error_t UU_USART_Write(Unit *unit, const uint8_t *buffer, uint32_t len) { + CHECK_TYPE(unit, &UNIT_USART); struct priv *priv = unit->data; - switch (command) { - case CMD_WRITE:; - uint32_t len; - const uint8_t *pld = pp_tail(pp, &len); + uint32_t t_start = HAL_GetTick(); + while (len > 0) { + // this should be long enough even for the slowest bitrates and 512 bytes + if (HAL_GetTick() - t_start > 5000) { + return E_HW_TIMEOUT; + } + + uint16_t chunk = UUSART_DMA_TxQueue(priv, buffer, (uint16_t) len); - uint32_t t_start = HAL_GetTick(); - while (len > 0) { - // this should be long enough even for the slowest bitrates and 512 bytes - if (HAL_GetTick() - t_start > 5000) { - return E_HW_TIMEOUT; - } + buffer += chunk; + len -= chunk; - uint16_t chunk = UUSART_DMA_TxQueue(priv, pld, (uint16_t) len); + // We give up control if there's another thread waiting and this isn't the last cycle + if (len > 0) { + osThreadYield(); + } + } - pld += chunk; - len -= chunk; + return E_SUCCESS; +} - // We give up control if there's another thread waiting and this isn't the last cycle - if (len > 0) { - osThreadYield(); - } - } +error_t UU_USART_WriteSync(Unit *unit, const uint8_t *buffer, uint32_t len) +{ + CHECK_TYPE(unit, &UNIT_USART); + struct priv *priv = unit->data; + + TRY(UU_USART_Write(unit, buffer, len)); + + // Now wait for the last DMA to complete + uint32_t t_start = HAL_GetTick(); + while (priv->tx_dma_busy) { + if (HAL_GetTick() - t_start > 1000) { + return E_HW_TIMEOUT; + } + } + + return E_SUCCESS; +} + +enum PinCmd_ { + CMD_WRITE = 0, + CMD_WRITE_SYNC = 1, +}; + +/** Handle a request message */ +static error_t UUSART_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp) +{ + uint32_t len; + const uint8_t *pld; + switch (command) { + case CMD_WRITE: + pld = pp_tail(pp, &len); + TRY(UU_USART_Write(unit, pld, len)); + return E_SUCCESS; + case CMD_WRITE_SYNC: + pld = pp_tail(pp, &len); + TRY(UU_USART_WriteSync(unit, pld, len)); return E_SUCCESS; - //return E_NOT_IMPLEMENTED; default: return E_UNKNOWN_COMMAND; diff --git a/units/usart/unit_usart.h b/units/usart/unit_usart.h index efa5318..32b363a 100644 --- a/units/usart/unit_usart.h +++ b/units/usart/unit_usart.h @@ -9,4 +9,26 @@ extern const UnitDriver UNIT_USART; +/** + * Write bytes. This function is asynchronous and does not wait for completion. + * It blocks until there's space in the Tx buffer for the data. + * + * @param unit + * @param buffer - bytes to send + * @param len - number of bytes to send + * @return success + */ +error_t UU_USART_Write(Unit *unit, const uint8_t *buffer, uint32_t len); + +/** + * Write bytes. Same like UU_USART_Write(), except it waits for the transmission + * to complete after sending the last data. + * + * @param unit + * @param buffer - bytes to send + * @param len - number of bytes to send + * @return success + */ +error_t UU_USART_WriteSync(Unit *unit, const uint8_t *buffer, uint32_t len); + #endif //GEX_F072_UNIT_USART_H diff --git a/utils/payload_builder.h b/utils/payload_builder.h index 4c24aa2..cd34b91 100644 --- a/utils/payload_builder.h +++ b/utils/payload_builder.h @@ -48,7 +48,7 @@ struct PayloadBuilder_ { // --- initializer helper macros --- /** Start the builder. */ -#define pb_start_e(buf, capacity, bigendian, full_handler) ((PayloadBuilder){buf, buf, (buf)+(capacity), full_handler, bigendian, 1}) +#define pb_start_e(buf, capacity, bigendian, full_handler) ((PayloadBuilder){(uint8_t*)buf, (uint8_t*)buf, (uint8_t*)((buf)+(capacity)), full_handler, bigendian, 1}) /** Start the builder in big-endian mode */ #define pb_start_be(buf, capacity, full_handler) pb_start_e(buf, capacity, 1, full_handler) @@ -67,6 +67,12 @@ struct PayloadBuilder_ { /** Reset the current pointer to start */ #define pb_rewind(pb) do { pb->current = pb->start; } while (0) +/** Finalize the buffer composition and get the size */ +static inline uint8_t *pb_close(PayloadBuilder *pb, uint32_t *lendst) +{ + *lendst = pb_length(pb); + return pb->start; +} /** Write from a buffer */ bool pb_buf(PayloadBuilder *pb, const uint8_t *buf, uint32_t len);