3 * @author Shinichiro Nakamura
4 * @brief XMODEM for KOZOS.
8 * ===============================================================
11 * ===============================================================
12 * Copyright (c) 2012 Shinichiro Nakamura
14 * Permission is hereby granted, free of charge, to any person
15 * obtaining a copy of this software and associated documentation
16 * files (the "Software"), to deal in the Software without
17 * restriction, including without limitation the rights to use,
18 * copy, modify, merge, publish, distribute, sublicense, and/or
19 * sell copies of the Software, and to permit persons to whom the
20 * Software is furnished to do so, subject to the following
23 * The above copyright notice and this permission notice shall be
24 * included in all copies or substantial portions of the Software.
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
28 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
30 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
31 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
33 * OTHER DEALINGS IN THE SOFTWARE.
34 * ===============================================================
40 #include "kz_xmodem.h"
44 # define SLEEP(SEC) Sleep((SEC) * 1000)
47 # define SLEEP(SEC) sleep((SEC))
50 #define CODE_SOH (0x01)
51 #define CODE_EOT (0x04)
52 #define CODE_ACK (0x06)
53 #define CODE_NAK (0x15)
54 #define CODE_CAN (0x18)
55 #define CODE_EOF (0x1A)
56 #define CODE_EOF (0x1A)
57 #define XMODEM_DATA_BLKSIZ (128)
58 #define XMODEM_SEND_BLKSIZ (3 + XMODEM_DATA_BLKSIZ + 1)
60 #define TIMEOUT_SHORT_MS (10)
61 #define TIMEOUT_LONG_MS (1000)
63 #define NAK_WAIT_SEC (20)
64 #define NAK_WAIT_TIMES ((NAK_WAIT_SEC * 1000) / TIMEOUT_SHORT_MS)
67 * @brief シリアルポートに残留するデータをフラッシュする。
69 * ターゲット側には、接続前に何らかの入力が行なわれている可能性がある。
70 * ホスト側には、ターゲット側からやってきたエコーバック文字列や
71 * プロンプトの表示などが残っているかもしれない。
72 * リターンコードを送ってターゲット側のコマンド解釈層に残っている
75 * @param serial シリアルハンドラ。
79 ErrorCode flush_serial(SERIAL *serial)
81 unsigned char buf[64];
86 if (serial_write(serial, (unsigned char *)"\n", 1) != 0) {
87 return SerialWriteError;
91 * リターンに対するエコーバックとプロンプトを飲み込む。
92 * もしかしたら、シリアルポートドライバが抱えている
93 * バッファリングされたデータも読み出されるかもしれない。
94 * 必ず受信するわけではないので、ここではエラーを見ない。
96 serial_read_with_timeout(serial, buf, sizeof(buf), TIMEOUT_SHORT_MS);
107 * @brief KOZOSブートローダをロード状態に遷移させる。
109 * KOZOSブートローダは、loadというコマンドによってロード状態に遷移する。
111 * @param serial シリアルハンドラ。
115 ErrorCode setup_load_condition(SERIAL *serial)
117 unsigned char buf[64];
122 if (serial_write(serial, (unsigned char *)"load\n", 5) != 0) {
123 return SerialWriteError;
128 * 与えた受信要求長さを満たすわけでないのでエラーを見ない。
130 serial_read_with_timeout(serial, buf, sizeof(buf), TIMEOUT_SHORT_MS);
136 * @brief ターゲットからのNAKコード受信を待つ。
138 * ターゲットからのNAKコードは数秒おきにやってくる事になっている。
139 * あまり長くやってこない時にはターゲット側が期待している状態に
140 * 遷移していないと判定してエラーとする。
142 * @param serial シリアルハンドラ。
146 ErrorCode wait_target_nak(SERIAL *serial)
148 ErrorCode retval = NoError;
153 * タイムアウトを設定して読み込みを行なう。
155 unsigned char c = 0x00;
156 serial_read_with_timeout(serial, &c, 1, TIMEOUT_SHORT_MS);
161 if ((cnt % (1000 / TIMEOUT_SHORT_MS)) == 0) {
162 fprintf(stderr, ".");
174 * タイムアウトとして扱うかどうかを判定する。
177 if (cnt > NAK_WAIT_TIMES) {
178 retval = TargetIllegalState;
182 fprintf(stderr, "\n");
188 * @brief 与えられたバッファのチェックサムを計算する。
190 * @param buf バッファへのポインタ。
191 * @param siz バッファに格納されているデータバイト数。
193 * @return チェックサム計算結果。
195 unsigned char CALC_CHECKSUM(const unsigned char *buf, const int siz)
198 unsigned char cs = 0;
199 for (i = 0; i < siz; i++) {
208 * @param serial シリアルハンドラ。
209 * @param buf バッファへのポインタ。
210 * @param siz バッファに格納されているデータバイト数。
212 * @return ターゲットから受信した結果コード。
214 unsigned char TRANSMIT_BLOCK(
215 SERIAL *serial, const unsigned char *buf, const int siz)
218 serial_write(serial, buf, siz);
219 serial_read_with_timeout(serial, &c, 1, TIMEOUT_LONG_MS);
224 * @brief ファイルをターゲットに送信する。
226 * @param serial シリアルハンドラ。
227 * @param filename ファイル名。
231 ErrorCode transmit_file(SERIAL *serial, const char *filename)
233 ErrorCode retval = NoError;
238 FILE *fp = fopen(filename, "rb");
240 return FileOpenError;
246 if (fseek(fp, 0L, SEEK_END) != 0) {
247 retval = FileSeekError;
250 long file_size = ftell(fp);
251 int block_count = file_size / XMODEM_DATA_BLKSIZ;
252 int block_remainder = file_size % XMODEM_DATA_BLKSIZ;
253 if (fseek(fp, 0L, SEEK_SET) != 0) {
254 retval = FileSeekError;
257 fprintf(stderr, "File(%s): %d blocks + %d bytes\n",
258 filename, block_count, block_remainder);
263 unsigned char block_number = 1;
266 for (i = 0; i < block_count; i++) {
268 unsigned char buf[XMODEM_SEND_BLKSIZ];
270 buf[1] = block_number;
271 buf[2] = ~block_number;
272 if (fread(buf + 3, XMODEM_DATA_BLKSIZ, 1, fp) != 1) {
273 retval = FileReadError;
276 buf[3 + XMODEM_DATA_BLKSIZ] =
277 CALC_CHECKSUM(&buf[3], XMODEM_DATA_BLKSIZ);
279 c = TRANSMIT_BLOCK(serial, buf, sizeof(buf));
280 fprintf(stderr, "%c", (c == CODE_ACK) ? '.' : 'x');
281 } while (c == CODE_NAK);
283 retval = TargetIllegalResponse;
289 if (block_remainder > 0) {
291 unsigned char buf[XMODEM_SEND_BLKSIZ];
293 buf[1] = block_number;
294 buf[2] = ~block_number;
295 memset(buf + 3, CODE_EOF, XMODEM_DATA_BLKSIZ);
296 if (fread(buf + 3, block_remainder, 1, fp) != 1) {
297 retval = FileReadError;
300 buf[3 + XMODEM_DATA_BLKSIZ] =
301 CALC_CHECKSUM(&buf[3], XMODEM_DATA_BLKSIZ);
303 c = TRANSMIT_BLOCK(serial, buf, sizeof(buf));
304 fprintf(stderr, "%c", (c == CODE_ACK) ? '.' : 'x');
305 } while (c == CODE_NAK);
307 retval = TargetIllegalResponse;
313 * 終了コードを送信して結果コードを取得する。
316 unsigned char c = CODE_EOT;
317 serial_write(serial, &c, 1);
318 serial_read_with_timeout(serial, &c, 1, TIMEOUT_LONG_MS);
320 retval = TargetIllegalResponse;
326 fprintf(stderr, "\n");
332 * @brief エラーメッセージを表示する。
336 void ERROR_MESSAGE(ErrorCode ec)
343 case SerialOpenError:
344 txt = "Serial open error.";
346 case SerialWriteError:
347 txt = "Serial write error.";
349 case TargetIllegalState:
350 txt = "Illegal target state found.";
352 case TargetIllegalResponse:
353 txt = "Illegal target response found.";
356 txt = "File open error.";
359 txt = "File seek error.";
362 txt = "File read error.";
365 txt = "Unknown error.";
368 fprintf(stderr, "%s\n", txt);
375 * @param argv 引数のポインタのポインタ。
379 int main(int argc, char **argv)
383 fprintf(stderr, "=================================================\n");
384 fprintf(stderr, " XMODEM for KOZOS H8/3069F (Version %d.%d.%d)\n",
388 fprintf(stderr, " Copyright(C) 2012 Shinichiro Nakamura\n");
389 fprintf(stderr, "=================================================\n");
392 fprintf(stderr, "kz_xmodem [elf file] [interface]\n");
399 SERIAL *serial = serial_open(argv[2], SerialBaud9600);
400 if (serial == NULL) {
401 fprintf(stderr, "Serial open failed.\n");
406 * ホストとターゲットのシリアルポートに滞留しているデータを吐き出す。
408 fprintf(stderr, "Flushing serial port.\n");
409 ec = flush_serial(serial);
415 * バッファリングによってコマンドが行き違いになるのを防ぐ。
417 fprintf(stderr, "Wait.\n");
421 * KOZOSブートローダをload状態に遷移させる。
423 fprintf(stderr, "Setup load condition.\n");
424 ec = setup_load_condition(serial);
432 fprintf(stderr, "Wait a NAK.\n");
433 ec = wait_target_nak(serial);
439 * ELFファイルをターゲットに転送する。
441 fprintf(stderr, "Transmit the target ELF file.\n");
442 ec = transmit_file(serial, argv[1]);
448 * ターゲットからやってくる最後のメッセージを待つ。
450 fprintf(stderr, "Wait a message from the target.\n");
454 * ターゲットからやってくる最後のメッセージを飲み込む。
456 flush_serial(serial);
461 serial_close(serial);
466 fprintf(stderr, "Complete.\n");
473 serial_close(serial);
478 fprintf(stderr, "Error: ");