1
0
mirror of https://github.com/mist-devel/mist-firmware.git synced 2026-01-11 23:43:04 +00:00
2021-07-10 22:31:46 +02:00

263 lines
5.9 KiB
C

// stty -F /dev/ttyUSB0 speed 115200 raw -echo
// interceptty -o log -l /dev/ttyUSB0
// cat /dev/pts/5
// timeout 5s cat /dev/pts/5; echo 'xtest.img' > /dev/pts/5 ; sx ymodem.h < /dev/pts/5 > /dev/pts/5
//
//
//
//
#include <stdio.h>
#include <string.h>
#include "debug.h"
#include "xmodem.h"
#include "hardware.h"
#include "fat_compat.h"
#include "user_io.h"
#include "data_io.h"
typedef enum { IDLE, X_NAME, EXP_SOH1, EXP_SOH, BLKNO, DATA, CHK, U_NAME } state_t;
extern unsigned char sector_buffer[SECTOR_BUFFER_SIZE];
static state_t state = IDLE;
static unsigned char block;
static unsigned char chk;
static unsigned int count;
static unsigned long timer;
static char filename[11]; // a 8+3 filename
static unsigned long filelen;
static FIL file;
static unsigned char *sector_ptr;
static unsigned short sector_count;
#define SOH 0x01
#define EOT 0x04
#define ACK 0x06
#define NAK 0x15
#define CAN 0x18
// this function is frequently called in debug mode (dip1 on)
void xmodem_poll(void) {
// USART_Write(unsigned char c)
if(state == EXP_SOH1) {
if(CheckTimer(timer)) {
// send NAK max ten seconds
if(++count == 10) {
iprintf("XMODEM start expired\n");
state = IDLE;
} else {
USART_Write(NAK);
timer = GetTimer(1000);
}
}
} else if(state != IDLE) {
if(CheckTimer(timer)) {
iprintf("XMODEM timeout\n");
state = IDLE;
}
}
}
void xmodem_rx_byte(unsigned char byte) {
switch(state) {
// idle state
case IDLE:
if((byte == 'r') || (byte == 'R')) { // _R_eset
MCUReset();
for(;;);
}
// character x starts xmodem transfer
if((byte == 'x') || (byte == 'X') || // _X_modem
(byte == 'u') || (byte == 'U')) { // _U_pload
timer = GetTimer(2000);
filename[0] = 0; // no valid filename yet
filelen = 0;
// now expect either x+return or a filename
state = ((byte == 'x') || (byte == 'X'))?X_NAME:U_NAME;
count = 0;
}
break;
case X_NAME:
case U_NAME:
if((byte == '\r')||(byte == '\n')) {
// return starts x/xmodem
if(state == X_NAME) {
// start xmodem only if filename and file length were given
if(filename[0] && filelen) {
if(FileOpenCompat(&file, filename, FA_READ | FA_WRITE | FA_OPEN_ALWAYS) != FR_OK) {
iprintf("XMODEM: file creation failed\n");
state = IDLE;
} else {
// start asking for file
timer = GetTimer(1);
count = 0;
state = EXP_SOH1;
block = 1; // first data block is 1
sector_ptr = sector_buffer;
sector_count = 0;
}
} else {
iprintf("XMODEM: No file name and/or file lenght given\n");
state = IDLE;
}
} else {
if(user_io_core_type() != CORE_TYPE_8BIT)
iprintf("UPLOAD: Only supported by 8 bit cores\n");
else {
char *p = user_io_8bit_get_string(0);
if(!filename[0] || !p || strncmp(p, filename+8, 3) != 0)
iprintf("UPLOAD: Core reports file type '%s', but given was '%.3s'\n", p, filename+8);
else {
if(FileOpenCompat(&file, filename, FA_READ | FA_WRITE))
iprintf("UPLOAD: File open failed\n");
else
data_io_file_tx(&file, 1, 0);
}
}
state = IDLE;
}
} else {
timer = GetTimer(2000);
// max 8+3 filename allowed
if(count < 11) {
// convert to upper case
if((byte >= 'a') && (byte <= 'z'))
byte = byte - 'a' + 'A';
// only A-Z/0-9 and _ accpepted
if(((byte >= 'A')&&(byte <= 'Z')) ||
((byte >= '0')&&(byte <= '9')) ||
(byte == '_')) {
filename[count++] = byte;
}
// jump to extension if '.' was seen
if((byte == '.') && (count < 8))
while(count < 8)
filename[count++] = ' ';
// ignore spaces before filename and end file
// name parsing else
if((byte == ' ') && (count > 0)) {
// fill 8+3 filename to 11 chars
if(filename[0])
while(count < 11)
filename[count++] = ' ';
}
} else {
// parsing file length
if((byte >= '0')&&(byte <= '9'))
filelen = (filelen * 10) + (byte - '0');
}
}
break;
// waiting for start of header SOH
case EXP_SOH1: // send NAK's while waiting for block 1
case EXP_SOH: // don't send NAK's while waiting for other blocks
if(byte == SOH) {
timer = GetTimer(1000); // 1 sec timeout
state = BLKNO; // expect block no
count = 0;
} else if(byte == EOT) {
USART_Write(ACK);
state = IDLE;
// partially filled sector in buffer?
if(sector_count)
if(FileWriteBlock(&file, sector_buffer) != FR_OK)
iprintf("XMODEM: write failed\n");
// close file
// end writing file, so cluster chain may be trimmed
if(f_close(&file) != FR_OK)
iprintf("XMODEM: End chain failed\n");
}
break;
// waiting for block no
case BLKNO:
// first byte = block no
if((count == 0) && (block == byte)) {
timer = GetTimer(1000); // 1 sec timeout
count = 1;
}
// second byte = inverted block no
else if((count == 1) && (block == 0xff^byte)) {
timer = GetTimer(1000); // 1 sec timeout
count = 0;
state = DATA;
chk = 0;
}
else {
USART_Write(NAK);
state = EXP_SOH;
count = 0;
}
break;
// rx data
case DATA:
timer = GetTimer(1000);
*sector_ptr++ = byte;
chk += byte;
if(++count == 128)
state = CHK;
if(filelen && (++sector_count == 512)) {
if(FileWriteBlock(&file, sector_buffer) != FR_OK)
iprintf("XMODEM: write failed\n");
// still more than 512 bytes expected?
if(filelen > 512) {
filelen -= 512;
// if(!FileNextSectorExpand(&file))
// iprintf("XMODEM: File next sector failed\n");
// } else {
// filelen = 0;
}
sector_count = 0;
sector_ptr = sector_buffer;
}
break;
// rx chk
case CHK:
timer = GetTimer(1000);
if(chk == byte) {
USART_Write(ACK);
block++;
} else
USART_Write(NAK);
state = EXP_SOH;
count = 0;
break;
default:
break;
}
}