TCSETS

ioctlシステムコールにTCSETSとtermios構造体変数を渡すと、指定のファイルディスクリプタに端末情報を設定できる。

Man page of TTY_IOCTL

TCSETSの使いどころというと、やはりシリアル通信だろう・・・。
ということで、シリアル通信を始める。
といっても、昨今デスクトップPCでもなかなかシリアル(COM)ポートが使えるものは少ない・・・。
シリアルケーブルもないし、ましてや現在使っているのは「New Surface Pro」だし・・・。

COMポートはない
COMポートはない

ということで、VirtualBox上のゲストのFedoraと、ホストのWindows10の間で仮想的にシリアル通信できるかを調べたら、

VirtualBox内のLinuxとホストPC(Windows)間でシリアル通信を行う方法メモ - 『何故ならば・・・、』

こんなのがあったので、

Null-modem emulator (com0com) 日本語情報トップページ - OSDN

ここにあるcom0comという仮想シリアルポートを設定できるツールを使う。
com0com-3.0.0.0-i386-and-x64-signed.zipのzip開けたら、64bit用のSetup_com0com_v3.0.0.0_W7_x64_signed.exeを実行。

インストーラ
インストーラ

あとは、このインストーラでぽちぽちしてインストール。

インストールしたが警告
インストールしたが警告

完了してもデバイスマネージャを見ると失敗してそう。

検証できない
検証できない

ドライバ署名の問題かな・・・。

仮想シリアル(COM) ポートドライバ「com0com」によるシリアル通信 - Qiita

Windows10 64bitだとドライバロゴ認証通してないといけないのだが、通してないのか・・・。

テストモードにするか・・・。

Windows10で署名なしドライバをインストールする方法/テストモードにする方法 - ぼくんちのTV 別館

で、

管理者権限で起動
管理者権限で起動

コマンドプロンプトを管理者権限で起動し、

今度はセキュアブートの問題
今度はセキュアブートの問題

今度はセキュアブートの問題。

Windows 10でBIOS/UEFIセットアップ画面を確実に開く方法:Tech TIPS - @IT

電源押して出すよりは、設定からのほうが楽かな。

設定
設定

設定で、

回復のPCの起動をカスタマイズする
回復のPCの起動をカスタマイズする

[回復]の[PCの起動をカスタマイズする]で、[今すぐ再起動]。

起動時に、

トラブルシューティング
トラブルシューティング

トラブルシューティング

詳細オプション
詳細オプション

詳細オプション。

UEFIファームウェアの設定
UEFIファームウェアの設定

UEFIファームウェアの設定。

再起動
再起動

で再起動。

Surface UEFI
Surface UEFI

こっからUEFIで、

Secure Bootが有効に
Secure Bootが有効に

Secure Bootが有効に。

None
None

None。

Disabled
Disabled

Disabledになった。

再起動
再起動

Restart nowで再起動。

Windowsロゴの上に
Windowsロゴの上に

Windowsロゴの上にこんなのが出て。

BitLocker
BitLocker

BitLockerを解決しないといけない。

https://support.microsoft.com/ja-jp/help/4026181/windows-10-find-my-bitlocker-recovery-key

スマホのブラウザから、
http://windows.microsoft.com/recoverykey
ここにアクセスすると、MSアカウントログインを求められるので、手持ちのhotmailアカウントでログインして、

ページに長いキーが書いてある
ページに長いキーが書いてある

ページに長いキーが書いてあるので、

入力して続行
入力して続行

入力して続行すると、

これが出るので再起動
これが出るので再起動

これが出るので再起動。

さて、Windowsに戻ってきたら、テストモードに。

今度は成功
今度は成功

今度は成功。

起動した段階で治っていた
起動した段階で治っていた

起動した段階で治っていた。

そしてCOMポートが既にできていた
そしてCOMポートが既にできていた

そしてCOMポートが既にできていた。

com0comを起動
com0comを起動

com0comを起動。

COM3とCOM4がつながっているのを確認
COM3とCOM4がつながっているのを確認

COM3とCOM4が繋がっているらしい。

Tera Term (テラターム) プロジェクト日本語トップページ - OSDN

実際にCOM3とCOMで通信できるかをTera Termで確認。

ぽちぽちインストール
ぽちぽちインストール

ぽちぽちteraterm-4.103.exeをインストール。

Tera Term起動
Tera Term起動

Tera Term起動。

COM3
COM3

COM3。

COM4
COM4

もう1個起動してこっちはCOM4。

COM3で入力
COM3で入力

COM3にフォーカスを合わせているのにCOM4に"ABC"が出る。

COM4で入力
COM4で入力

逆にCOM4にフォーカス合わせて"XYZ"を入力してもCOM3側に出る。
互いに相手側に出ているので通信出来ていると言える。

次は、VirtualBoxにCOMポート設定する。

Fedoraの設定
Fedoraの設定

Fedoraの設定で、

COM4をFedoraのCOM1とする
COM4をFedoraのCOM1とする

さきほど出てきたCOM4をFedoraの仮想COM1(IRQ4, 0x3f8)とする。

Fedoraを起動したら、

dmesgでIRQ4がわかる
dmesgでIRQ4がわかる

dmesgでIRQ4がわかる。
ということは、ttyS0が仮想COM1(IRQ4, 0x3f8)である。

screenコマンドで接続
screenコマンドで接続

screenコマンドで接続。

Linuxでシリアル通信 - Qiita

この状態になる
この状態になる

この状態になる。

Windows側はCOM3で待つ
Windows側はCOM3で待つ

Windows側はCOM3で待つ。

Fedora側からXYZ
Fedora側からXYZ

Fedora側からXYZ。

Windows側からABX
Windows側からABX

Windows側からABX。(ABCと打とうと思ったら間違えた。)

デタッチ
デタッチ

デタッチはCtrl+aの後にdらしい。
ただ何度かやらないと抜けられない・・・。下手なだけかもだが。

デタッチなのに既にいない
デタッチなのに既にいない

デタッチなのにセッションがいない。
macだと残っていた気もするが。
(その場合はセッションプロセスをkillした。)

さて、最後にFedora側にシリアルサーバプログラムを置いて、Windows側の通信を受信する。

TCSETSで、基本的なシリアルの設定を最終セットする。

Man page of TTY_IOCTL

ttyS0がrootじゃないと受け付けないのでsudoで起動
ttyS0がrootじゃないと受け付けないのでsudoで起動

Fedora側は、ttyS0がrootじゃないと受け付けないのでsudoで起動。

Windows側
Windows

Windows側はCOM3で接続。

aと入力
aと入力

Windows側で、aと入力すると、aが受信出来てるのがわかる。
1回に1文字ずつか。

b, c, d, eも1文字ずつ
b, c, d, eも1文字ずつ

b, c, d, eも1回に1文字ずつ。
bufは256バイト読み込むはずだが。

qが入力されたら終了するようにしてある
qが入力されたら終了するようにしてある

qが入力されたら終了するようにしてあるので、こうなる。

こんな感じでシリアル通信できる。
今回は仮想ポートだが、ディスプレイポートの付いたマシン(緊急サブ用のLatitude)は無くもないので、いずれそれでの通信も扱いたいが・・・。いやはや大変だった・・・。

Sample/unixsyscall/TCSETS/TCSETS/src/TCSETS at master · bg1bgst333/Sample · GitHub

TCGETS

ioctlシステムコールにTCGETSを渡すことで、指定のファイルディスクリプタの指す端末情報を取得できる。

Man page of TTY_IOCTL

これを使うと、リダイレクトによるファイル出力か、標準入出力かを判定できる。

TCGETS1.cは標準出力について判定するものである。

TCGETS0.cは標準入力について判定するものである。
端末情報取得が成功ならtermios構造体のttyに端末情報が入る。
どちらもリダイレクトすると、-1が返る。

$ vi TCGETS1.c
$ gcc TCGETS1.c -o TCGETS1
$ ./TCGETS1
ABCDE
success
$ ls
TCGETS1  TCGETS1.c
$ ./TCGETS1 > test.txt
failure
$ cat test.txt
ABCDE
$ vi TCGETS0.c
$ gcc TCGETS0.c -o TCGETS0
$ ./TCGETS0
abc
success
buf = abc
$ ./TCGETS0 < test.txt
failure
buf = ABCDE
$

リダイレクトすると、端末ではなくファイルとなるため、失敗する。

Sample/unixsyscall/TCGETS/TCGETS/src/TCGETS at master · bg1bgst333/Sample · GitHub

stderr

stdinが標準入力のファイルポインタ、stdoutが標準出力のファイルポインタを表すように、stderrは標準エラー出力のファイルポインタを表す。

Man page of STDIN

perrorは標準エラー出力に出力する。
そして、stderrを指定すれば、fprintfでも標準エラー出力にメッセージを出力できる。

そのまま実行だと、標準出力と、標準エラー出力の違いがわからない。

$ vi stderr.c
$ gcc stderr.c -o stderr
$ ./stderr
errno = 0
errno = 2
strerror(errno) = No such file or directory
fopen error!: No such file or directory
stderr string
$ ./stderr > output.txt
fopen error!: No such file or directory
stderr string
$ cat output.txt
errno = 0
errno = 2
strerror(errno) = No such file or directory
$

そこで、リダイレクトしてみると、標準エラー出力だけがコンソール出力になり、標準出力はファイル出力になる。

Sample/c/stderr/stderr/src/stderr at master · bg1bgst333/Sample · GitHub

strerror

strerrorを使えとのことなので、strerrorの引数にerrnoを渡してみる。

Man page of STRERROR

strerror(errno)でも、

$ vi strerror.c
$ gcc strerror.c -o strerror
$ ./strerror
errno = 0
errno = 2
strerror(errno) = No such file or directory
fopen error!: No such file or directory
$

"No such file or directory"となる。

Sample/c/strerror/strerror/src/strerror at master · bg1bgst333/Sample · GitHub

sys_errlist

sys_errlistというグローバル配列にエラーメッセージが格納されている。
これに添字でerrnoを指定すると、そのerrnoのエラーメッセージを取得できる。

sys_errlist(3) manページ

sys_errlist[errno]の中身は、

$ vi sys_errlist.c
$ gcc sys_errlist.c -o sys_errlist
/usr/bin/ld: /tmp/ccuhxoeV.o: in function `main':
sys_errlist.c:(.text+0x5f): 警告: `sys_errlist' is deprecated; use
`strerror' or `strerror_r' instead
$ ./sys_errlist
errno = 0
errno = 2
sys_errlist[errno] = No such file or directory
fopen error!: No such file or directory
$

"No such file or directory"だった。
警告が出ている。
sys_errlistはdeprecatedなのか。

Sample/c/sys_errlist/sys_errlist/src/sys_errlist at master · bg1bgst333/Sample · GitHub

errno

何らかのエラーが発生した時、グローバル変数errnoにどんなエラーが発生したかエラー番号が格納される。

Man page of ERRNO

エラー発生前と、エラー発生後に、errnoを出力。
存在しないtest.txtを開いて、どうなるか見てみる。

$ vi errno.c
$ gcc errno.c -o errno
$ ./errno
errno = 0
errno = 2
fopen error!: No such file or directory
$

がんばるかも: Unix errno一覧

2は、"No such file or directory"らしいので合ってる。

Sample/c/errno/errno/src/errno at master · bg1bgst333/Sample · GitHub

リダイレクト(入力)

ファイルの中身を標準入力として受け取ることもできる。

https://www.kushiro-ct.ac.jp/yanagawa/C-2015/26-0722/index.html
もう一度基礎からC言語 第9回 制御構造と変数(5)~forとwhileに関するあれこれ 入出力の切り替え~リダイレクトとパイプ

dest.cは、

標準入力で入力された文字列をbufに格納し、bufを出力してるだけ。

$ vi dest.c
$ gcc dest.c -o dest
$ ./dest
ABCDE
buf = ABCDE
$ vi test.txt
$ cat test.txt
ABCDE
$ ./dest < test.txt
buf = ABCDE
$

dest単体だと、標準入力になるが、'<'とtest.txtの追加で、ファイル入力となった。

Sample/c/redirect/in/src/redirect at master · bg1bgst333/Sample · GitHub