forked from mist-devel/mist-firmware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
xmodem.c
262 lines (221 loc) · 5.84 KB
/
xmodem.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
// 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.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[1024];
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 fileTYPE 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
*AT91C_RSTC_RCR = 0xA5 << 24 | AT91C_RSTC_PERRST | AT91C_RSTC_PROCRST; // restart
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(!FileNew(&file, filename, filelen)) {
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(!FileOpen(&file, filename))
iprintf("UPLOAD: File open failed\n");
else
data_io_file_tx(&file, 1);
}
}
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(!FileWrite(&file, sector_buffer))
iprintf("XMODEM: write failed\n");
// close file
// end writing file, so cluster chain may be trimmed
if(!FileWriteEnd(&file))
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(!FileWrite(&file, sector_buffer))
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;
}
}