<!DOCTYPE LINUXDOCTR SYSTEM>

<ARTICLE>
<TITLE>The Linux Serial Programming HOWTO
<AUTHOR>by Peter H. Baumann, <TT><HTMLURL URL="mailto:Peter.Baumann@dlr.de"
NAME="Peter.Baumann@dlr.de"></TT>
<DATE>v1.0, 22 January 1998
<trans>藤原輝嘉, <tt><HTMLURL
URL="mailto:fujiwara@linux.or.jp" NAME="fujiwara@linux.or.jp"></tt>
<tdate>26 February 1998
<ABSTRACT>
<!-- This document describes how to program communications with devices
over a serial port on a Linux box. -->
この文書では，Linux マシンのシリアルポートを使った通信プログラムの書き方を説明します．
</ABSTRACT>
<TOC>

<!-- Introduction -->
<SECT>はじめに
<P>
<!-- This is the Linux Serial Programming HOWTO.  All about how to program
communications with other devices / computers over a serial line under
Linux. Different techniques are explained: Canonical I/O (only
complete lines are transmitted/received), asyncronous I/O, and waiting
for input from multiple sources. -->

本文書は Linux Serial Programming HOWTO です．Linux のシリアルポートを
使って，他のデバイス/コンピュータと通信するプログラムの書き方を解説し
ます．カノニカル I/O(行単位での送受信)，非同期 I/O，複数ソースからの入
力待ちの，異なるタイプの手法を解説します．

<P>
<!-- This document does not describe how to set up serial ports, because
this has been described by Greg Hankins in the Serial-HOWTO. -->

本文書では，シリアルポートの設定方法は説明しません．こちらについては，
Greg Hankins 氏の書かれた Serial-HOWTO をご覧ください．

<P>
<!-- I have to emphasize that I am not an expert in this field, but have
had problems with a project that involved such communication. The code
examples presented here were derived from the miniterm code available
from the LDP programmers guide
(<TT>ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers-guide/lpg-0.4.tar.gz</TT>
and mirrors)
in the examples directory.  -->

私はシリアル通信の分野の専門家ではありませんが，シリアル通信に関わるプ
ロジェクトで問題を解決してきました．本文書で紹介するサンプルコードは，
LDP プログラマーズガイド
(<TT>ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers-guide/lpg-0.4.tar.gz</TT>
及びミラーサイト)の 'examples' ディレクトリから入手することができる
miniterm に由来するものです．

<P>
<!-- Since I wrote this document in June 1997, I have moved to WinNT to
satisfy customers need, so I have not built up more in depth
knowledge. If anybody has any comments, I will gladly incorporate them
into this document (see sect. Feedback). If someone would like to take
over and do a better job, please e-mail me. -->

1997年の6月にこの文書を書いてから，私は顧客の都合により WindowsNT を使
うようになり，関連知識を深めることができませんでした．本文書に反映させ
ますので，コメントがあれば是非送ってください(フィードバックの章を参照)．
仕事を引き継いでよりよいものにしてくださる方がいれば，メールで知らせて
ください．

<P>
<!-- All examples were tested using a i386 Linux Kernel 2.0.29. -->
全ての例題のテストは，i386 ベースの Linux 2.0.29 で行いました．

<!-- Copyright -->
<SECT1>著作権について
<!--
<P>
The Linux Serial-Programming-HOWTO is copyright (C) 1997 by Peter
Baumann.  Linux HOWTO documents may be reproduced and distributed in
whole or in part, in any medium physical or electronic, as long as
this copyright notice is retained on all copies. Commercial
redistribution is allowed and encouraged; however, the author would
<EM/like/ to be notified of any such distributions.
<P>
All translations, derivative works, or aggregate works incorporating 
any Linux HOWTO documents must be covered under this copyright notice. 
That is, you may not produce a derivative work from a HOWTO and impose
additional restrictions on its distribution. Exceptions to these rules
may be granted under certain conditions; please contact the Linux HOWTO
coordinator at the address given below.
<P>
In short, we wish to promote dissemination of this information through
as many channels as possible. However, we do wish to retain copyright
on the HOWTO documents, and would <EM/like/ to be notified of any
plans to redistribute the HOWTOs.
-->
<p>
Linux Serial-Programming-HOWTOはPeter Baumann の著作物です(copyright
(C) 1997)．Linux HOWTO 文書は，この著作権表示を改変しない限り，文書の
一部あるいは全体を，任意の電子的・物理的メディアへ複製及び配布すること
ができます．商業的配布は認められており，また歓迎しています．しかし，こ
のような配布の場合には，著者への連絡があることを<em/希望/します．
<p>
翻訳，派生物，Linux HOWTO 文書を含む編集物はこの著作権表示に従わなけれ
ばなりません．つまり，HOWTO から派生した成果物を作り，これに制限を追加
して配布することはできません．ただし，一定の条件の下でこの規則に例外を
認めることができる場合があります．以下にアドレスを示す Linux HOWTO の
世話役に相談してください．
<p>
簡単に言えば，我々はこの情報をできるだけ多くの経路で広めたいと思ってい
ます．しかし，HOWTO 文書の著作権は保持されることと，HOWTO を再配布する
全ての方法を<em/できれば/知らせてもらえることを希望しています．

<P>
<!-- If you have questions, please contact Greg Hankins, the Linux HOWTO
coordinator, at <TT><HTMLURL URL="mailto:gregh@sunsite.unc.edu"
NAME="gregh@sunsite.unc.edu"></TT> via email. -->
疑問点があれば，Linux HOWTO の世話役である <TT><HTMLURL
URL="mailto:gregh@sunsite.unc.edu" NAME="gregh@sunsite.unc.edu"></TT>
氏に電子メールで相談してください．

<!-- New Versions Of This Document -->
<SECT1>本文書の最新版の入手方法
<P>
<!-- New versions of the Serial-Programming-HOWTO will be available
at<NEWLINE> <TT><HTMLURL
URL="ftp://sunsite.unc.edu:/pub/Linux/docs/HOWTO/Serial-Programming-HOWTO"
NAME="ftp://sunsite.unc.edu:/pub/Linux/docs/HOWTO/Serial-Programming-HOWTO"></TT>
and mirror sites.  There are other formats, such as PostScript and DVI
versions in the <TT/other-formats/ directory.  The
Serial-Programming-HOWTO is also available at <TT><HTMLURL
URL="http://sunsite.unc.edu/LDP/HOWTO/Serial-Programming-HOWTO.html"
NAME="http://sunsite.unc.edu/LDP/HOWTO/Serial-Programming-HOWTO.html">
</TT>
and will be posted to <TT><HTMLURL URL="news:comp.os.linux.answers"
NAME="comp.os.linux.answers"></TT> monthly. -->
Serial-Programming-HOWTO の最新版は
<NEWLINE> <TT><HTMLURL
URL="ftp://sunsite.unc.edu:/pub/Linux/docs/HOWTO/Serial-Programming-HOWTO"
NAME="ftp://sunsite.unc.edu:/pub/Linux/docs/HOWTO/Serial-Programming-HOWTO"></TT>
及び，そのミラーサイトで入手できます．PostScript や DVI 等の形式の文書
は<TT/other-formats/ディレクトリにあります．また，
<TT><HTMLURL
URL="http://sunsite.unc.edu/LDP/HOWTO/Serial-Programming-HOWTO.html"
NAME="http://sunsite.unc.edu/LDP/HOWTO/Serial-Programming-HOWTO.html">
</TT>
にもありますし，月に一度<TT><HTMLURL URL="news:comp.os.linux.answers"
NAME="comp.os.linux.answers"></TT>ニュースグループにも投稿されます．

<!-- Feedback -->
<SECT1>フィードバック
<P>
<!--
Please send me any corrections, questions, comments, suggestions, or
additional material. I would like to improve this HOWTO!  Tell me
exactly what you don't understand, or what could be clearer.  You can
reach me at <TT><HTMLURL URL="mailto:Peter.Baumann@dlr.de"
NAME="Peter.Baumann@dlr.de"></TT> via email. Please include the
version number of the Serial-Programming-HOWTO when writing, this is
version 0.3.
-->

訂正，質問，コメント，提案，追加事項などがあれば，是非筆者にお知らせく
ださい．この文書をより良いものにしたいと思っています．よく分からないと
ころや，もっと分かりやすくできる部分を知らせてください．筆者に連絡する
には<TT><HTMLURL URL="mailto:Peter.Baumann@dlr.de"
NAME="Peter.Baumann@dlr.de"></TT>へ電子メールを出してください．電子メー
ルを書く際には，読んだ Serial-Programming-HOWTO のバージョン(この文書
はバージョン1.0です)も書いてください．

<p>
<em/訳注/:
日本語訳の最新版は<HTMLURL URL="http://jf.gee.kyoto-u.ac.jp/JF/JF.html"
NAME="JF プロジェクトホームページ"> で入手できます．
また，誤訳があれば訳者の方へメールを出してください．

<!-- Getting started -->
<SECT>はじめてみましょう
<P>
<!-- Debugging -->
<SECT1>デバッグの方法
<P>
<!--
The best way to debug your code is to set up another Linux box, and
connect the two computers via a null-modem cable. Use miniterm
(available from the LDP programmers guide
(<TT>ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers-guide/lpg-0.4.tar.gz</TT>
in the examples directory) to transmit characters to your Linux
box. Miniterm can be compiled very easily and will transmit all
keyboard input raw over the serial port. Only the define statement
<TT>#define MODEMDEVICE "/dev/ttyS0"</TT> has to be checked. Set it to
<TT>ttyS0</TT> for COM1, <TT>ttyS1</TT> for COM2, etc.. It is
essential for testing, that <EM/all/ characters are transmitted raw
(without output processing) over the line. To test your connection,
start miniterm on both computers and just type away. The characters
input on one computer should appear on the other computer and vice
versa. The input will not be echoed to the attached screen.
-->
あなたが書いたコードをデバッグするための最も良い方法は，Linux マシンを
もう一台用意し，二台のコンピュータをヌルモデムケーブルで接続することで
す．miniterm(LDP プログラマーズガイド
(<TT>ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers-guide/lpg-0.4.tar.gz</TT>
の'examples'ディレクトリにあります)を使って，あなたの Linux マシンへ文
字を送ってみましょう．miniterm は非常に簡単にコンパイルでき，キーボー
ド入力をシリアルポート経由でそのまま(raw)送ります．ただ，define 文
<TT>#define MODEMDEVICE "/dev/ttyS0"</TT>の部分だけはチェックしなくて
はなりません．この部分は，ケーブルで接続するのが COM1 であれば
<TT>ttyS0</TT>，COM2であれば<TT>ttyS1</TT>のように設定します．このテス
トで大事な点は，<EM/全ての/文字はそのまま(raw, 出力処理無しに)送信され
ることです．接続テストをするときは，両方のマシンで miniterm を実行し，
キーボードを叩くだけです．片方のマシンで入力された文字は，もう一方のマ
シンで出力されます．その逆も同じです．入力した文字は，入力した方のマシ
ンの画面にはエコーされません．

<P>
<!--
To make a null-modem cable you have to cross the TxD (transmit) and
RxD (receive) lines. For a description of a cable see sect. 7 of the
Serial-HOWTO. -->
ヌルモデムケーブルを自作するには，TxD(送信)とRxD(受信)の結線をクロスさ
せます．ケーブルについての詳しい解説は，Serial-HOWTO の第7章にあります．

<P>
<!--
It is also possible to perform this testing with only one computer, if
you have two unused serial ports. You can then run two miniterms off
two virtual consoles. If you free a serial port by disconnecting the
mouse, remember to redirect <TT>/dev/mouse</TT> if it exists. If you
use a multiport serial card, be sure to configure it correctly. I had
mine configured wrong and everything worked fine as long as I was
testing only on my computer. When I connected to another computer, the port
started loosing characters. Executing two programs on one computer
just isn't fully asynchronous.
-->
未使用のシリアルポートが2つあるのならば，一台のコンピュータでこのテス
トを実行することもできます．この場合は，2つの仮想コンソールでそれぞれ 
miniterm を実行します．シリアルマウスを外してシリアルポートを空ける場
合には，<TT>/dev/mouse</TT>を書換えることを忘れないようにしましょう．
マルチポートシリアルカードを使っている場合には，設定が正しいことを確認
しましょう．筆者は自分のマシンで間違った設定をしていたために，自分のマ
シンでテストした時だけうまく動作するという事態になったことがあります．
この設定で他のコンピュータに接続した時には，文字が消えてしまいました．
2つのプログラムを1つのコンピュータで動かすことは，完全に非同期とは言え
ません．

<!-- Port Settings -->
<SECT1>ポートの設定
<P>
<!--
The devices <TT>/dev/ttyS*</TT> are intended to hook up terminals to
your Linux box, and are configured for this use after startup. This
has to be kept in mind when programming communication with a raw
device. E.g. the ports are configured to echo characters sent from the
device back to it, which normally has to be changed for data
transmission.
-->
<TT>/dev/ttyS*</TT>というデバイスは端末を Linux マシンに繋ぐためのもの
で，通信を始めた後は端末に合わせた設定が行われます．この点は raw デバ
イスを使って通信するプログラムを行う場合には，注意しておかなければなり
まん．例えば，送ったキャラクタをエコーバックさせるようにシリアルポート
を設定されていますが，通常データ送信のためにはこの設定は変更しなければ
いけません．

<P>
<!--
All parameters can be easily configured from within a program. The
configuration is stored in a structure <TT>struct termios</TT>, which
is defined in <TT>&lt;asm/termbits.h&gt;</TT>:
-->
全てのパラメータはプログラム内で簡単に設定できます．設定は
ヘッダファイル<TT>&lt;asm/termbits.h&gt;</TT>で定義されている
<TT>termios 構造体</TT>に保存されます．

<TSCREEN><VERB>
#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 */
};
</VERB></TSCREEN>
<!--
This file also includes all flag definitions. The input mode flags in
<TT>c_iflag</TT> handle all input processing, which means that the
characters sent from the device can be processed before they are read
with <TT>read</TT>. Similarly <TT>c_oflag</TT> handles the output
processing. <TT>c_cflag</TT> contains the settings for the port, as
the baudrate, bits per character, stop bits, etc.. The local mode
flags stored in <TT>c_lflag</TT> determine if characters are echoed,
signals are sent to your program, etc.. Finally the array
<TT>c_cc</TT> defines the control characters for end of file, stop,
etc..  Default values for the control characters are defined in
<TT>&lt;asm/termios.h&gt;</TT>. The flags are described in the manual
page <TT>termios(3)</TT>. The structure <TT>termios</TT> contains the
<TT>c_line</TT> (line discipline) element, which is not used in POSIX compliant systems.
-->
この<TT>&lt;asm/termbits.h&gt;</TT> ファイルには，フラグの定義も全て記
述されています．<TT>c_iflag</TT> フラグには入力モードのフラグがいくつ
か含まれており，全ての入力処理を指定します．入力処理とは，デバイスから
送られたキャラクタは，<TT>read</TT>で読み出される前に処理することがで
きるということです．同様に，<TT>c_oflag</TT>は出力処理を扱います．
<TT>c_cflag</TT>は，ボーレートや文字毎のビット数，ストップビットなどの
ポート設定を記録しています．<TT>c_lflag</TT> 内のローカルモードフラグ
は，文字がエコーされるかどうか，プログラムにシグナルが送られるかどうか
等を指定します．最後に，配列 <TT>c_cc</TT> では，ファイル終端，stop 等
の制御文字を定義します．制御文字のデフォルト値は 
<TT>&lt;asm/termios.h&gt;</TT> で定義されています．各フラグについては，
オンラインマニュアルの <TT>termios(3)</TT> に説明があります．構造体
<tt>termios</tt>は POSIX 準拠のシステムでは用いられない，
<tt>c_line</tt>要素を含んでいます．

<!-- Input Concepts for Serial Devices -->
<SECT1>シリアルデバイスにおける入力の概念
<P>
<!--
Here three different input concepts will be presented. The appropriate
concept has to be chosen for the intended application. Whenever
possible, do not loop reading single characters to get a complete
string. When I did this, I lost characters, whereas a <TT>read</TT> for
the whole string did not show any errors.
-->
ここでは，3つの異なる種類の入力の概念を説明します．利用目的に従って，
適切なものを選択してください．文字列全体を取得するのに，1文字読み込み
のループを使うことはできる限り避けるべきです．私はこれをやったとき，
読み込み時に <TT>read</TT> が全くエラーを出力しなかったのにもかかわら
ず，文字が欠けてしまったことがありました．

<!-- Canonical Input Processing -->
<SECT2>カノニカル入力処理
<P>
<!--
This is the normal processing mode for terminals, but can also be
useful for communicating with other dl input is processed in
units of lines, which means that a <TT>read</TT> will only return a
full line of input. A line is by default terminated by a <TT>NL</TT>
(ASCII <TT>LF</TT>), an end of file, or an end of line character. A
<TT>CR</TT> (the DOS/Windows default end-of-line) will not terminate a
line with the default settings.
-->
これは端末に対しての通常の処理モードですが，他のデバイスとの通信の時に
も便利です．全ての入力は行単位で処理されます．つまり，<TT>read</TT> は
入力の1行全体のみを返してきます．デフォルトでは，行は<TT>NL</TT>(ASCII
の<TT>LF</TT>)，ファイル終端，行終端文字のいずれかで終ります．標準の設
定では，<TT>CR</TT> (DOS/Windows のデフォルトの行終端文字)は行の終端と
はなりません．

<P>
<!--
Canonical input processing can also handle the erase, delete word, and
reprint characters, translate <TT>CR</TT> to <TT>NL</TT>, etc..
-->
カノニカル入力処理では，消去(erase)，単語の削除(delete word)，文字の再
出力(reprint characters)，<TT>CR</TT>の<TT>NL</TT>への変換などを扱うこ
とができます．

<!-- Non-Canonical Input Processing -->
<SECT2>非カノニカル入力処理
<P>
<!--
Non-Canonical Input Processing will handle a fixed amount of
characters per read, and allows for a character timer. This mode
should be used if your application will always read a fixed number of
characters, or if the connected device sends bursts of characters.
-->
非カノニカル入力処理は，read 毎に決まった数の文字を扱う方法で，キャラ
クタタイマを利用することもできます．このモードはアプリケーションが決まっ
た文字数のキャラクタを読み込む時や，接続したデバイスが大量の文字を送っ
てくる場合に使用します．
<P>

<!-- Asynchronous Input -->
<SECT2>非同期入力
<P>
<!--
The two modes described above can be used in synchronous and asynchronous
mode. Synchronous is the default, where a <TT>read</TT> statement will
block, until the read is satisfied. In asynchronous mode the
<TT>read</TT> statement will return immediatly and send a signal to the
calling program upon completion. This signal can be received by a
signal handler.
-->
上記2つのモードは，同期及び非同期モードで使うことができます．デフォル
トは，入力がうまくいくまで <TT>read</TT> 文がブロックされる同期モード
です．非同期モードでは，<TT>read</TT> 文は即座に終了し，後で読み込みが
完了した時にプログラムにシグナルが送られます．このシグナルは，シグナル
ハンドラを使って受け取ります．

<!-- Waiting for Input from Multiple Sources -->
<SECT2>複数入力からの入力待ち
<P>
<!--
This is not a different input mode, but might be useful, if you are
handling multiple devices. In my application I was handling input over
a TCP/IP socket and input over a serial connection from another computer
quasi-simultaneously. The program example given below will wait for
input from two different input sources. If input from one source
becomes available, it will be processed, and the program will then
wait for new input.
-->
これは別の入力モードというわけではありませんが，複数のデバイスを扱う時
に便利です．筆者のアプリケーションでは，TCP/IP ソケット経由の入力と他
のコンピュータからのシリアル接続経由の入力を擬似的に同時に扱っています．
以下に示すプログラム例では，異なる2つの入力ソースからの入力待ちを行い
ます．片方のソースからの入力が可能になると，その入力が処理され，プログ
ラムは次の入力を待ちます．

<P>
<!--
The approach presented below seems rather complex, but it is important
to keep in mind that Linux is a multi-processing operating system. The
<TT>select</TT> system call will not load the CPU while waiting for
input, whereas looping until input becomes available would slow down
other processes executing at the same time.
-->
以下に示すアプローチはちょっと複雑に見えますが，Linux はマルチプロセッ
シング OS であることを忘れてはいけません．<TT>select</TT>システムコー
ルは入力待ちの間は CPU に負荷を与えませんが，ループを使った入力待ちを
行うと，同時に実行されている他のプロセスが遅くなってしまいます．
<P>

<!-- Program Examples -->
<SECT>プログラム例
<P>
<!--
All examples have been derived from <TT>miniterm.c</TT>. The type
ahead buffer is limited to 255 characters, just like the maximum
string length for canonical input processing
(<TT>&lt;linux/limits.h&gt</TT> or <TT>&lt;posix1_lim.h&gt;</TT>). -->
全ての例は <TT>miniterm.c</TT> から取り出したものです．先行入力のバッ
ファは 255 文字に制限されています．これは，カノニカル入力処理の最大文
字長と同じです (<TT>&lt;linux/limits.h&gt</TT> あるいは 
<TT>&lt;posix1_lim.h&gt;</TT>を参照)．

<P>
<!--
See the comments in the code for explanation of the use of the
different input modes. I hope that the code is understandable. The
example for canonical input is commented best, the other examples are
commented only where they differ from the example for canonical input
to emphasize the differences.
-->
それぞれの入力モードの使い方についてはプログラム中のコメントを見てくだ
さい．プログラムは十分読みこなせると思います．カノニカル入力モードの例
には一番詳しいコメントを付けています．違いを強調するために，他の例につ
いてはカノニカル入力モードとの相違点だけを書いています．

<P>
<!--
The descriptions are not complete, but you are encouraged to
experiment with the examples to derive the best solution for your
application.
-->
説明は完全ではありませんが，勇気を出して例題で実験し，あなたの目的に最
も適した解を見つけだしてください．

<P>
<!-- Don't forget to give the appropriate serial ports the right
permissions (e. g.: <TT>chmod a+rw /dev/ttyS1</TT>)! -->
使用するシリアルポートには，適切なパーミッションを与えるのを忘れないで
ください(例: <TT>chmod a+rw /dev/ttyS1</TT>)．

<P>

<!-- Canonical Input Processing -->
<SECT1>カノニカル入力処理
<P>
<TSCREEN><VERB>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>

/* <asm/termbits.h> で定義されているボーレートの設定．これは
<termios.h>からインクルードされる． */
#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 &lt;0) {perror(MODEMDEVICE); exit(-1); }

 tcgetattr(fd,&ero;oldtio); /* 現在のシリアルポートの設定を待避させる*/
 bzero(&ero;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,&ero;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,&ero;oldtio);
}


</VERB></TSCREEN>

<!-- Non-Canonical Input Processing -->
<SECT1>非カノニカル入力処理
<P>
<!--
In non-canonical input processing mode, input is not assembled into
lines and input processing (erase, kill, delete, etc.) does not
occur. Two parameters control the behavior of this mode:
<TT>c_cc[VTIME]</TT> sets the character timer, and <TT>c_cc[VMIN]</TT>
sets the minimum number of characters to receive before satisfying the
read.
-->
非カノニカル入力処理モードでは，入力を行としてまとめることは行われず，
入力処理(erase, kill, delete 等)も行われません．このモードの制御は2つ
のパラメータで行います．
<TT>c_cc[VTIME]</TT>でキャラクタタイマの設定を行い，
<TT>c_cc[VMIN]</TT>では読み込みを満足する前に受け取る最小の文字数を設定
します．

<P>
<!--
If MIN &gt; 0 and TIME = 0, MIN sets the number of characters to receive
before the read is satisfied. As TIME is zero, the timer is not used.
-->
MIN &gt; 0 で TIME = 0 の場合，MIN で読み込みが満足する前に受け取る文
字数を設定します．TIME はゼロなので，タイマは使われません．

<P>
<!--
If MIN = 0 and TIME &gt; 0, TIME serves as a timeout value. The read
will be satisfied if a single character is read, or TIME is exceeded (t =
TIME *0.1 s). If TIME is exceeded, no character will be returned.
-->
MIN = 0 で TIME &gt; 0 の場合には，TIME はタイムアウト値になります．読
み込みは1文字読み込まれた場合か，時間 TIME (待ち時間 = TIME * 0.1 秒)
を過ぎた場合に満足されます．時間を越えた場合には，文字は返されません．

<P>
<!--
If MIN &gt; 0 and TIME &gt; 0, TIME serves as an inter-character
timer. The read will be satisfied if MIN characters are received, or
the time between two characters exceeds TIME. The timer is restarted
every time a character is received and only becomes active after the
first character has been received.
-->
MIN &gt; 0 で TIME &gt; 0 の場合には，TIME はキャラクタ間タイマの設定
になります．読み込みは MIN 文字受け取った場合か，2つの文字を受け取る間
の時間が TIME を越えた場合に満足されます．タイマは1文字受け取るごとに
初期化されます．また，最初の1文字を受け取るまではタイマは有効にはなり
ません．

<P>
<!--
If MIN = 0 and TIME = 0, read will be satisfied immediately. The
number of characters currently available, or the number of characters
requested will be returned. According to Antonino (see contributions),
you could issue a <TT>fcntl(fd, F_SETFL, FNDELAY);</TT> before reading
to get the same result.
-->
MIN = 0 かつ TIME = 0 の場合には，読み込みは即座に満足されます．現在読
み込み可能な文字数か，要求した文字数が戻されます．Antonio さんによれば
(contributions 参照)，読み込みの前に <TT>fcntl(fd,
F_SETFL,FNDELAY);</TT> を実行することで，同じ結果を得ることができます．

<P>
<!--
By modifying <TT>newtio.c_cc[VTIME]</TT> and
<TT>newtio.c_cc[VMIN]</TT> all modes described above can be tested.
-->
<TT>newtio.c_cc[VTIME]</TT> と <TT>newtio.c_cc[VMIN]</TT> を変更するこ
とで，以上のモードを全て試すことができます．

<TSCREEN><VERB>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>

#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 &lt;0) {perror(MODEMDEVICE); exit(-1); }

 tcgetattr(fd,&ero;oldtio); /* 現在のポート設定を待避 */

 bzero(&ero;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,&ero;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,&ero;oldtio);
}
</VERB></TSCREEN>

<!-- Asynchronous Input -->
<SECT1>非同期入力
<P>
<TSCREEN><VERB>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <sys/types.h>

#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 &lt;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,&ero;saio,NULL);
  
  /* プロセスが SIGIO を受け取れるようにする */
  fcntl(fd, F_SETOWN, getpid());
  /* ファイルデスクリプタを非同期モードにする (オンラインマニュアルに
     よれば，O_APPEND と O_NONBLOCK だけが F_SETFL で動作するとのこと… 
   */
  fcntl(fd, F_SETFL, FASYNC);

  tcgetattr(fd,&ero;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,&ero;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,&ero;oldtio);
}

/**********************************************************************
* シグナルハンドラ．上記のループで文字を受け取ったことを示すために，  *
* wait_flag に FALSE をセットする．                                   *
***********************************************************************/

void signal_handler_IO (int status)
{
  printf("received SIGIO signal.\n");
  wait_flag = FALSE;
}

</VERB></TSCREEN>

<!-- Waiting for Input from Multiple Sources -->
<SECT1>複数の入力からの入力待ち
<P>
<!--
This section is kept to a minimum. It is just intended to be a hint,
and therefore the example code is kept short. This will not only work
with serial ports, but with any set of file descriptors.
-->
この章は簡単に済ませます．ヒントを示すことが目的なので，サンプルのプロ
グラムも短いものにしています．この仕組はシリアルポートだけでなく，全て
のファイルデスクリプタの集合に対して使うことができます．

<P>
<!--
The select call and accompanying macros use a <TT>fd_set</TT>. This is a
bit array, which has a bit entry for every valid file descriptor
number. <TT>select</TT> will accept a <TT>fd_set</TT> with the bits set
for the relevant file descriptors and returns a <TT>fd_set</TT>, in which
the bits for the file descriptors are set where input, output, or an exception
occurred. All handling of <TT>fd_set</TT> is done with the provided
macros. See also the manual page <TT>select(2)</TT>.
-->
select システムコールとこれに伴うマクロでは，<TT>fd_set</TT> を使いま
す．これは，全ての有効なファイルデスクリプタのエントリーが含まれるビッ
ト列です．<TT>select</TT> はファイルデスクリプタに対応するビット集合
<TT>fd_set</TT>を引数とし，<TT>fd_set</TT>に入出力が可能なファイルデス
クリプタ，あるいは例外が発生したファイルデスクリプタの集合をセットしま
す．<TT>fd_set</TT> の操作は用意されているマクロで行います．詳しくはオ
ンラインマニュアルの <TT>select(2)</TT> を参照してください．

<TSCREEN><VERB>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

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 &lt;0) exit(0);
   fd2 = open_input_source("/dev/ttyS2");   /* COM3 */
   if (fd2 &lt;0) exit(0);
   maxfd = MAX (fd1, fd2)+1;  /* 調べるビット長の最大値 */

   /* 入力のためのループ */
   while (loop) {
     FD_SET(fd1, &ero;readfs);  /* ソース 1 を調べることを指定 */
     FD_SET(fd2, &ero;readfs);  /* ソース 2 を調べることを指定 */
     /* block until input becomes available */
     select(maxfd, &ero;readfs, NULL, NULL, NULL);
     if (FD_ISSET(fd1))         /* ソース 1 からの入力が可能 */
       handle_input_from_source1();
     if (FD_ISSET(fd2))         /* ソース 2 からの入力が可能 */
       handle_input_from_source2();
   }

}   

</VERB></TSCREEN>
<P>
<!--
The given example blocks indefinitely, until input from one of
the sources becomes available. If you need to timeout on input, just
replace the select call by:
-->
この例では，どちらかのソースから入力が行われるまで，プログラムがいつま
でもブロックされてしまいます．入力のタイムアウトを指定したい場合には，
select システムコールの部分を次のように書き換えます．

<TSCREEN><VERB>
int res;
struct timeval Timeout;

/* 入力ループでのタイムアウト値を設定 */
Timeout.tv_usec = 0;  /* ミリ秒 */
Timeout.tv_sec  = 1;  /* 秒 */
res = select(maxfd, &ero;readfs, NULL, NULL, &ero;Timeout);
if (res==0)
/* 入力があったファイルデスクリプタの数が 0 ならば，タイムアウト発生 */
</VERB></TSCREEN>
<!--
This example will timeout after 1 second. If a timeout occurs, select
will return 0, but beware that <TT>Timeout</TT> is decremented by the
time actually waited for input by <TT>select</TT>. If the timeout
value is zero, select will return immediatly.
-->
この例では 1 秒後にタイムアウトになります．タイムアウトとなった場合に
は，<TT>select</TT> は 0 を返しますが，<TT>Timeout</TT> は 
<TT>select</TT >が実際に入力を待った時間の分だけ減らされることに注意し
てください．タイムアウト値がゼロの場合には，<TT>select</TT> は即座に帰っ
てきます．

<!-- Other Sources of Information -->
<SECT>他の情報源
<P>

<ITEMIZE>

<!-- <ITEM>The Linux Serial-HOWTO describes how to set up serial ports and
contains hardware information. -->

<ITEM>Linux Serial-HOWTO にはシリアルポートの設定方法と，ハードウェア
の情報が書かれています．

<!-- <ITEM><URL URL="http://www.easysw.com/~mike/serial" NAME="Serial
Programming Guide for POSIX Compliant Operating Systems">, by Michael
Sweet. This link is obsolete and I could not find a new location for
it. Does somebody know where we can find it again? It was a well
prepared document!
-->
<ITEM><URL URL="http://www.easysw.com/~mike/serial" NAME="Serial
Programming Guide for POSIX Compliant Operating Systems">．Michael
Sweet さんのページです．このリンクはたどれなくなってしまったのですが，
新しい URL がわかりません．どなたか教えてください．良くできた文書だっ
たのに！

<!-- <ITEM>The manual page <TT>termios(3)</TT> describes all flags for the
<TT>termios</TT> structure. -->
<ITEM>オンラインマニュアルの<TT>termios(3)</TT>には，<TT>termios</TT>
構造体の全てのフラグの説明があります．

</ITEMIZE>

<!-- Contributions -->
<SECT>コントリビューション
<P>
<!--
As mentioned in the introduction, I am no expert in this field, but
had problems myself, and found a solution with the help of others.
Thanks for the help from Mr. Strudthoff from the European Transonic
Windtunnel, Cologne, Michael Carter
(<TT>mcarter@rocke.electro.swri.edu</TT>, and Peter Waltenberg
(<TT>p.waltenberg@karaka.chch.cri.nz</TT>)
-->
最初に述べたように，筆者はこの分野のエキスパートではありません．しかし，
自分自身で問題に直面し，他の人の助けを借りながら問題を解決しました．
European Transonic Windtunnel, Cologne の Strudthoff 氏，
Michael Carter 氏
(<TT>mcarter@rocke.electro.swri.edu</TT>，Peter Waltenberg 氏
(<TT>p.waltenberg@karaka.chch.cri.nz</TT>)の皆様に感謝します．

<P>
<!--
Antonino Ianella (<TT>antonino@usa.net</TT> wrote the
Serial-Port-Programming Mini HOWTO, at the same time I prepared this
document. Greg Hankins asked me to incorporate Antonino's Mini-HOWTO
into this document.
-->
Antonino Ianella 氏(<TT>antonino@usa.net</TT>は，筆者がこの文書を準備
しているのとほぼ同時に Serial-Port-Programming Mini HOWTO を作成しまし
た．Greg Hankins 氏の提案により，Antonino 氏の Mini-HOWTO を本文書と統
合しました．

<P>
<!--
The structure of this document and SGML formatting was derived from
the Serial-HOWTO by Greg Hankins. Thanks also for various corrections
made by : Dave Pfaltzgraff (<TT>Dave_Pfaltzgraff@patapsco.com</TT>),
Sean Lincolne (<TT>slincol@tpgi.com.au</TT>), Michael Wiedmann
(<TT>mw@miwie.in-berlin.de</TT>), and Adrey Bonar (<TT>andy@tipas.lt</TT>).
-->
この文書の構造と SGML による整形は，Greg Hankins 氏の Serial-HOWTO を
基にしています．本文書に修正を入れてくれた
Dave Pfaltzgraff 氏(<TT>Dave_Pfaltzgraff@patapsco.com</TT>)，
Sean Lincolne 氏(<TT>slincol@tpgi.com.au</TT>)， Michael Wiedmann 氏
(<TT>mw@miwie.in-berlin.de</TT>)， Adrey Bonar 氏(<TT>andy@tipas.lt</TT>)
の皆さんに感謝します．
</ARTICLE>
