U-Boot 上支援 ZMODEM 接收

For non-Chinese readers, in short, the source code and modification for U-Boot ZModem is here.

前陣子在公司做了一個案子,希望利用 RS422 上進行檔案傳輸,來進行系統更新。至於為什麼不用網路傳呢!?當然是客戶最大,問太多也沒用,總是有他的需求在。

U-Boot 支援現況與ZModem優點

用過 U-Boot 的資深使用者應該知道其支援利用 YModem 與 XModem 進行接收,所以在一般狀況其實用不太到 ZModem,我想這也是其一直沒有支援 ZModem 的原因吧~

根據我模糊的印象,ZModem 支援串流傳輸,而非一般的 stop-and-wait,所以效率會較高。另外,其也支援動態的 block 大小,比起 YModem 的 1024 或 256 block size,會有較好的效率。

實際測試的效果來講,在一般RS232 115200 bps 上,ZModem 與 YModem 速度大致相同,但 ZModem 開始傳輸的反應時間會快蠻多的。在高速應用 RS422 上,當用 5Mbps 傳輸時,ZModem 可達到 200KB/s,而 YModem 僅 150KB/s,算是改善了不少。

ZModem 原始碼套件

支援 ZMODEM 的原始碼,是來自 lrzsz 的這個套件,其實很早就用在 Linux 環境下了。這次因為要將其移植到 U-Boot 下,截取了其 RX 的部份,也順利完成,算是運氣還不錯,沒花太多功夫。甚至移植到 non-OS 的環境,也沒什麼問題。

U-Boot 下 ZModem 移植

ZModem 接收的程式碼,已經從 lrzsz-0.12.20 內截取下來,放在 gitlab 的「zmodem for u-boot」專案裡。裡面的 README.md 裡已有一些說明,這邊就以中文再說明一下。

新增命令

當套用這些變更後, U-Boot 就會新增一個 loadz 的命令,用法跟 loady 或 loadx 是相同的。

檔案複製

將專案下載下來後,將下列檔案複製到 U-Boot 的 common/ 目錄下:

  • config.h
  • error.h
  • lrz.c
  • zglobal.h
  • zm.c
  • zmodem.h
  • zreadline.c
  • rbsb.c

手動套用 diff 檔

有三個檔案需要使用者手動套用差異到現有檔案。

 

cmd/load.c, 加入 ‘loadz’ 命令的辨識,並呼叫 load_serial_zmodem()。

common/Makefile, 將新檔案加入 Makefile 的 compile list。

將 「static int CYGACC_COMM_IF_GETC_TIMEOUT (char chan, char *c)」與「static void CYGACC_COMM_IF_PUTC (char x, char y)」從 static,改成非 static。

 

開始測試

當完成上面的修改後,應該就可以在 U-Boot 上透過 ZModem 來接收檔案。可以使用免費的 Teraterm 來進行測試,或以商業的 SecureCRT。以測試結果來講,SecureCRT 會較快,在一些更快的 Serial Port (如 USB Gadget UART) ,SecureCRT 可達到 3MB/s 而 Teraterm 仍只有數百KB。

移植到其它平台

若需要移植到其它非 U-Boot 的平台上,ex.沒有 OS 的 standalone application (通常是個大 while loop),會需要修改一些跟 UART 傳送接收有關的 function,即單字元的接收與發送之類的。這些修改,都放在 lrz.c 裡面。

void sendline(int c)

將一個字元發送到 uart 去

int read_data(int tout_in_100ms, char *buf, int size)

讀取UART的輸入字元,tout_in_100ms 是沒資料時的 timeout 時間,以100ms為單位。buf 是存放的 buffer pointer,而 size 是 buffer 的大小。返回值代表讀取到的資料個數,返回0則是代表 timeout。目前的程式碼是一次只會讀入一個字元,若不同平台讀到的資料較多,也可以在buffer內一次放入較多資料。

void send_data(int fd, char *buf, int size)

送出一串字元。不修改的話,就是以 sendline() 來組合實現。

void flushmo()

目前沒用過。猜測是有的裝置會有 TX fifo ,呼叫此 function 來確實將資料推送出去。

double timing (int reset, time_t *nowp)

用來計算經過秒數的 function。計算 reset=1 與 reset=0 中間經過的秒數,以 double 為單位,試過返回整數秒也是可以的。

 

結語

ZModem 這些古老的 protocol 在現經真是很少碰到了,所以在 U-Boot 上也遲遲沒有實現,依稀記得在 2009 年就有人在問這個問題了。這麼冷門的知識,希望能幫到看文章的你。

附件

怕以後 gitlab GG,把 gitlab 的檔案放在這裡

Leave a Reply