The Linux Serial Programming HOWTO by Peter H. Baumann, Peter.Baumann@dlr.de v1.0, 22 January 1998 藤原輝嘉, fujiwara@linux.or.jp 26 February 1998 この文書では,Linux マシンのシリアルポートを使った通信プログラムの書き 方を説明します. ______________________________________________________________________ 目次 1. はじめに 1.1 著作権について 1.2 本文書の最新版の入手方法 1.3 フィードバック 2. はじめてみましょう 2.1 デバッグの方法 2.2 ポートの設定 2.3 シリアルデバイスにおける入力の概念 2.3.1 カノニカル入力処理 2.3.2 非カノニカル入力処理 2.3.3 非同期入力 2.3.4 複数入力からの入力待ち 3. プログラム例 3.1 カノニカル入力処理 3.2 非カノニカル入力処理 3.3 非同期入力 3.4 複数の入力からの入力待ち 4. 他の情報源 5. コントリビューション ______________________________________________________________________ 1. はじめに 本文書は Linux Serial Programming HOWTO です.Linux のシリアルポートを 使って,他のデバイス/コンピュータと通信するプログラムの書き方を解説し ます.カノニカル I/O(行単位での送受信),非同期 I/O,複数ソースからの入 力待ちの,異なるタイプの手法を解説します. 本文書では,シリアルポートの設定方法は説明しません.こちらについては, Greg Hankins 氏の書かれた Serial-HOWTO をご覧ください. 私はシリアル通信の分野の専門家ではありませんが,シリアル通信に関わるプ ロジェクトで問題を解決してきました.本文書で紹介するサンプルコードは, LDP プログラマーズガイド (ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers- guide/lpg-0.4.tar.gz 及びミラーサイト)の 'examples' ディレクトリから入 手することができる miniterm に由来するものです. 1997年の6月にこの文書を書いてから,私は顧客の都合により WindowsNT を使 うようになり,関連知識を深めることができませんでした.本文書に反映させ ますので,コメントがあれば是非送ってください(フィードバックの章を参 照).仕事を引き継いでよりよいものにしてくださる方がいれば,メールで知 らせてください. 全ての例題のテストは,i386 ベースの Linux 2.0.29 で行いました. 1.1. 著作権について Linux Serial-Programming-HOWTOはPeter Baumann の著作物です(copyright (C) 1997).Linux HOWTO 文書は,この著作権表示を改変しない限り,文書の 一部あるいは全体を,任意の電子的・物理的メディアへ複製及び配布すること ができます.商業的配布は認められており,また歓迎しています.しかし,こ のような配布の場合には,著者への連絡があることを希望します. 翻訳,派生物,Linux HOWTO 文書を含む編集物はこの著作権表示に従わなけれ ばなりません.つまり,HOWTO から派生した成果物を作り,これに制限を追加 して配布することはできません.ただし,一定の条件の下でこの規則に例外を 認めることができる場合があります.以下にアドレスを示す Linux HOWTO の 世話役に相談してください. 簡単に言えば,我々はこの情報をできるだけ多くの経路で広めたいと思ってい ます.しかし,HOWTO 文書の著作権は保持されることと,HOWTO を再配布する 全ての方法をできれば知らせてもらえることを希望しています. 疑問点があれば,Linux HOWTO の世話役である gregh@sunsite.unc.edu 氏に 電子メールで相談してください. 1.2. 本文書の最新版の入手方法 Serial-Programming-HOWTO の最新版は ftp://sunsite.unc.edu:/pub/Linux/docs/HOWTO/Serial-Programming-HOWTO 及び,そのミラーサイトで入手できます.PostScript や DVI 等の形式の文書 はother-formatsディレクトリにあります.また, http://sunsite.unc.edu/LDP/HOWTO/Serial-Programming-HOWTO.html にもあ りますし,月に一度comp.os.linux.answersニュースグループにも投稿されま す. 1.3. フィードバック 訂正,質問,コメント,提案,追加事項などがあれば,是非筆者にお知らせく ださい.この文書をより良いものにしたいと思っています.よく分からないと ころや,もっと分かりやすくできる部分を知らせてください.筆者に連絡する にはPeter.Baumann@dlr.deへ電子メールを出してください.電子メールを書く 際には,読んだ Serial-Programming-HOWTO のバージョン(この文書はバー ジョン1.0です)も書いてください. 訳注: 日本語訳の最新版はJF プロジェクトホームページ で入手できます.ま た,誤訳があれば訳者の方へメールを出してください. 2. はじめてみましょう 2.1. デバッグの方法 あなたが書いたコードをデバッグするための最も良い方法は,Linux マシンを もう一台用意し,二台のコンピュータをヌルモデムケーブルで接続することで す.miniterm(LDP プログラマーズガイド (ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers- guide/lpg-0.4.tar.gz の'examples'ディレクトリにあります)を使って,あな たの Linux マシンへ文字を送ってみましょう.miniterm は非常に簡単にコン パイルでき,キーボード入力をシリアルポート経由でそのまま(raw)送りま す.ただ,define 文 #define MODEMDEVICE "/dev/ttyS0"の部分だけはチェッ クしなくてはなりません.この部分は,ケーブルで接続するのが COM1 であれ ば ttyS0,COM2であればttyS1のように設定します.このテストで大事な点 は,全ての文字はそのまま(raw, 出力処理無しに)送信されることです.接続 テストをするときは,両方のマシンで miniterm を実行し,キーボードを叩く だけです.片方のマシンで入力された文字は,もう一方のマシンで出力されま す.その逆も同じです.入力した文字は,入力した方のマシンの画面にはエ コーされません. ヌルモデムケーブルを自作するには,TxD(送信)とRxD(受信)の結線をクロスさ せます.ケーブルについての詳しい解説は,Serial-HOWTO の第7章にありま す. 未使用のシリアルポートが2つあるのならば,一台のコンピュータでこのテス トを実行することもできます.この場合は,2つの仮想コンソールでそれぞれ miniterm を実行します.シリアルマウスを外してシリアルポートを空ける場 合には,/dev/mouseを書換えることを忘れないようにしましょう.マルチポー トシリアルカードを使っている場合には,設定が正しいことを確認しましょ う.筆者は自分のマシンで間違った設定をしていたために,自分のマシンでテ ストした時だけうまく動作するという事態になったことがあります.この設定 で他のコンピュータに接続した時には,文字が消えてしまいました. 2つのプ ログラムを1つのコンピュータで動かすことは,完全に非同期とは言えませ ん. 2.2. ポートの設定 /dev/ttyS*というデバイスは端末を Linux マシンに繋ぐためのもので,通信 を始めた後は端末に合わせた設定が行われます.この点は raw デバイスを 使って通信するプログラムを行う場合には,注意しておかなければなりまん. 例えば,送ったキャラクタをエコーバックさせるようにシリアルポートを設定 されていますが,通常データ送信のためにはこの設定は変更しなければいけま せん. 全てのパラメータはプログラム内で簡単に設定できます.設定はヘッダファイ ルで定義されている termios 構造体に保存されます. #define NCCS 19 struct termios { tcflag_t c_iflag; /* input mode flags */ tcflag_t c_oflag; /* output mode flags */ tcflag_t c_cflag; /* control mode flags */ tcflag_t c_lflag; /* local mode flags */ cc_t c_line; /* line discipline */ cc_t c_cc[NCCS]; /* control characters */ }; この ファイルには,フラグの定義も全て記述されていま す.c_iflag フラグには入力モードのフラグがいくつか含まれており,全ての 入力処理を指定します.入力処理とは,デバイスから送られたキャラクタ は,readで読み出される前に処理することができるということです.同様 に,c_oflagは出力処理を扱います. c_cflagは,ボーレートや文字毎のビッ ト数,ストップビットなどのポート設定を記録しています.c_lflag 内のロー カルモードフラグは,文字がエコーされるかどうか,プログラムにシグナルが 送られるかどうか等を指定します.最後に,配列 c_cc では,ファイル終 端,stop 等の制御文字を定義します.制御文字のデフォルト値は で定義されています.各フラグについては,オンラインマ ニュアルの termios(3) に説明があります.構造体 termiosは POSIX 準拠の システムでは用いられない, c_line要素を含んでいます. 2.3. シリアルデバイスにおける入力の概念 ここでは,3つの異なる種類の入力の概念を説明します.利用目的に従って, 適切なものを選択してください.文字列全体を取得するのに,1文字読み込み のループを使うことはできる限り避けるべきです.私はこれをやったとき,読 み込み時に read が全くエラーを出力しなかったのにもかかわらず,文字が欠 けてしまったことがありました. 2.3.1. カノニカル入力処理 これは端末に対しての通常の処理モードですが,他のデバイスとの通信の時に も便利です.全ての入力は行単位で処理されます.つまり,read は入力の1行 全体のみを返してきます.デフォルトでは,行はNL(ASCII のLF),ファイル終 端,行終端文字のいずれかで終ります.標準の設定では,CR (DOS/Windows の デフォルトの行終端文字)は行の終端とはなりません. カノニカル入力処理では,消去(erase),単語の削除(delete word),文字の再 出力(reprint characters),CRのNLへの変換などを扱うことができます. 2.3.2. 非カノニカル入力処理 非カノニカル入力処理は,read 毎に決まった数の文字を扱う方法で,キャラ クタタイマを利用することもできます.このモードはアプリケーションが決 まった文字数のキャラクタを読み込む時や,接続したデバイスが大量の文字を 送ってくる場合に使用します. 2.3.3. 非同期入力 上記2つのモードは,同期及び非同期モードで使うことができます.デフォル トは,入力がうまくいくまで read 文がブロックされる同期モードです.非同 期モードでは,read 文は即座に終了し,後で読み込みが完了した時にプログ ラムにシグナルが送られます.このシグナルは,シグナルハンドラを使って受 け取ります. 2.3.4. 複数入力からの入力待ち これは別の入力モードというわけではありませんが,複数のデバイスを扱う時 に便利です.筆者のアプリケーションでは,TCP/IP ソケット経由の入力と他 のコンピュータからのシリアル接続経由の入力を擬似的に同時に扱っていま す.以下に示すプログラム例では,異なる2つの入力ソースからの入力待ちを 行います.片方のソースからの入力が可能になると,その入力が処理され,プ ログラムは次の入力を待ちます. 以下に示すアプローチはちょっと複雑に見えますが,Linux はマルチプロセッ シング OS であることを忘れてはいけません.selectシステムコールは入力待 ちの間は CPU に負荷を与えませんが,ループを使った入力待ちを行うと,同 時に実行されている他のプロセスが遅くなってしまいます. 3. プログラム例 全ての例は miniterm.c から取り出したものです.先行入力のバッファは 255 文字に制限されています.これは,カノニカル入力処理の最大文字長と同じで す ( あるいは を参照). それぞれの入力モードの使い方についてはプログラム中のコメントを見てくだ さい.プログラムは十分読みこなせると思います.カノニカル入力モードの例 には一番詳しいコメントを付けています.違いを強調するために,他の例につ いてはカノニカル入力モードとの相違点だけを書いています. 説明は完全ではありませんが,勇気を出して例題で実験し,あなたの目的に最 も適した解を見つけだしてください. 使用するシリアルポートには,適切なパーミッションを与えるのを忘れないで ください(例: chmod a+rw /dev/ttyS1). 3.1. カノニカル入力処理 #include #include #include #include #include /* で定義されているボーレートの設定.これは からインクルードされる. */ #define BAUDRATE B38400 /* 適切なシリアルポートを指すように,この定義を変更する.*/ #define MODEMDEVICE "/dev/ttyS1" #define _POSIX_SOURCE 1 /* POSIX 準拠のソース */ #define FALSE 0 #define TRUE 1 volatile int STOP=FALSE; main() { int fd,c, res; struct termios oldtio,newtio; char buf[255]; /* 読み書きのためにモデムデバイスをオープンする.ノイズによって CTRL-C がたまたま発生しても接続が切れないように,tty 制御はしない. */ fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); if (fd <0) {perror(MODEMDEVICE); exit(-1); } tcgetattr(fd,&oldtio); /* 現在のシリアルポートの設定を待避させる*/ bzero(&newtio, sizeof(newtio)); /* 新しいポートの設定の構造体をクリアする */ /* BAUDRATE: ボーレートの設定.cfsetispeed と cfsetospeed も使用できる. CRTSCTS : 出力のハードウェアフロー制御 (必要な結線が全てされているケー ブルを使う場合のみ.Serial-HOWTO の7章を参照のこと) CS8 : 8n1 (8 ビット,ノンパリティ,ストップビット 1) CLOCAL : ローカル接続,モデム制御なし CREAD : 受信文字(receiving characters)を有効にする. */ newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; /* IGNPAR : パリティエラーのデータは無視する ICRNL : CR を NL に対応させる(これを行わないと,他のコンピュータで CR を入力しても,入力が終りにならない) それ以外の設定では,デバイスは raw モードである(他の入力処理は行わない) */ newtio.c_iflag = IGNPAR | ICRNL; /* Raw モードでの出力 */ newtio.c_oflag = 0; /* ICANON : カノニカル入力を有効にする 全てのエコーを無効にし,プログラムに対してシグナルは送らせない */ newtio.c_lflag = ICANON; /* 全ての制御文字を初期化する デフォルト値は /usr/include/termios.h を見れば分かるが,コメントに書 いてあるので,ここでは見る必要はない. */ newtio.c_cc[VINTR] = 0; /* Ctrl-c */ newtio.c_cc[VQUIT] = 0; /* Ctrl-\ */ newtio.c_cc[VERASE] = 0; /* del */ newtio.c_cc[VKILL] = 0; /* @ */ newtio.c_cc[VEOF] = 4; /* Ctrl-d */ newtio.c_cc[VTIME] = 0; /* キャラクタ間タイマを使わない */ newtio.c_cc[VMIN] = 1; /* 1文字来るまで,読み込みをブロックする */ newtio.c_cc[VSWTC] = 0; /* '\0' */ newtio.c_cc[VSTART] = 0; /* Ctrl-q */ newtio.c_cc[VSTOP] = 0; /* Ctrl-s */ newtio.c_cc[VSUSP] = 0; /* Ctrl-z */ newtio.c_cc[VEOL] = 0; /* '\0' */ newtio.c_cc[VREPRINT] = 0; /* Ctrl-r */ newtio.c_cc[VDISCARD] = 0; /* Ctrl-u */ newtio.c_cc[VWERASE] = 0; /* Ctrl-w */ newtio.c_cc[VLNEXT] = 0; /* Ctrl-v */ newtio.c_cc[VEOL2] = 0; /* '\0' */ /* モデムラインをクリアし,ポートの設定を有効にする */ tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&newtio); /* 端末の設定終了.入力を処理するできるようになった. 例では,行の先頭に 'z' を入力することでプログラムを終了させる */ while (STOP==FALSE) { /* 終了条件が満たされるまでループ */ /* 255文字以上入力された場合でも,行終端文字が入力されるまでは,プロ グラムの実行は read でブロックされる.読み込んだ文字数が,実際に入 力されている文字数より少ない場合には,次回の read で残りの文字が読 み込まれる.変数 res には実際に読み込まれた文字数がセットされる. */ res = read(fd,buf,255); buf[res]=0; /* printf で使うため,文字列の終端をセットする */ printf(":%s:%d\n", buf, res); if (buf[0]=='z') STOP=TRUE; } /* ポートの設定をプログラム開始時のものに戻す */ tcsetattr(fd,TCSANOW,&oldtio); } 3.2. 非カノニカル入力処理 非カノニカル入力処理モードでは,入力を行としてまとめることは行われず, 入力処理(erase, kill, delete 等)も行われません.このモードの制御は2つ のパラメータで行います. c_cc[VTIME]でキャラクタタイマの設定を行い, c_cc[VMIN]では読み込みを満足する前に受け取る最小の文字数を設定します. MIN > 0 で TIME = 0 の場合,MIN で読み込みが満足する前に受け取る文字数 を設定します.TIME はゼロなので,タイマは使われません. MIN = 0 で TIME > 0 の場合には,TIME はタイムアウト値になります.読み 込みは1文字読み込まれた場合か,時間 TIME (待ち時間 = TIME * 0.1 秒) を 過ぎた場合に満足されます.時間を越えた場合には,文字は返されません. MIN > 0 で TIME > 0 の場合には,TIME はキャラクタ間タイマの設定になり ます.読み込みは MIN 文字受け取った場合か,2つの文字を受け取る間の時間 が TIME を越えた場合に満足されます.タイマは1文字受け取るごとに初期化 されます.また,最初の1文字を受け取るまではタイマは有効にはなりませ ん. MIN = 0 かつ TIME = 0 の場合には,読み込みは即座に満足されます.現在読 み込み可能な文字数か,要求した文字数が戻されます.Antonio さんによれば (contributions 参照),読み込みの前に fcntl(fd, F_SETFL,FNDELAY); を実 行することで,同じ結果を得ることができます. newtio.c_cc[VTIME] と newtio.c_cc[VMIN] を変更することで,以上のモード を全て試すことができます. #include #include #include #include #include #define BAUDRATE B38400 #define MODEMDEVICE "/dev/ttyS1" #define _POSIX_SOURCE 1 /* POSIX 準拠のソース */ #define FALSE 0 #define TRUE 1 volatile int STOP=FALSE; main() { int fd,c, res; struct termios oldtio,newtio; char buf[255]; fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); if (fd <0) {perror(MODEMDEVICE); exit(-1); } tcgetattr(fd,&oldtio); /* 現在のポート設定を待避 */ bzero(&newtio, sizeof(newtio)); newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; newtio.c_iflag = IGNPAR; newtio.c_oflag = 0; /* set input mode (non-canonical, no echo,...) */ newtio.c_lflag = 0; newtio.c_cc[VTIME] = 0; /* キャラクタ間タイマは未使用 */ newtio.c_cc[VMIN] = 5; /* 5文字受け取るまでブロックする */ tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&newtio); while (STOP==FALSE) { /* 入力ループ */ res = read(fd,buf,255); /* 5 文字入力されたら戻る */ buf[res]=0; /* printf を使うために文字列終端をセット */ printf(":%s:%d\n", buf, res); if (buf[0]=='z') STOP=TRUE; } tcsetattr(fd,TCSANOW,&oldtio); } 3.3. 非同期入力 #include #include #include #include #include #include #define BAUDRATE B38400 #define MODEMDEVICE "/dev/ttyS1" #define _POSIX_SOURCE 1 /* POSIX 準拠のソース */ #define FALSE 0 #define TRUE 1 volatile int STOP=FALSE; void signal_handler_IO (int status); /* シグナルハンドラの宣言 */ int wait_flag=TRUE; /* シグナルを受け取っていなければ TRUE */ main() { int fd,c, res; struct termios oldtio,newtio; struct sigaction saio; /* シグナルを受けた時の動作を定義 */ char buf[255]; /* デバイスを非ブロッキングモードでオープンする (read は即座に戻ってくるようになる) */ fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK); if (fd <0) {perror(MODEMDEVICE); exit(-1); } /* デバイスを非同期モードにする前に,シグナルハンドラを設定する */ saio.sa_handler = signal_handler_IO; saio.sa_mask = 0; saio.sa_flags = 0; saio.sa_restorer = NULL; sigaction(SIGIO,&saio,NULL); /* プロセスが SIGIO を受け取れるようにする */ fcntl(fd, F_SETOWN, getpid()); /* ファイルデスクリプタを非同期モードにする (オンラインマニュアルに よれば,O_APPEND と O_NONBLOCK だけが F_SETFL で動作するとのこと… */ fcntl(fd, F_SETFL, FASYNC); tcgetattr(fd,&oldtio); /* save current port settings */ /* 新しいポートの設定をカノニカル入力処理に設定する */ newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; newtio.c_iflag = IGNPAR | ICRNL; newtio.c_oflag = 0; newtio.c_lflag = ICANON; newtio.c_cc[VMIN]=1; newtio.c_cc[VTIME]=0; tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&newtio); /* 入力待ちの間ループします.通常はここで何らかの作業をする */ while (STOP==FALSE) { printf(".\n");usleep(100000); /* SIGIO を受け取り,wait_flag = FALSE になっていれば入力が利用可 能なので読み込みを行うことができる */ if (wait_flag==FALSE) { res = read(fd,buf,255); buf[res]=0; printf(":%s:%d\n", buf, res); if (res==1) STOP=TRUE; /* CR だけが入力された場合にループ終了 */ wait_flag = TRUE; /* 次の入力を待つ */ } } /* プログラム開始時のポート設定を復元する */ tcsetattr(fd,TCSANOW,&oldtio); } /********************************************************************** * シグナルハンドラ.上記のループで文字を受け取ったことを示すために, * * wait_flag に FALSE をセットする. * ***********************************************************************/ void signal_handler_IO (int status) { printf("received SIGIO signal.\n"); wait_flag = FALSE; } 3.4. 複数の入力からの入力待ち この章は簡単に済ませます.ヒントを示すことが目的なので,サンプルのプロ グラムも短いものにしています.この仕組はシリアルポートだけでなく,全て のファイルデスクリプタの集合に対して使うことができます. select システムコールとこれに伴うマクロでは,fd_set を使います.これ は,全ての有効なファイルデスクリプタのエントリーが含まれるビット列で す.select はファイルデスクリプタに対応するビット集合 fd_setを引数と し,fd_setに入出力が可能なファイルデスクリプタ,あるいは例外が発生した ファイルデスクリプタの集合をセットします.fd_set の操作は用意されてい るマクロで行います.詳しくはオンラインマニュアルの select(2) を参照し てください. #include #include #include main() { int fd1, fd2; /* 入力ソース 1, 2 */ fd_set readfs; /* ファイルデスクリプタの集合 */ int maxfd; /* 使われているファイルデスクリプタの最大値 */ int loop=1; /* ループしている間は TRUE */ /* open_input_source ではデバイスのオープン,正しいポートの設定が行わ れ,ファイルデスクリプタが戻し値になっているものとする. */ fd1 = open_input_source("/dev/ttyS1"); /* COM2 */ if (fd1 <0) exit(0); fd2 = open_input_source("/dev/ttyS2"); /* COM3 */ if (fd2 <0) exit(0); maxfd = MAX (fd1, fd2)+1; /* 調べるビット長の最大値 */ /* 入力のためのループ */ while (loop) { FD_SET(fd1, &readfs); /* ソース 1 を調べることを指定 */ FD_SET(fd2, &readfs); /* ソース 2 を調べることを指定 */ /* block until input becomes available */ select(maxfd, &readfs, NULL, NULL, NULL); if (FD_ISSET(fd1)) /* ソース 1 からの入力が可能 */ handle_input_from_source1(); if (FD_ISSET(fd2)) /* ソース 2 からの入力が可能 */ handle_input_from_source2(); } } この例では,どちらかのソースから入力が行われるまで,プログラムがいつま でもブロックされてしまいます.入力のタイムアウトを指定したい場合には, select システムコールの部分を次のように書き換えます. int res; struct timeval Timeout; /* 入力ループでのタイムアウト値を設定 */ Timeout.tv_usec = 0; /* ミリ秒 */ Timeout.tv_sec = 1; /* 秒 */ res = select(maxfd, &readfs, NULL, NULL, &Timeout); if (res==0) /* 入力があったファイルデスクリプタの数が 0 ならば,タイムアウト発生 */ この例では 1 秒後にタイムアウトになります.タイムアウトとなった場合に は,select は 0 を返しますが,Timeout は selectが実際に入力を待った時 間の分だけ減らされることに注意してください.タイムアウト値がゼロの場合 には,select は即座に帰ってきます. 4. 他の情報源 o Linux Serial-HOWTO にはシリアルポートの設定方法と,ハードウェアの情 報が書かれています. o Serial Programming Guide for POSIX Compliant Operating Systems .Michael Sweet さんのページで す.このリンクはたどれなくなってしまったのですが,新しい URL がわか りません.どなたか教えてください.良くできた文書だったのに! o オンラインマニュアルのtermios(3)には,termios 構造体の全てのフラグ の説明があります. 5. コントリビューション 最初に述べたように,筆者はこの分野のエキスパートではありません.しか し,自分自身で問題に直面し,他の人の助けを借りながら問題を解決しまし た. European Transonic Windtunnel, Cologne の Strudthoff 氏, Michael Carter 氏 (mcarter@rocke.electro.swri.edu,Peter Waltenberg 氏 (p.waltenberg@karaka.chch.cri.nz)の皆様に感謝します. Antonino Ianella 氏(antonino@usa.netは,筆者がこの文書を準備しているの とほぼ同時に Serial-Port-Programming Mini HOWTO を作成しました.Greg Hankins 氏の提案により,Antonino 氏の Mini-HOWTO を本文書と統合しまし た. この文書の構造と SGML による整形は,Greg Hankins 氏の Serial-HOWTO を 基にしています.本文書に修正を入れてくれた Dave Pfaltzgraff 氏(Dave_Pfaltzgraff@patapsco.com), Sean Lincolne 氏(slincol@tpgi.com.au), Michael Wiedmann 氏 (mw@miwie.in- berlin.de), Adrey Bonar 氏(andy@tipas.lt) の皆さんに感謝します.