OSDN Git Service

Moved the junk codes to junk directory.
[kozos-expbrd/kozos_expbrd.git] / firm / junk / tools / kz_xmodem / kz_xmodem.c
1 /**
2  * @file kz_xmodem.c
3  * @author Shinichiro Nakamura
4  * @brief XMODEM for KOZOS.
5  */
6
7 /*
8  * ===============================================================
9  *  XMODEM for KOZOS
10  *  Version 0.0.2
11  * ===============================================================
12  * Copyright (c) 2012 Shinichiro Nakamura
13  *
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
21  * conditions:
22  *
23  * The above copyright notice and this permission notice shall be
24  * included in all copies or substantial portions of the Software.
25  *
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  * ===============================================================
35  */
36
37 #include <stdio.h>
38 #include <string.h>
39 #include "serial.h"
40 #include "kz_xmodem.h"
41
42 #ifdef WIN32
43 #   include <windows.h>
44 #   define SLEEP(SEC)       Sleep((SEC) * 1000)
45 #else
46 #   include <unistd.h>
47 #   define SLEEP(SEC)       sleep((SEC))
48 #endif
49
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)
59
60 #define TIMEOUT_SHORT_MS    (10)
61 #define TIMEOUT_LONG_MS     (1000)
62
63 #define NAK_WAIT_SEC        (20)
64 #define NAK_WAIT_TIMES      ((NAK_WAIT_SEC * 1000) / TIMEOUT_SHORT_MS)
65
66 /**
67  * @brief シリアルポートに残留するデータをフラッシュする。
68  * @details
69  * ターゲット側には、接続前に何らかの入力が行なわれている可能性がある。
70  * ホスト側には、ターゲット側からやってきたエコーバック文字列や
71  * プロンプトの表示などが残っているかもしれない。
72  * リターンコードを送ってターゲット側のコマンド解釈層に残っている
73  * データをフラッシュする。
74  *
75  * @param serial シリアルハンドラ。
76  *
77  * @return エラーコード。
78  */
79 ErrorCode flush_serial(SERIAL *serial)
80 {
81     unsigned char buf[64];
82
83     /*
84      * リターンコードをターゲットに送信する。
85      */
86     if (serial_write(serial, (unsigned char *)"\n", 1) != 0) {
87         return SerialWriteError;
88     }
89
90     /*
91      * リターンに対するエコーバックとプロンプトを飲み込む。
92      * もしかしたら、シリアルポートドライバが抱えている
93      * バッファリングされたデータも読み出されるかもしれない。
94      * 必ず受信するわけではないので、ここではエラーを見ない。
95      */
96     serial_read_with_timeout(serial, buf, sizeof(buf), TIMEOUT_SHORT_MS);
97
98     /*
99      * どんなものを受信したか知りたい場合、
100      * ここでbufを表示させてみれば良い。
101      */
102
103     return NoError;
104 }
105
106 /**
107  * @brief KOZOSブートローダをロード状態に遷移させる。
108  * @details
109  * KOZOSブートローダは、loadというコマンドによってロード状態に遷移する。
110  *
111  * @param serial シリアルハンドラ。
112  *
113  * @return エラーコード。
114  */
115 ErrorCode setup_load_condition(SERIAL *serial)
116 {
117     unsigned char buf[64];
118
119     /*
120      * loadコマンドを発行する。
121      */
122     if (serial_write(serial, (unsigned char *)"load\n", 5) != 0) {
123         return SerialWriteError;
124     }
125
126     /*
127      * エコーバックを飲み込む。
128      * 与えた受信要求長さを満たすわけでないのでエラーを見ない。
129      */
130     serial_read_with_timeout(serial, buf, sizeof(buf), TIMEOUT_SHORT_MS);
131
132     return NoError;
133 }
134
135 /**
136  * @brief ターゲットからのNAKコード受信を待つ。
137  * @details
138  * ターゲットからのNAKコードは数秒おきにやってくる事になっている。
139  * あまり長くやってこない時にはターゲット側が期待している状態に
140  * 遷移していないと判定してエラーとする。
141  *
142  * @param serial シリアルハンドラ。
143  *
144  * @return エラーコード。
145  */
146 ErrorCode wait_target_nak(SERIAL *serial)
147 {
148     ErrorCode retval = NoError;
149     int cnt = 0;
150
151     while (1) {
152         /*
153          * タイムアウトを設定して読み込みを行なう。
154          */
155         unsigned char c = 0x00;
156         serial_read_with_timeout(serial, &c, 1, TIMEOUT_SHORT_MS);
157
158         /*
159          * ユーザを不安にさせないための出力。
160          */
161         if ((cnt % (1000 / TIMEOUT_SHORT_MS)) == 0) {
162             fprintf(stderr, ".");
163         }
164
165         /*
166          * NAKを受信したら終了。
167          */
168         if (c == CODE_NAK) {
169             retval = NoError;
170             break;
171         }
172
173         /*
174          * タイムアウトとして扱うかどうかを判定する。
175          */
176         cnt++;
177         if (cnt > NAK_WAIT_TIMES) {
178             retval = TargetIllegalState;
179             break;
180         }
181     }
182     fprintf(stderr, "\n");
183
184     return retval;
185 }
186
187 /**
188  * @brief 与えられたバッファのチェックサムを計算する。
189  *
190  * @param buf バッファへのポインタ。
191  * @param siz バッファに格納されているデータバイト数。
192  *
193  * @return チェックサム計算結果。
194  */
195 unsigned char CALC_CHECKSUM(const unsigned char *buf, const int siz)
196 {
197     int i;
198     unsigned char cs = 0;
199     for (i = 0; i < siz; i++) {
200         cs += buf[i];
201     }
202     return cs;
203 }
204
205 /**
206  * @brief 転送を実行する。
207  *
208  * @param serial シリアルハンドラ。
209  * @param buf バッファへのポインタ。
210  * @param siz バッファに格納されているデータバイト数。
211  *
212  * @return ターゲットから受信した結果コード。
213  */
214 unsigned char TRANSMIT_BLOCK(
215         SERIAL *serial, const unsigned char *buf, const int siz)
216 {
217     unsigned char c = 0;
218     serial_write(serial, buf, siz);
219     serial_read_with_timeout(serial, &c, 1, TIMEOUT_LONG_MS);
220     return c;
221 }
222
223 /**
224  * @brief ファイルをターゲットに送信する。
225  *
226  * @param serial シリアルハンドラ。
227  * @param filename ファイル名。
228  *
229  * @return エラーコード。
230  */
231 ErrorCode transmit_file(SERIAL *serial, const char *filename)
232 {
233     ErrorCode retval = NoError;
234
235     /*
236      * ファイルをオープンする。
237      */
238     FILE *fp = fopen(filename, "rb");
239     if (fp == NULL) {
240         return FileOpenError;
241     }
242
243     /*
244      * ファイルの長さを取得する。
245      */
246     if (fseek(fp, 0L, SEEK_END) != 0) {
247         retval = FileSeekError;
248         goto out;
249     }
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;
255         goto out;
256     }
257     fprintf(stderr, "File(%s): %d blocks + %d bytes\n",
258             filename, block_count, block_remainder);
259
260     /*
261      * ブロック転送を開始する。
262      */
263     unsigned char block_number = 1;
264     {
265         int i;
266         for (i = 0; i < block_count; i++) {
267             unsigned char c;
268             unsigned char buf[XMODEM_SEND_BLKSIZ];
269             buf[0] = CODE_SOH;
270             buf[1] = block_number;
271             buf[2] = ~block_number;
272             if (fread(buf + 3, XMODEM_DATA_BLKSIZ, 1, fp) != 1) {
273                 retval = FileReadError;
274                 goto out;
275             }
276             buf[3 + XMODEM_DATA_BLKSIZ] =
277                 CALC_CHECKSUM(&buf[3], XMODEM_DATA_BLKSIZ);
278             do {
279                 c = TRANSMIT_BLOCK(serial, buf, sizeof(buf));
280                 fprintf(stderr, "%c", (c == CODE_ACK) ? '.' : 'x');
281             } while (c == CODE_NAK);
282             if (c != CODE_ACK) {
283                 retval = TargetIllegalResponse;
284                 goto out;
285             }
286             block_number++;
287         }
288     }
289     if (block_remainder > 0) {
290         unsigned char c;
291         unsigned char buf[XMODEM_SEND_BLKSIZ];
292         buf[0] = CODE_SOH;
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;
298             goto out;
299         }
300         buf[3 + XMODEM_DATA_BLKSIZ] =
301             CALC_CHECKSUM(&buf[3], XMODEM_DATA_BLKSIZ);
302         do {
303             c = TRANSMIT_BLOCK(serial, buf, sizeof(buf));
304             fprintf(stderr, "%c", (c == CODE_ACK) ? '.' : 'x');
305         } while (c == CODE_NAK);
306         if (c != CODE_ACK) {
307             retval = TargetIllegalResponse;
308             goto out;
309         }
310     }
311
312     /*
313      * 終了コードを送信して結果コードを取得する。
314      */
315     {
316         unsigned char c = CODE_EOT;
317         serial_write(serial, &c, 1);
318         serial_read_with_timeout(serial, &c, 1, TIMEOUT_LONG_MS);
319         if (c != CODE_ACK) {
320             retval = TargetIllegalResponse;
321             goto out;
322         }
323     }
324
325 out:
326     fprintf(stderr, "\n");
327     fclose(fp);
328     return retval;
329 }
330
331 /**
332  * @brief エラーメッセージを表示する。
333  *
334  * @param ec エラーコード。
335  */
336 void ERROR_MESSAGE(ErrorCode ec)
337 {
338     char *txt;
339     switch (ec) {
340         case NoError:
341             txt = "No error.";
342             break;
343         case SerialOpenError:
344             txt = "Serial open error.";
345             break;
346         case SerialWriteError:
347             txt = "Serial write error.";
348             break;
349         case TargetIllegalState:
350             txt = "Illegal target state found.";
351             break;
352         case TargetIllegalResponse:
353             txt = "Illegal target response found.";
354             break;
355         case FileOpenError:
356             txt = "File open error.";
357             break;
358         case FileSeekError:
359             txt = "File seek error.";
360             break;
361         case FileReadError:
362             txt = "File read error.";
363             break;
364         default:
365             txt = "Unknown error.";
366             break;
367     }
368     fprintf(stderr, "%s\n", txt);
369 }
370
371 /**
372  * @brief エントリポイント。
373  *
374  * @param argc 引数の数。
375  * @param argv 引数のポインタのポインタ。
376  *
377  * @return シェルに返す値。
378  */
379 int main(int argc, char **argv)
380 {
381     ErrorCode ec;
382
383     fprintf(stderr, "=================================================\n");
384     fprintf(stderr, " XMODEM for KOZOS H8/3069F (Version %d.%d.%d)\n",
385             VERSION_MAJOR,
386             VERSION_MINOR,
387             VERSION_RELEASE);
388     fprintf(stderr, " Copyright(C) 2012 Shinichiro Nakamura\n");
389     fprintf(stderr, "=================================================\n");
390
391     if (argc != 3) {
392         fprintf(stderr, "kz_xmodem [elf file] [interface]\n");
393         return 1;
394     }
395
396     /*
397      * シリアルをオープンする。
398      */
399     SERIAL *serial = serial_open(argv[2], SerialBaud9600);
400     if (serial == NULL) {
401         fprintf(stderr, "Serial open failed.\n");
402         return 2;
403     }
404
405     /*
406      * ホストとターゲットのシリアルポートに滞留しているデータを吐き出す。
407      */
408     fprintf(stderr, "Flushing serial port.\n");
409     ec = flush_serial(serial);
410     if (ec != NoError) {
411         goto failure;
412     }
413
414     /*
415      * バッファリングによってコマンドが行き違いになるのを防ぐ。
416      */
417     fprintf(stderr, "Wait.\n");
418     SLEEP(1);
419
420     /*
421      * KOZOSブートローダをload状態に遷移させる。
422      */
423     fprintf(stderr, "Setup load condition.\n");
424     ec = setup_load_condition(serial);
425     if (ec != NoError) {
426         goto failure;
427     }
428
429     /*
430      * ターゲットからのNAK着信を待つ。
431      */
432     fprintf(stderr, "Wait a NAK.\n");
433     ec = wait_target_nak(serial);
434     if (ec != NoError) {
435         goto failure;
436     }
437
438     /*
439      * ELFファイルをターゲットに転送する。
440      */
441     fprintf(stderr, "Transmit the target ELF file.\n");
442     ec = transmit_file(serial, argv[1]);
443     if (ec != NoError) {
444         goto failure;
445     }
446
447     /*
448      * ターゲットからやってくる最後のメッセージを待つ。
449      */
450     fprintf(stderr, "Wait a message from the target.\n");
451     SLEEP(1);
452
453     /*
454      * ターゲットからやってくる最後のメッセージを飲み込む。
455      */
456     flush_serial(serial);
457
458     /*
459      * シリアルポートを閉じる。
460      */
461     serial_close(serial);
462
463     /*
464      * 正常終了を示す文字列を出力して終了。
465      */
466     fprintf(stderr, "Complete.\n");
467     return (int)NoError;
468
469 failure:
470     /*
471      * シリアルポートを閉じる。
472      */
473     serial_close(serial);
474
475     /*
476      * エラーメッセージを出力して終了。
477      */
478     fprintf(stderr, "Error: ");
479     ERROR_MESSAGE(ec);
480     return (int)ec;
481 }
482