diff --git a/openrtx/include/core/xmodem.h b/openrtx/include/core/xmodem.h
index ae191d97..d6e250a2 100644
--- a/openrtx/include/core/xmodem.h
+++ b/openrtx/include/core/xmodem.h
@@ -28,8 +28,26 @@
extern "C" {
#endif
+/**
+ * Send an XMODEM packet over the serial port.
+ *
+ * @param data: pointer to payload data.
+ * @param size: data size, must be either 128 or 1024 byte.
+ * @param blockNum: packet sequence number.
+ */
void xmodem_sendPacket(const void *data, size_t size, uint8_t blockNum);
+/**
+ * Send data using the XMODEM protocol, blocking function.
+ * Data transfer begins when the start command from the receiving endpoint is
+ * detected.
+ *
+ * @param size: data size.
+ * @param callback: pointer to a callback function in charge of providing data
+ * for the new packets being sent.
+ */
+ssize_t xmodem_sendData(size_t size, int (*callback)(uint8_t *, size_t));
+
#ifdef __cplusplus
}
#endif
diff --git a/openrtx/src/core/xmodem.c b/openrtx/src/core/xmodem.c
index 1928a64f..6f53ca00 100644
--- a/openrtx/src/core/xmodem.c
+++ b/openrtx/src/core/xmodem.c
@@ -18,11 +18,12 @@
* along with this program; if not, see *
***************************************************************************/
-#include
+#include
+#include
+#include
#include
#include
-#include
-#include
+#include
#define SOH (0x01) /* start of 128-byte data packet */
#define STX (0x02) /* start of 1024-byte data packet */
@@ -35,11 +36,29 @@
#define ABT2 (0x61) /* 'a' == 0x61, assume try abort by user typing */
+/**
+ * @internal
+ * Collect a given amount of data from serial port.
+ *
+ * @param ptr: pointer to destination buffer.
+ * @param size: number of bytes to be retrieved.
+ */
+static void waitForData(uint8_t *ptr, size_t size)
+{
+ size_t curSize = 0;
+
+ while(curSize < size)
+ {
+ ssize_t recvd = vcom_readBlock(ptr + curSize, size - curSize);
+ if(recvd >= 0) curSize += recvd;
+ }
+}
+
void xmodem_sendPacket(const void *data, size_t size, uint8_t blockNum)
{
// Bad payload size, null block number or null data pointer: do not send
- if(((size != 128) && (size != 1024)) || (blockNum == 0) || (data == NULL))
+ if(((size != 128) && (size != 1024)) || (data == NULL))
{
return;
}
@@ -66,3 +85,76 @@ void xmodem_sendPacket(const void *data, size_t size, uint8_t blockNum)
buf[1] = crc & 0xFF;
vcom_writeBlock(buf, 2);
}
+
+ssize_t xmodem_sendData(size_t size, ssize_t (*callback)(uint8_t *, size_t))
+{
+ // Wait for the start command from the receiver, only CRC mode is supported.
+ uint8_t cmd = 0;
+ while(cmd != CRC)
+ {
+ waitForData(&cmd, 1);
+ }
+
+ // Send data.
+ uint8_t dataBuf[1024];
+ uint8_t blockNum = 1;
+ size_t sentSize = 0;
+
+ while(sentSize < size)
+ {
+ size_t remaining = size - sentSize;
+ size_t blockSize = 1024;
+ if(remaining < blockSize) blockSize = remaining;
+
+ // Request data, stop transfer on failure
+ if(callback(dataBuf, blockSize) < 0)
+ {
+ cmd = CAN;
+ vcom_writeBlock(&cmd, 1);
+ return -1;
+ }
+
+ // Pad data to 128 or 1024 bytes, if necessary
+ size_t padSize = 0;
+ if(blockSize < 128)
+ {
+ padSize = 128 - blockSize;
+ }
+ else if(blockSize < 1024)
+ {
+ padSize = 1024 - blockSize;
+ }
+
+ uint8_t *ptr = dataBuf + padSize + 1;
+ memset(ptr, 0x1A, padSize);
+
+ // Send packet and wait for ACK, resend on NACK.
+ bool ok = false;
+ do
+ {
+ blockSize += padSize;
+ xmodem_sendPacket(dataBuf, blockSize, blockNum);
+
+ cmd = 0;
+ while((cmd != ACK) && (cmd != NAK))
+ {
+ waitForData(&cmd, 1);
+ if(cmd == ACK) ok = true;
+ }
+ }
+ while(ok == false);
+
+ sentSize += blockSize - padSize;
+ blockNum++;
+ }
+
+ // End of transfer
+ cmd = EOT;
+ vcom_writeBlock(&cmd, 1);
+ while(cmd != ACK)
+ {
+ waitForData(&cmd, 1);
+ }
+
+ return sentSize;
+}
diff --git a/tests/platform/xmodem_flash_dump.c b/tests/platform/xmodem_flash_dump.c
index d72f5d17..1c78662f 100644
--- a/tests/platform/xmodem_flash_dump.c
+++ b/tests/platform/xmodem_flash_dump.c
@@ -24,20 +24,18 @@
#include
#include
#include "W25Qx.h"
-#include "usb_vcom.h"
-
-#define SOH (0x01) /* start of 128-byte data packet */
-#define STX (0x02) /* start of 1024-byte data packet */
-#define EOT (0x04) /* End Of Transmission */
-#define ACK (0x06) /* ACKnowledge, receive OK */
-#define NAK (0x15) /* Negative ACKnowledge, receiver ERROR, retry */
-#define CAN (0x18) /* two CAN in succession will abort transfer */
-#define CRC (0x43) /* 'C' == 0x43, request 16-bit CRC, use in place of first NAK for CRC mode */
-#define ABT1 (0x41) /* 'A' == 0x41, assume try abort by user typing */
-#define ABT2 (0x61) /* 'a' == 0x61, assume try abort by user typing */
static const size_t FLASH_SIZE = 16*1024*1024; /* 16MB */
-uint8_t blockData[1024];
+size_t addr = 0;
+
+int callback(uint8_t *ptr, size_t size)
+{
+ if((addr + size) > FLASH_SIZE) return -1;
+ W25Qx_readData(addr, ptr, size);
+ addr += size;
+ return 0;
+}
+
int main()
{
@@ -45,52 +43,7 @@ int main()
W25Qx_init();
W25Qx_wakeup();
- uint8_t cmd = 0;
- while(cmd != 'C')
- {
- platform_ledOn(GREEN);
- sleepFor(0,50);
- platform_ledOff(GREEN);
- sleepFor(0,50);
- vcom_readBlock(&cmd, 1);
- }
-
-
- uint8_t block = 1;
- for(size_t addr = 0; addr < FLASH_SIZE; )
- {
- W25Qx_readData(addr, blockData, 1024);
-
- bool ok = false;
- do
- {
- xmodem_sendPacket(blockData, 1024, block);
-
- cmd = 0;
- while((cmd != ACK) && (cmd != NAK))
- {
- platform_ledOn(RED);
- sleepFor(0,50);
- platform_ledOff(RED);
- sleepFor(0,50);
- vcom_readBlock(&cmd, 1);
-
- if(cmd == ACK) ok = true;
- }
- }
- while(ok == false);
-
- block++;
- if(block == 255) block = 1;
- addr += 1024;
- }
-
- cmd = EOT;
- vcom_writeBlock(&cmd, 1);
- while(cmd != ACK)
- {
- vcom_readBlock(&cmd, 1);
- }
+ xmodem_sendData(FLASH_SIZE, callback);
while(1)
{