diff --git a/openrtx/include/core/xmodem.h b/openrtx/include/core/xmodem.h index d6e250a2..0ab6609e 100644 --- a/openrtx/include/core/xmodem.h +++ b/openrtx/include/core/xmodem.h @@ -37,6 +37,15 @@ extern "C" { */ void xmodem_sendPacket(const void *data, size_t size, uint8_t blockNum); +/** + * Receive an XMODEM packet from the serial port. + * + * @param data: pointer to a buffer for payload data. + * @param expectedBlockNum: expected block number, for sanity check. + * @return number of bytes received or zero in case of errors. + */ +size_t xmodem_receivePacket(void *data, uint8_t expectedBlockNum); + /** * Send data using the XMODEM protocol, blocking function. * Data transfer begins when the start command from the receiving endpoint is @@ -45,9 +54,20 @@ void xmodem_sendPacket(const void *data, size_t size, uint8_t blockNum); * @param size: data size. * @param callback: pointer to a callback function in charge of providing data * for the new packets being sent. + * @return number of bytes sent. */ ssize_t xmodem_sendData(size_t size, int (*callback)(uint8_t *, size_t)); +/** + * Receive data using the XMODEM protocol, blocking function. + * Transfer starts immediately when this function is called. + * + * @param size: expected data size, in bytes. + * @param callback: callback function invoked when a new data block is recevied. + * @return number of bytes received. + */ +ssize_t xmodem_receiveData(size_t size, void (*callback)(uint8_t *, size_t)); + #ifdef __cplusplus } #endif diff --git a/openrtx/src/core/xmodem.c b/openrtx/src/core/xmodem.c index 6f53ca00..c2fdbcb4 100644 --- a/openrtx/src/core/xmodem.c +++ b/openrtx/src/core/xmodem.c @@ -25,16 +25,15 @@ #include #include -#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 */ - +#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 /** * @internal @@ -86,6 +85,39 @@ void xmodem_sendPacket(const void *data, size_t size, uint8_t blockNum) vcom_writeBlock(buf, 2); } +size_t xmodem_receivePacket(void* data, uint8_t expectedBlockNum) +{ + // Get first byte + uint8_t status = 0; + while((status != STX) && (status != SOH)) + { + waitForData(&status, 1); + } + + // Get sequence number + uint8_t seq[2] = {0}; + waitForData(seq, 2); + + // Determine payload size and get data + size_t blockSize = 128; + if(status == STX) blockSize = 1024; + waitForData(((uint8_t *) data), blockSize); + + // Get CRC + uint8_t crc[2] = {0}; + waitForData(crc, 2); + + // First sanity check: sequence number + if((seq[0] ^ seq[1]) != 0xFF) return 0; + if(expectedBlockNum != seq[0]) return 0; + + // Second sanity check: CRC + uint16_t dataCrc = crc_ccitt(data, blockSize); + if((crc[0] != (dataCrc >> 8)) || (crc[1] != (dataCrc & 0xFF))) return 0; + + return blockSize; +} + 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. @@ -158,3 +190,52 @@ ssize_t xmodem_sendData(size_t size, ssize_t (*callback)(uint8_t *, size_t)) return sentSize; } + +ssize_t xmodem_receiveData(size_t size, void (*callback)(uint8_t *, size_t)) +{ + uint8_t dataBuf[1024]; + uint8_t command = 0; + uint8_t blockNum = 1; + size_t rcvdSize = 0; + + // Request data transfer in CRC mode + command = CRC; + vcom_writeBlock(&command, 1); + + while(rcvdSize < size) + { + size_t blockSize = xmodem_receivePacket(dataBuf, blockNum); + if(blockSize == 0) + { + // Bad packet, send NACK + command = NAK; + } + else + { + // New data arrived + size_t delta = size - rcvdSize; + if(blockSize < delta) delta = blockSize; + callback(dataBuf, delta); + + rcvdSize += delta; + blockNum++; + + // ACK and go on + command = ACK; + } + + vcom_writeBlock(&command, 1); + } + + // Wait for EOT from the sender, ACK and return + uint8_t status = 0; + while(status != EOT) + { + waitForData(&status, 1); + } + + command = ACK; + vcom_writeBlock(&command, 1); + + return rcvdSize; +}