-
-
Save zonque/0ae2dc8cedbcdbd9b933 to your computer and use it in GitHub Desktop.
/* | |
* Minimalistic implementation of the XModem/YModem protocol suite, including | |
* a compact version of an CRC16 algorithm. The code is just enough to upload | |
* an image to an MCU that bootstraps itself over an UART. | |
* | |
* Copyright (c) 2014 Daniel Mack <[email protected]> | |
* | |
* License: MIT | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <string.h> | |
#include <errno.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <termios.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <sys/mman.h> | |
#define X_STX 0x02 | |
#define X_ACK 0x06 | |
#define X_NAK 0x15 | |
#define X_EOF 0x04 | |
#define min(a, b) ((a) < (b) ? (a) : (b)) | |
struct xmodem_chunk { | |
uint8_t start; | |
uint8_t block; | |
uint8_t block_neg; | |
uint8_t payload[1024]; | |
uint16_t crc; | |
} __attribute__((packed)); | |
#define CRC_POLY 0x1021 | |
static uint16_t crc_update(uint16_t crc_in, int incr) | |
{ | |
uint16_t xor = crc_in >> 15; | |
uint16_t out = crc_in << 1; | |
if (incr) | |
out++; | |
if (xor) | |
out ^= CRC_POLY; | |
return out; | |
} | |
static uint16_t crc16(const uint8_t *data, uint16_t size) | |
{ | |
uint16_t crc, i; | |
for (crc = 0; size > 0; size--, data++) | |
for (i = 0x80; i; i >>= 1) | |
crc = crc_update(crc, *data & i); | |
for (i = 0; i < 16; i++) | |
crc = crc_update(crc, 0); | |
return crc; | |
} | |
static uint16_t swap16(uint16_t in) | |
{ | |
return (in >> 8) | ((in & 0xff) << 8); | |
} | |
enum { | |
PROTOCOL_XMODEM, | |
PROTOCOL_YMODEM, | |
}; | |
static int xymodem_send(int serial_fd, const char *filename, int protocol, int wait) | |
{ | |
size_t len; | |
int ret, fd; | |
uint8_t answer; | |
struct stat stat; | |
const uint8_t *buf; | |
uint8_t eof = X_EOF; | |
struct xmodem_chunk chunk; | |
int skip_payload = 0; | |
fd = open(filename, O_RDONLY); | |
if (fd < 0) { | |
perror("open"); | |
return -errno; | |
} | |
fstat(fd, &stat); | |
len = stat.st_size; | |
buf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); | |
if (!buf) { | |
perror("mmap"); | |
return -errno; | |
} | |
if (wait) { | |
printf("Waiting for receiver ping ..."); | |
fflush(stdout); | |
do { | |
ret = read(serial_fd, &answer, sizeof(answer)); | |
if (ret != sizeof(answer)) { | |
perror("read"); | |
return -errno; | |
} | |
} while (answer != 'C'); | |
printf("done.\n"); | |
} | |
printf("Sending %s ", filename); | |
if (protocol == PROTOCOL_YMODEM) { | |
strncpy((char *) chunk.payload, filename, sizeof(chunk.payload)); | |
chunk.block = 0; | |
skip_payload = 1; | |
} else { | |
chunk.block = 1; | |
} | |
chunk.start = X_STX; | |
while (len) { | |
size_t z = 0; | |
int next = 0; | |
char status; | |
if (!skip_payload) { | |
z = min(len, sizeof(chunk.payload)); | |
memcpy(chunk.payload, buf, z); | |
memset(chunk.payload + z, 0xff, sizeof(chunk.payload) - z); | |
} else { | |
skip_payload = 0; | |
} | |
chunk.crc = swap16(crc16(chunk.payload, sizeof(chunk.payload))); | |
chunk.block_neg = 0xff - chunk.block; | |
ret = write(serial_fd, &chunk, sizeof(chunk)); | |
if (ret != sizeof(chunk)) | |
return -errno; | |
ret = read(serial_fd, &answer, sizeof(answer)); | |
if (ret != sizeof(answer)) | |
return -errno; | |
switch (answer) { | |
case X_NAK: | |
status = 'N'; | |
break; | |
case X_ACK: | |
status = '.'; | |
next = 1; | |
break; | |
default: | |
status = '?'; | |
break; | |
} | |
printf("%c", status); | |
fflush(stdout); | |
if (next) { | |
chunk.block++; | |
len -= z; | |
buf += z; | |
} | |
} | |
ret = write(serial_fd, &eof, sizeof(eof)); | |
if (ret != sizeof(eof)) | |
return -errno; | |
/* send EOT again for YMODEM */ | |
if (protocol == PROTOCOL_YMODEM) { | |
ret = write(serial_fd, &eof, sizeof(eof)); | |
if (ret != sizeof(eof)) | |
return -errno; | |
} | |
printf("done.\n"); | |
return 0; | |
} | |
static int open_serial(const char *path, int baud) | |
{ | |
int fd; | |
struct termios tty; | |
fd = open(path, O_RDWR | O_SYNC); | |
if (fd < 0) { | |
perror("open"); | |
return -errno; | |
} | |
memset(&tty, 0, sizeof(tty)); | |
if (tcgetattr(fd, &tty) != 0) { | |
perror("tcgetattr"); | |
return -errno; | |
} | |
cfsetospeed(&tty, baud); | |
cfsetispeed(&tty, baud); | |
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars | |
tty.c_iflag &= ~IGNBRK; // disable break processing | |
tty.c_lflag = 0; // no signaling chars, no echo, | |
// no canonical processing | |
tty.c_oflag = 0; // no remapping, no delays | |
tty.c_cc[VMIN] = 1; // read doesn't block | |
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout | |
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl | |
tty.c_cflag |= (CLOCAL | CREAD); // ignore modem controls, | |
// enable reading | |
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity | |
tty.c_cflag &= ~CSTOPB; | |
tty.c_cflag &= ~CRTSCTS; | |
if (tcsetattr(fd, TCSANOW, &tty) != 0) { | |
perror("tcsetattr"); | |
return -errno; | |
} | |
return fd; | |
} | |
static void dump_serial(int serial_fd) | |
{ | |
char in; | |
for (;;) { | |
read(serial_fd, &in, sizeof(in)); | |
printf("%c", in); | |
fflush(stdout); | |
} | |
} | |
int main(int argc, char **argv) | |
{ | |
int a, ret, serial_fd; | |
serial_fd = open_serial("/dev/ttyUSB0", 115200); | |
if (serial_fd < 0) | |
return -errno; | |
ret = xymodem_send(serial_fd, argv[1], PROTOCOL_XMODEM, 1); | |
if (ret < 0) | |
return ret; | |
ret = xymodem_send(serial_fd, argv[2], PROTOCOL_YMODEM, 1); | |
if (ret < 0) | |
return ret; | |
sleep(4); | |
ret = xymodem_send(serial_fd, argv[1], PROTOCOL_YMODEM, 0); | |
if (ret < 0) | |
return ret; | |
sleep(3); | |
ret = xymodem_send(serial_fd, argv[2], PROTOCOL_YMODEM, 0); | |
if (ret < 0) | |
return ret; | |
dump_serial(serial_fd); | |
return 0; | |
} |
#include <termios.h>
fatal error: termios.h: No such file or directory
Is there is no termios.h availible to windows 10 plataforms?
Look's like it works fine on linux but need a workaround to windows.
Is there is no termios.h availible to windows 10 plataforms?
Windows is not Linux, even if it pretends to be. Most low-level APIs are missing, and I have no intention to support Windows ;)
I'm going around in circles trying to implement a function.
I think I didn't understand the dynamics of the Y-modem.
Maybe you can help me, my question, it seems simple.
For example, to download a file named TEST.TXT. what sequence of binary data should i transmit through the serial port?
what is should be the arguments if I want to send using sx -k
command and serial setting using stty
command
thanks
@JonnieBoy Thanks for this. I don't currently have a setup to test and fix the missing pieces. If you want, please send me a patch so I can add that here for reference.