Linuxのデバイスファイルを意識して使ったこと が無い方へ入口の紹介。

Slides:



Advertisements
Similar presentations
Linux Device Driver 輪講 Che++. 第 6 章 キャラクタ型ドライバの高度な 機 第 1 回 (全 2 回)
Advertisements

ネットワーク・プログラミ ング カーネルの役割とプロセス生成. 1.1 OS の役割 仮想マシン OS はハードウェアの多様性 をカプセル化し、利用者を 複雑な処理から開放する。 プロセス管理 時間多重化により各プロセ スに CPU を割当てる。 メモリ管理 メモリ空間の多重化により、 各プロセスにメモリを割当.
プロセスの生成とコマンドの実行 プロセスの生成とコマンドの実行 プロセス生成のシステムコール プロセス生成のシステムコール プロセス生成のプログラム例 プロセス生成のプログラム例 プログラム実行のシステムコール プログラム実行のシステムコール 子プロセスの終了を待つシステムコール 子プロセスの終了を待つシステムコール.
Ddによる複製 2004/05/24 伊原 秀明(Port139).
Linuxを組み込んだマイコンによる 遠隔監視システムの開発
■プロセスとfileの関係 プロセスが一つファイルをオープンすると以下のようなデータ構造が作られる。
理学院 宇宙理学専攻 惑星物理学研究室 修士 2 年 徳永 義哉
ファイルキャッシュを考慮したディスク監視のオフロード
セキュリティ機構のオフロードを考慮した仮想マシンへの動的メモリ割当
システムプログラミング 第7回、8回 ファイルシステム関連の システムコール
榮樂 英樹 LilyVM と仮想化技術 榮樂 英樹
1.コンピュータと情報処理 p.20 第1章第1節 3.ソフトウェア ソフトウェア 基本ソフトウェア
組み込み環境における ユーザレベル・デバイスドライバの検討 (進捗報告)
システムプログラミング 第6回、7回 main関数の引数 usageメッセージ システムコールのエラーメッセージ ファイル
数理情報工学演習第一C プログラミング演習 (第3回 ) 2014/04/21
実行時のメモリ構造(1) Jasminの基礎とフレーム内動作
リダイレクト パイプ 標準入出力プログラム コマンド行引数 関数 system()
システムプログラミング 第5回 情報工学科 篠埜 功 ヒアドキュメント レポート課題 main関数の引数 usageメッセージ
OSとコマンド OS:コンピュータを使うための基本プログラム コマンド:OS上で使用できる命令 OS本体であるカーネルの内部コマンド
ファイルシステムとコマンド.
オペレーティングシステム (OSの機能と構造)
ファイルシステムキャッシュを 考慮した仮想マシン監視機構
Debian GNU/Linux ー Linuxインストールに必要な基礎知識 ー 三上 彩 鈴木 倫太郎
(original Takagi & Saito, 2007)
数値計算及び実習 第7回 プログラミングの基礎(5).
担当:青木義満 情報工学科 3年生対象 専門科目 システムプログラミング システムプログラミング プロセス間通信(パイプ) 担当:青木義満
アルゴリズムとデータ構造 補足資料6-3 「サンプルプログラムcat3.c」
Linuxカーネルについて 2014/01.
CC/7700,CC32を用いた データ収集システム 筑波大学 木村 博美 小松原 哲郎 (c)2007 木村博美 筑波大学.
システムプログラミング 第11回 シグナル 情報工学科  篠埜 功.
UNIXについて 松野秀平.
インターネット技術特論 B:コマンドライン, shell 山口 実靖
オペレーティングシステム2005 デバイス管理 (1)
型付きアセンブリ言語を用いた安全なカーネル拡張
プログラミング論 ファイル入出力
第8回 入出力装置1 インターフェース、ヒューマンデバイス
システムプログラミング 第6回、7回、8回 情報工学科 篠埜 功.
FreeBSDの デバイスドライバについて
情報・知能工学系 山本一公 プログラミング演習Ⅱ 第2回 ファイル処理 情報・知能工学系 山本一公
プログラミング演習I 2003年6月25日(第10回) 木村巌.
画像処理プログラムの説明.
実行時情報に基づく OSカーネルのコンフィグ最小化
プログラミング論 ファイル処理 (中級編)
オペレーティングシステム イントロダクション
プログラミング論 ファイル入出力
Ibaraki Univ. Dept of Electrical & Electronic Eng.
デジタル画像とC言語.
実践ロボットプログラミング LEGO Mindstorms EV3 で目指せロボコン!
演習1の解答例の解説 2006年11月8日 海谷 治彦.
システムプログラミング 第7回、8回 ファイルシステム関連の システムコール
2005年度 データ構造とアルゴリズム 第6回 「ハッシュ法を用いた探索」
Ibaraki Univ. Dept of Electrical & Electronic Eng.
C言語演習 情報ネットワーク特論.
システムプログラミング 第7回、8回 ファイルシステム関連の システムコール
Linux の世界に 触れてみよう! 情報実験 第 3 回 (2005/10/21)
オペレーティングシステム (OSの機能と構造)
千代浩司 高エネルギー加速器研究機構 素粒子原子核研究所
千代浩司 高エネルギー加速器研究機構 素粒子原子核研究所
プログラミング演習I 2003年7月2日(第11回) 木村巌.
システムプログラミング 第6回 システムコールのエラーメッセージ ファイルシステム 情報工学科 篠埜 功.
オペレーティングシステムJ/K 2004年10月4日
ネットワーク・プログラミング デバイスドライバと環境変数.
標準入出力、変数、演算子、エスケープシーケンス
ネットワーク・プログラミング 1対多のプロセス間通信.
オペレーティングシステム (OSの機能と構造)
システムプログラミング 第11回 シグナル 情報工学科  篠埜 功.
第3回簡単なデータの入出力.
千代浩司 高エネルギー加速器研究機構 素粒子原子核研究所
岩村雅一 知能情報工学演習I 第7回(後半第1回) 岩村雅一
岡村耕二 TCP通信プログラム 岡村耕二 情報ネットワーク.
Presentation transcript:

Linuxのデバイスファイルを意識して使ったこと が無い方へ入口の紹介。 /dev/* とミキサーデバイスの実装 〜デバイスドライバの入口〜 LILO Monthly Seminor 2007-4-28 and LUCK 勉強会(?) 2007-05-26 / 丸市展之 Linuxのデバイスファイルを意識して使ったこと が無い方へ入口の紹介。 /dev/* はワイルドカードでデバイスファイルを示しています。 サブタイトルとして「デバイスドライバの入口」と言うのをつけさせてもらいました。 …と言うことで初歩的なことにしか触れません。 デバイスドライバの話の前にユーザ・プログラムからドライバ使うところにも触れます。 最初に使った、Linux は、0.97 ですが当初はユーザとして使っているだけでした。その後に、アプリケーションなどを作ることで関わり始め、@niftyのFTOWNSやPlamo/TOWNSを作ったりしました。 広島でソフトウェアの開発やサーバーネットワークの管理を行っています。 別にLinux以外のUNIXでも通用する 部分がほとんどだと思います。 1

この文書について この文書は、Linux/TOWNSのカーネル添付文書用に書いた 「FM TOWNS のミキサーデバイスについて」(towns_mixer.txt) に 他デバイスについての使い方やデバイスドライバの構成などつい ての説明を加えた形でほぼ書き直し再構成した文書です。 元がカーネル添付の文書なので、GPL として公開します。 ただし、他の文書からの引用になっている部分やコードを引用し た部分もありますので、それらは元のコードのライセンスに従い ます。 (カーネル添付のものは、同じライセンスのはずです。) 2

説明の流れ ※ なおこのドライバは10年前の物です。 デバイス→デバイスファイル マウスの場合 Linux/TOWNSのミキサーの例 デバイスの説明(サウンドドライバ全般/ミキサー関連) ミキサーデバイスの使い方 アプリケーションからデバイスドライバまでの流れ ハードウェア関連 デバイスファイルを説明。 デバイスファイルを知っているか? 特定のデバイスを使うプログラムを作ったことがある? デバイスドライバを作ったことがある? ※ なおこのドライバは10年前の物です。

注意書き 今回はデバイスドライバのことについて話します が、この話は極めて初歩的な話であり、本来説明 すべき事がまったく足りていません。 ∴ 以下の点に注意して下さい。 実際の読み書きを行う部分がありません。 モジュールについて触れません。 各所の内容が古いです。 開発をされたい方は、別途書籍を購入することを お薦めします。 4

まず、Linuxを使っていてデバイス を使っていない人は…?。 Linuxを始めUNIX系のオペレーティングシステ ムでは、あらゆるデバイスをファイルとして抽 象化して使用するようになっています。 Linuxをインストールした方や使っている方は 、CD-ROMやハードディスクのデバイスファイ ルを使っています。 /dev/hda?, /dev/sd?, /dev/cdrom などを使ったはず。 /dev/console, /dev/mouse → ∴ いない。 5

デバイスファイルの紹介 デバイスを抽象化したものは、デバイスファイルもしくはスペ シャルファイルと呼ばれます。 $ ls -l /usr/share/man/ja_JP.eucJP/man4/ /usr/share/man/man4/ /usr/share/man/ja_JP.eucJP/man4/: 合計 164 -rw-r--r-- 1 root root 1128 2月 11日 2004年 atalk.4.gz -rw-r--r-- 1 root root 2119 2月 11日 2004年 console.4.gz -rw-r--r-- 1 root root 9048 2月 11日 2004年 console_codes.4.gz -rw-r--r-- 1 root root 7810 2月 11日 2004年 console_ioctl.4.gz -rw-r--r-- 1 root root 2320 2月 11日 2004年 dsp56k.4.gz -rw-r--r-- 1 root root 3633 2月 11日 2004年 fd.4.gz -rw-r--r-- 1 root root 1402 2月 11日 2004年 fifo.4.gz -rw-r--r-- 1 root root 1179 2月 11日 2004年 full.4.gz -rw-r--r-- 1 root root 2556 2月 11日 2004年 futex.4.gz -rw-r--r-- 1 root root 2101 2月 11日 2004年 hd.4.gz -rw-r--r-- 1 root root 4162 2月 11日 2004年 i82365.4.gz -rw-r--r-- 1 root root 5480 2月 11日 2004年 initrd.4.gz -rw-r--r-- 1 root root 1313 2月 11日 2004年 intro.4.gz -rw-r--r-- 1 root root 42 2月 11日 2004年 kmem.4.gz -rw-r--r-- 1 root root 3065 2月 11日 2004年 lp.4.gz -rw-r--r-- 1 root root 3223 2月 11日 2004年 magic.4.gz -rw-r--r-- 1 root root 1657 2月 11日 2004年 mem.4.gz -rw-r--r-- 1 digit users 2875 10月 26日 2005年 mouse.4.gz まず、マニュアルを見ましょう。 $ man 4 intro じゃあ、そのファイルを見ましょう。 $ ls /dev $ ls /dev X0R@ ptyb3 ptyp6 ptyt9 ptyxc tty15 ttya6 ttye9 ttysc ttywf adsp ptyb4 ptyp7 ptyta ptyxd tty16 ttya7 ttyea ttysd ttyx0 agpgart ptyb5 ptyp8 ptytb ptyxe tty17 ttya8 ttyeb ttyse ttyx1 audio ptyb6 ptyp9 ptytc ptyxf tty18 ttya9 ttyec ttysf ttyx2 bus/ ptyb7 ptypa ptytd ptyy0 tty19 ttyaa ttyed ttyt0 ttyx3 cdrom@ ptyb8 ptypb ptyte ptyy1 tty2 ttyab ttyee ttyt1 ttyx4 cdrom1@ ptyb9 ptypc ptytf ptyy2 tty20 ttyac ttyef ttyt2 ttyx5 console ptyba ptypd ptyu0 ptyy3 tty21 ttyad ttyp0 ttyt3 ttyx6 dsp ptybb ptype ptyu1 ptyy4 tty22 ttyae ttyp1 ttyt4 ttyx7 dvd@ ptybc ptypf ptyu2 ptyy5 tty23 ttyaf ttyp2 ttyt5 ttyx8 fb@ ptybd ptyq0 ptyu3 ptyy6 tty24 ttyb0 ttyp3 ttyt6 ttyx9 ドライバに関するマニュアルどんなマニュアルがあるか確認。 6

UNIX以外では、…。 MS-DOSも昔の売り文句には、「UNIXライクな*1みた いな…」ことが書いてありました。 じゃあ、MS-DOS(と言うか今のWindowsでは?) dir … C:\>dir NUL CON COM1 \\. のディレクトリ ファイルが見つかりません $ uname -a CYGWIN_NT-5.1 today 1.5.23(0.156/4/2) 2006-12-19 10:52 i686 Cygwin $ ls -l NUL CON COM1 COM2 COM3 LPT1 LPT2 LPT3 hogehoge ls: CON: No such file or directory ls: COM1: No such file or directory ls: COM2: No such file or directory ls: LPT1: No such file or directory ls: LPT2: No such file or directory ls: LPT3: No such file or directory ls: cannot access hogehoge: No such file or directory ---------- 1 ???????? ???????? 0 Jan 1 1970 COM1 ---------- 1 ???????? ???????? 0 Jan 1 1970 COM2 -rwxrwxrwx 0 Administrators SYSTEM 0 Jan 1 1970 COM3 ---------- 0 ???????? ???????? 0 Jan 1 1970 CON ---------- 1 ???????? ???????? 0 Jan 1 1970 LPT1 ---------- 1 ???????? ???????? 0 Jan 1 1970 LPT2 ---------- 1 ???????? ???????? 0 Jan 1 1970 LPT3 -rwxrwxrwx 0 Administrators SYSTEM 0 Jan 1 1970 NUL Windows のデバイスドライバのことは良く知らないのでMS-DOSについてですが、 と行ったところですが、 *** STOP 0x0000XXXX まぁ、これ以上追っかけてもしょうがないので… ? *1 当時の基準ですけど。 ( 確かメーカのカタログに書かれていたものです。) 7

Linuxのデバイスファイルを さらに追っかける。 参考としてマウスを見ます。 $ ls -l /dev/mouse lrwxrwxrwx 1 root root 5 4月 14日 2005年 /dev/mouse -> psaux リンクなので追っかけます。 $ ls -l /dev/psaux crw-rw-rw- 1 root sys 10, 1 1月 7日 13:39 /dev/psaux デバイスファイルの作成方法を見ると…。 man mknod マウスのデバイスファイルを ls -l で詳細を見ると左端の'c'となっていて、通常はファイルサイズが出ているところに2つの数字が書いてあります。 8

で、/dev/mouse を使うには? Mouseのマニュアルを見ましょう。 $ man 4 mouse

マニュアルを読むとデバイスからデータが読めるみたいだけど…。 まぁ、実際にデータを見てみましょう。 od -t x1 /dev/mouse $ od -t x1 /dev/mouse 0000000 18 ff 00 38 ff ff 38 fe fe 38 fd fe 38 fd fd 38 0000020 fc fd 38 fc fd 38 fc fd 38 fc fd 38 fc fd 38 fc 0000040 fd 38 fc fd 38 fc fc 38 fc fd 38 fb fc 38 fc fd 0000060 38 fe fd 38 fd fd 38 fd fd 38 fd fd 38 fb fc 38 0000100 fe fe 38 fd fe 38 fd fd 38 fd fe 38 fe fe 38 fe 0000120 fe 38 fe fe 38 fe fe 38 fe fe 38 fd fe 38 fd fd 0000140 38 fd fd 38 f9 fa 38 fc fc 38 fc fc 38 fc fd 38 0000160 fd fd 38 fd fd 38 fc fe 38 fc fd 38 fc fd 38 fb 0000200 fd 38 fc fc 38 fd fe 38 fc fd 38 fd fd 38 fc fd ... 0000440 08 01 03 08 03 03 08 02 03 08 02 04 08 02 04 08 0000460 02 04 08 02 03 08 02 03 08 01 03 08 02 02 08 01 byte d7 d6 d5 d4 d3 d2 d1 d0 1 0 0 dy8 dx8 1 0 rb lb 2 dx7 dx6 dx5 dx4 dx3 dx2 dx1 dx0 3 dy7 dy6 dy5 dy4 dy3 dy2 dy1 dy0 y X L R BEGIN 実際にこのマシンでやってみます。(gpm を切る) -- マウスが、動かないでデータが来ないようです届くデータは、X軸方向を左右、Y軸を上下に動かしたとき、マウスのボタンを操作したときで異なります。 なお、od は、8進数ダンプを見るコマンドですが、私は16進数の方が分かりやすいので、16進数形式をしていてダンプをとっています。 詳細を知りたい方は、od(1) を御覧下さい。 実際のデータの16進数値を見ていくと (1) 下の位(4bit)の値が、8になっているものが定期的に3バイト毎にくり返していることが分かります。 (2) パッドの操作とデータの推移を確認すると規則性があることが分かります。 とりあえず読み込めたし、分析したフォーマット でプログラムできそうかな…。 11

テストプログラム リスト 先程調べた規則性から、X座標とY座標およびボタンの変化を取り出すプログラムを作ってみました。 int main(int argc, char **argv) { int rCounter = 0; int rByte = 0; int x = 0, y = 0; int dx, dy; FILE *fp; MouseData mouseData; fp = fopen("/dev/mouse", "r"); while(fp != NULL && rByte != EOF) { rByte = fgetc(fp); mouseData.byteData[rCounter%3] = rByte; if((rCounter%3) == 2) { dx = ((mouseData.vaioMouse.dx8)?(-256):(0)) | mouseData.vaioMouse.dx7_0; dy = ((mouseData.vaioMouse.dy8)?(-256):(0)) | mouseData.vaioMouse.dy7_0; x += dx; y += dy; printf("Button (L:%d / R:%d) - " "x = %4d(%3d) , y = %4d(%3d)\n", mouseData.vaioMouse.lButtun, mouseData.vaioMouse.rButtun, x, dx, y, dy); } ++rCounter; fclose(fp); リスト #include <stdio.h> #include <stdlib.h> typedef union { unsigned char byteData[4]; struct { unsigned int lButtun:1; unsigned int rButtun:1; unsigned int reserved0_1:1; unsigned int startflag0:1; unsigned int dx8:1; unsigned int dy8:1; unsigned int reserved0:2; unsigned int dx7_0:8; unsigned int dy7_0:8; unsigned int reserved3:8; } vaioMouse; } MouseData; /dev/mouseをオープン データを読み込む 先程調べた規則性から、X座標とY座標およびボタンの変化を取り出すプログラムを作ってみました。 まず、データのフォーマットを共用体/構造体で示します。 次に読み取りプログラムを示します。 /dev/mouseをクローズ

実行結果 試してみました。 実演予定。 $ ./mouse-test Button (L:0 / R:0) - x = 1( 1) , y = 0( 0) Button (L:0 / R:0) - x = 2( 1) , y = 0( 0) Button (L:0 / R:0) - x = 3( 1) , y = 0( 0) Button (L:0 / R:0) - x = 4( 1) , y = 0( 0) Button (L:0 / R:0) - x = 4( 0) , y = -1( -1) Button (L:1 / R:0) - x = 4( 0) , y = -1( 0) Button (L:0 / R:0) - x = 4( 0) , y = -1( 0) Button (L:0 / R:1) - x = 4( 0) , y = -1( 0) Button (L:0 / R:0) - x = 3( -1) , y = -1( 0) 実演予定。 13

ミキサーデバイスの概要と実装 以降はFM TOWNSの Linux(Linux/TOWNS) 用に 実装したミキサーデバイスについてデバイスの 概要と実装について説明します。 TOWNS, Linux/TOWNS の概要 ミキサーデバイスへ 10年前に 2.0.29 ベースのカーネルで追加したデバイスドライバなので、 内容に古いところがあります。 14

FM TOWNS は、1989年に富士通が発表したマ ルチメディアパソコンです。 80386ベースに設計されたため元もと32bitで動 作させることを前提になっています。 全てのマシンにCD-ROMドライブが搭載されて います。 標準搭載のCD-ROMドライブを活用したフリー ソフトウェアをメーカがとりまとめて流通に載 せていた。 TOWNS には、80386 機以外にも486/Pentium機などもあったことを説明。 PC/AT互換機ベースにTOWNSのハードの主要な機能をPCIのボードとして組み込んだ、V-TOWNS もあります。 FMV-TOWNS FUJITSU ∞ 15

gccが移植されていた。(Linux以前に) FM TOWNS とLinux gccが移植されていた。(Linux以前に) 8bitのFMシリーズではOS-9/6809と言うOSがあ ったのでマルチタスク/マルチユーザーOSを望 むユーザが多かった。 FM TOWNS向けLinuxが、1992年7月21日にリリ ースされた。おそらくPC/AT互換機以外のアー キテクチャーへの移植は世界初。 パソコン通信 niftyのFM関係のフォーラムで(AT 互換機も含めた)Linux FAQが作られた。 →後のJFにつながる流れです。 16

TOWNSにミキサーデバイス… (1) TOWNSで標準搭載のCD-ROMを活用したフリ ーウェアの優れたCD-Playerが作られていた。 Linux/TOWNS ユーザを増やすためには、 X Window System 上で動くCD-Playerが必要と思 い、開発を始めた。 Linux/TOWNS用のCD-Playerを作った関係でボ リューム制御を行う必要が出てきた。 17

TOWNSにミキサーデバイス… (2) 当初は、Linux/TOWNSのサウンドドライバの作 者の方が直接I/O制御を行うプログラムを実装 されていたので、それを流用させてもらった。 元もとCD-ROMのデバイスドライバの仕様は TOWNS独自の実装だったが、Linux標準のドラ イバが開発されてPC/ATとの互換性が確保され た。ミキサーを使用可能にすれば、PC/AT互換 機で動かすことが可能となる。 18

TOWNSにミキサーデバイス… (3) TOWNSの電子ボリュームのハードウェアの仕 様上、現在のボリューム値を取得する際に情報 が失われる可能性があった。 一般的なLinuxのアプリケーションでTOWNSの ボリューム制御が可能になる。 19

TOWNSの /dev/mixer 実装前は? int set_volume(int indx, int volume_level) { int fd; char vol; char chan; if (volume_level >= 0) { chan = chan_data[indx].chan | 0x04; vol = volume_level; } else if (volume_level == -1) { chan = chan_data[indx].chan & ~0x04; vol = 0; } if ((fd=open("/dev/port",O_RDWR))<0) { return(EOF); lseek(fd,chan_data[indx].creg,0); write(fd,&chan,1); lseek(fd,chan_data[indx].dreg,0); write(fd,&vol,1); volume_buffering(indx, chan, vol); close(fd); return(0); 以前は、/dev/port を使って直接ポートを制御 if ((fd=open("/dev/port",O_RDWR))<0) { return(EOF); } lseek(fd,chan_data[indx].creg,0); write(fd,&chan,1); lseek(fd,chan_data[indx].dreg,0); write(fd,&vol,1); volume_buffering(indx, chan, vol); close(fd); $ man 4 port 20

TOWNSのミキサーデバイスの例 システム構成とデバイスドライバの基礎 Linux 標準のサウンドドライバの実装の調査 Linux/TOWNSのミキサーの例 TOWNSのサウンド関連のハード構成 Linux 標準のサウンドドライバの実装の調査 21

デバイスドライバの位置付け ユーザー空間 カーネル空間 カーネル空間とユーザー空間 ユーザプロセス システムコール デバイスドライバ カーネル空間 この図は、Plamo のメーリングリストで出てきた図です。 OSの中心部分のカーネルやデバイスドライバと、一般のプログラムは、CPUから見て違う権限で動作しています。 一般的なプログラムは、ユーザー空間と言う他のプログラムやOS側を誤動作などで破壊することが無いように保護された状態で動かすことができるようになっています。 内部構成については、Plamo のメーリングリストを参照。 http://www.linet.gr.jp/~kojima/Plamo/ML/htdocs/200310/msg00196.html 22

ユーザ操作から実際のI/O制御まで を順を追って見て行きます。 ユーザ プロセス ファイルシステム ファイルシステム ファイルシステム /* * NOTE: * read, write, poll, fsync, readv, writev, unlocked_ioctl and compat_ioctl * can be called without the big kernel lock held in all filesystems. */ struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendfile) (struct file *, loff_t *, size_t, read_actor_t, void *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*dir_notify)(struct file *filp, unsigned long arg); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); }; /* * NOTE: * read, write, poll, fsync, readv, writev can be called * without the big kernel lock held in all filesystems. */ struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); }; ユーザ プロセス ファイルシステム ファイルシステム ファイルシステム ファイルシステム ファイルシステム ハードウェア デバイス ドライバ デバイス ドライバ HDD デバイス ドライバ デバイス ドライバ SCSI サウンド を順を追って見て行きます。 その他… ネットワーク

ミキサーデバイスを作るには? デバイスドライバの事を調べる。 サウンド関連のハードウェアを調べる。 どれが、ボリューム制御可能か? など サウンド関連のハードウェアを調べる。 どれが、ボリューム制御可能か? など サウンド関連のデバイスドライバを調べる。 ミキサーは他のサウンドドライバに影響する。 Linux標準とするため(主にAT互換機の環境で) どのように実装されているかを調べる。 24

ミキサーデバイス…って。 サウンドデバイスの中でソフトウェアで制御す るボリュームのことです。 つまりサウンドデバイスの一部です。 サウンド関連のデバイスには、CD, PCM音源な どから構成されています。 どの部分のボリュームを制御できるかなどは、 ハード的なサウンド関係の構成に大きく依存し ます。 25

FM TOWNSのサウンド機能 TOWNS独自のものもあるかもしれませんが、 特殊なものではありません。 個々のものをあげて行きます。 CD-ROMドライブ (CD Digital Audio出力) MIC ヘッドフォン端子 LINE IN スピーカ LINE OUT FM音源 個々のものをあげて行きます。 (1) TOWNS の特徴である CD-ROM ドライブ。 (2) マイク(内蔵/MIC端子)とライン入力(HR背面) PCM音源 その他デバイス(オプション) 内蔵モデム, AD-PCM 26

Linuxのサウンドドライバ 現在のカーネル2.6系になってからは、ALSAが 標準のドライバに置き換わり*1ましたが、私が TOWNSでドライバを作った段階では、OSSが 標準でした。TOWNSで実装されているもの は、OSSの実装に近いもので互換性がありま す。 27

OSS/Freeのデバイスファイル /usr/src/linux/Documentation/devices.txt 14 char Open Sound System (OSS) 0 = /dev/mixer Mixer control 1 = /dev/sequencer Audio sequencer 2 = /dev/midi00 First MIDI port 3 = /dev/dsp Digital audio 4 = /dev/audio Sun-compatible digital audio 6 = /dev/sndstat Sound card status information {2.6} 7 = /dev/audioctl SPARC audio control device 8 = /dev/sequencer2 Sequencer -- alternate device 16 = /dev/mixer1 Second soundcard mixer control 17 = /dev/patmgr0 Sequencer patch manager 18 = /dev/midi01 Second MIDI port 19 = /dev/dsp1 Second soundcard digital audio 20 = /dev/audio1 Second soundcard Sun digital audio 33 = /dev/patmgr1 Sequencer patch manager 34 = /dev/midi02 Third MIDI port 50 = /dev/midi03 Fourth MIDI port カーネル添付文書より引用。 28

TOWNSのサウンドデバイス デバイスファイル一覧 TOWNSのサウンドデバイスは、OSS/Freeの仕様にあわせて実装にされています。しかし、FM TOWNS向けのデバイスが追加されていることや、実装の過程から独自部分が多くなっています。 29 29

OSS/Freeのミキサーデバイス ミキサーデバイスで実装すべき機能を調べる。 ミキサーを使うプログラムの処理を調べる。 ミキサーデバイスで実装すべき機能を調べる。 ミキサーを使うプログラムの処理を調べる。 #include <stdio.h> #include <stdlib.h> /*********************************************** * I/O 制御のためのインクルード・ファイル ***********************************************/ #include <fcntl.h> #include <sys/types.h> * Linux のサウンド関係インクルード・ファイル #include <linux/soundcard.h> void deviceList(int mixer) { int devices; int streoDevices; int counter, bitCounter; static const char *stereoOrMono[] = {"mono", "stero"}; const char *soundDeviceNames[] = SOUND_DEVICE_NAMES; const char *soundDeviceLabels[] = SOUND_DEVICE_LABELS; if(ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &devices) >= 0 && ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &streoDevices) >= 0) { for(counter = 0, bitCounter = 0x001; counter < SOUND_MIXER_NRDEVICES;++counter, bitCounter <<= 1) { printf("%s %s\n", soundDeviceNames[counter], stereoOrMono[(streoDevices & bitCounter) != 0]); } int main(int argc, char **argv) { int mixer; mixer = open("/dev/mixer", O_RDWR); ・ ・(この部分にミキサーを操作するコードを記述する) close(mixer); return(0); }

「今の」ってどんなのI/O操作? Ioctl() とは? では、ioctl(2) を見ましょう。 $ man 2 ioctl ユーザ /* * NOTE: * read, write, poll, fsync, readv, writev can be called * without the big kernel lock held in all filesystems. */ struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char *, size_t, loff_t *); ssize_t (*write) (struct file *, const char *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); }; ユーザ プロセス static struct file_operations fmmidi_fops = { read: fmmidi_read, write: fmmidi_write, poll: fmmidi_select, ioctl: fmmidi_ioctl, mmap: fmmidi_mmap, open: fmmidi_open, release: fmmidi_release, }; ・ static int __init init_tsound(void) { if(devfs_register_chrdev(SOUND_MAJOR, DRIVER_NAME, &fmmidi_fops)==-1) printk(KERN_ERR DRIVER_NAME ": sound device already in use.\n"); return -EBUSY; } devfs_handle = devfs_mk_dir (NULL, DRIVER_NAME, NULL); pcm16_inst=pcm16_init(); static int fmmidi_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg ) { int dev; dev = inode->i_rdev; dev = MINOR(dev); if( cmd == FMMINIT ){ if( MID_sysInit() ){ printk( DEVICE_NAME ":MIDI initializing fail.\n" ); return -EIO; } return 0; switch( dev & FMIDI_MINOR_MASK ){ case FMIDI_SEQCTL: return seq_ioctl( inode, file, cmd, arg ); case FMIDI_PCM: return pcm_ioctl(inode, file, cmd, arg); case FMIDI_MIXER: return towns_mixer_ioctl(inode, file, cmd , arg); break; case FMIDI_PCM16: return pcm16_ioctl( inode, file, cmd , arg); case FMIDI_FM: return fmpcm_ioctl( inode, file, cmd, arg ); return -EPERM; ファイルシステム ファイルシステム ファイルシステム ファイルシステム ファイルシステム ハードウェア デバイス ドライバ デバイス ドライバ HDD デバイス ドライバ デバイス ドライバ SCSI サウンド その他… ネットワーク

TOWNSのサウンドドライバ サウンドドライバのシステムコール各機能

ミキサーの機能 (ioctl()) SOUND_MIXER_READ_DEVMASK ボリューム制御可能な対象を取得。 SOUND_MIXER_READ_STREODEV LR 独立のステレオボリュームな対象を取得。 SOUND_MIXER_READ_RECMASK 録音可能な音源として選択可能な対象を取得。 SOUND_MIXER_READ_RECSRC 録音用の対象となっている対象を取得。 SOUND_MIXER_WRITE_RECSRC 録音用の対象となる対象を設定します。 SOUND_MIXER_READ_xxx / MIXER_READ(SOUND_MIXER_xxx) ボリューム・レベルの取得 SOUND_MIXER_WRITE_xxx / MIXER_WRITE(SOUND_MIXER_xxx) ボリューム・レベルの設定 33 33

SOUND_MIXER_READ_DEVMASK ボリューム制御可能なチャンネルを取得します。 制御可能なチャンネルは、SOUND_MIXER_xxxと一対一で対応する SOUND_MASK_xxx と言う名前のマスクビットの論理和で表される。 (下の表を参照) TOWNS では、 全ミュート/ FM 音源 / 8bit PCM 音源/ 16bit PCM 音源(*10) / CD オーディオ / ライン入力 / マイク入力 / モデムカードとなります。 ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &devices);

/usr/include/linux/soundcard.h /* * Mixer devices * * There can be up to 20 different analog mixer channels. The * SOUND_MIXER_NRDEVICES gives the currently supported maximum. * The SOUND_MIXER_READ_DEVMASK returns a bitmask which tells * the devices supported by the particular mixer. */ #define SOUND_MIXER_NRDEVICES 25 #define SOUND_MIXER_VOLUME 0 #define SOUND_MIXER_BASS 1 #define SOUND_MIXER_TREBLE 2 #define SOUND_MIXER_SYNTH 3 #define SOUND_MIXER_PCM 4 《中略》 /* Device mask bits */ #define SOUND_MASK_VOLUME (1 << SOUND_MIXER_VOLUME) #define SOUND_MASK_BASS (1 << SOUND_MIXER_BASS) #define SOUND_MASK_TREBLE (1 << SOUND_MIXER_TREBLE) #define SOUND_MASK_SYNTH (1 << SOUND_MIXER_SYNTH) #define SOUND_MASK_PCM (1 << SOUND_MIXER_PCM)

ボリューム設定可能な対象 OSS/Freeのボリューム設定対象 36

SOUND_MIXER_READ_STREODEV LR 独立のステレオボリュームが扱えるチャンネルを取得します。 LR 独立での制御可能なチャンネルは、SOUND_MIXER_xxx と一対一で対応するSOUND_MASK_xxx と言う名前のマスクビットの論理和で表される。 TOWNS では、 CD オーディオ/ ライン入力/ 16bit PCM 音源10となります。 ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &streoDevices); チャンネルの取得形式は、 SOUND_MIXER_READ_DEVMASK と同じ形式です。

SOUND_MIXER_READ_RECMASK 録音可能な音源として選択可能なチャンネルを取得します。 制御可能なチャンネルは、SOUND_MIXER_xxxと一対一で対応するSOUND_MASK_xxx と言う名前のマスクビットの論理和で表される。 TOWNS では、得に録音のみのON/OFF が可能なチャンネルが無く、常にライン出力される音と同じものが録音の対象になります。 このため便宜上、 TOWNS では、 0 を返すことにしています。 チャンネルの取得形式は、 SOUND_MIXER_READ_DEVMASK と同じ形式です。

SOUND_MIXER_READ_RECSRC 録音用の対象となっているチャンネルを取得します。 制御可能なチャンネルは、SOUND_MIXER_xxxと一対一で対応するSOUND_MASK_xxx と言う名前のマスクビットの論理和で表される。 これは、 SOUND_MIXER_READ_DEVMASK と同じ形式です。 TOWNS では、 CD オーディオ/ ライン入力/ マイク入力としています。 チャンネルの取得形式は、 SOUND_MIXER_READ_DEVMASK と同じ形式です。

SOUND_MIXER_WRITE_RECSRC 録音用の対象となっているチャンネルを設定します。 TOWNS では、CD オーディオ/ ライン入力/ マイク入力としていて固定です。 また、TOWNS では機器の選択が不可能なため変更できません。 チャンネルの取得形式は、 SOUND_MIXER_READ_DEVMASK と同じ形式です。

SOUND_MIXER_READ_xxx or MIXER_READ(SOUND_MIXER_xxx) ボリューム・レベルの取得 xxx の部分は、 取得したいチャンネルになります。 これらのラベルは、 以下のような関係になっています。 #define SOUND_MIXER_READ_xxx \ MIXER_READ(SOUND_MIXER_xxx) つまり、 channel_no = SOUND_MIXER_CD; ioctl(fd, SOUND_MIXER_READ_CD, &volume); ioctl(fd, MIXER_READ(channel_no), &volume);

SOUND_MIXER_WRITE_xxx or MIXER_WRITE(SOUND_MIXER_xxx) ボリューム・レベルの設定 xxx の部分は、 設定したいチャンネルになります。 これらのラベルは、 以下のような関係になっています。 #define SOUND_MIXER_WRITE_xxx \ MIXER_WRITE(SOUND_MIXER_xxx) つまり、 channel_no = SOUND_MIXER_CD; ioctl(fd, SOUND_MIXER_WRITE_CD, &volume); ioctl(fd, MIXER_WRITE(channel_no), &volume);

ミキサーの機能 (ioctl()) SOUND_MIXER_READ_DEVMASK ボリューム制御可能な対象を取得。 int volume = (left & 0xff) | ((right & 0xff) << 8); ioctl(fd, MIXER_WRITE(channel_no), &volume); SOUND_MIXER_READ_DEVMASK ボリューム制御可能な対象を取得。 int devices; ioctl(mixer, SOUND_MIXER_READ_DEVMASK, &devices); SOUND_MIXER_READ_STREODEV LR 独立のステレオボリュームな対象を取得。 SOUND_MIXER_READ_xxx / MIXER_READ(SOUND_MIXER_xxx) ボリューム・レベルの取得 int streoDevices; ioctl(mixer, SOUND_MIXER_READ_STEREODEVS, &streoDevices); SOUND_MIXER_WRITE_xxx / MIXER_WRITE(SOUND_MIXER_xxx) ボリューム・レベルの設定 int left, right, volume; ioctl(fd, MIXER_READ(channel_no), &volume); left = volume & 0x000000ff; right = (volume & 0x0000ff00) >> 8; 43 43

サウンドデバイス→ミキサー サウンドデバイスからミキサーデバイスへ ファイルシステム ユーザ プロセス HDD デバイス ドライバ SCSI /* Device Driver routines. */ static int fmmidi_open( struct inode *inode, struct file *file) { int dev,ret; dev = inode->i_rdev; dev = MINOR(dev); switch( dev & FMIDI_MINOR_MASK ){ 《中略》 case FMIDI_PCM: ret=pcm_open( inode, file ); break; case FMIDI_MIXER: ret=towns_mixer_open( inode, file ); case FMIDI_PCM16: ret=pcm16_open( inode, file ); default: ret=-ENODEV; } if (!ret) { MOD_INC_USE_COUNT; return ret; static int fmmidi_release( struct inode *inode, struct file *file) { int dev; dev = inode->i_rdev; dev = MINOR(dev); switch( dev & FMIDI_MINOR_MASK ){ case FMIDI_MIDI: midi_release( inode, file ); break; 《中略》 case FMIDI_PCM: pcm_release( inode, file ); case FMIDI_MIXER: towns_mixer_release( inode, file ); case FMIDI_PCM16: pcm16_release(inode,file ); case FMIDI_FM: fmpcm_release( inode, file ); } MOD_DEC_USE_COUNT; return 0; static int fmmidi_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg ) { int dev; dev = inode->i_rdev; dev = MINOR(dev); if( cmd == FMMINIT ){ if( MID_sysInit() ){ printk( DEVICE_NAME ":MIDI initializing fail.\n" ); return -EIO; } return 0; switch( dev & FMIDI_MINOR_MASK ){ case FMIDI_SEQCTL: return seq_ioctl( inode, file, cmd, arg ); case FMIDI_PCM: return pcm_ioctl(inode, file, cmd, arg); case FMIDI_MIXER: return towns_mixer_ioctl(inode, file, cmd , arg); break; case FMIDI_PCM16: return pcm16_ioctl( inode, file, cmd , arg); case FMIDI_FM: return fmpcm_ioctl( inode, file, cmd, arg ); return -EPERM; ユーザ プロセス デバイス ドライバ ファイルシステム ネットワーク ハードウェア HDD SCSI サウンド その他…

TOWNSミキサーのioctl()処理 ソースコード switch(dev) { case SOUND_MIXER_RECSRC: int towns_mixer_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) { int vol; int dev = cmd & 0xff; TownsMixerDevices *mixer = NULL; if(dev >= 0 && dev < SOUND_MIXER_NRDEVICES) mixer = &townsMixers[dev]; if (cmd == SOUND_MIXER_INFO) { mixer_info info; strncpy(info.id, "TOWNS", sizeof(info.id)); strncpy(info.name, "FM-TOWNS", sizeof(info.name)); info.modify_counter = modify_counter; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; strncpy(info.name, "TOWNS", sizeof(info.name)); if (((cmd >> 8) & 0xff) == 'M') { modify_counter++; if (_IOC_DIR(cmd) & _IOC_WRITE) { if(mixer != NULL && mixer->setVolumeLevel != NULL) { int tmp; get_user(tmp, (int *) arg); vol = mixer->setVolumeLevel(mixer, tmp); return(ioctl_return((int*)arg, ((vol >= 0)?(vol):(0)))); } else switch(dev) { case SOUND_MIXER_RECSRC: return(ioctl_return((int*)arg, townsSystemMixer.recordableMask)); break; case SOUND_MIXER_DEVMASK: return(ioctl_return((int*)arg, townsSystemMixer.enableDevMask)); case SOUND_MIXER_STEREODEVS: return(ioctl_return((int*)arg, townsSystemMixer.stereoDevices)); case SOUND_MIXER_RECMASK: return(ioctl_return((int*)arg, 0)); /*return(ioctl_return((int*)arg, townsSystemMixer.recordableMask));*/ case SOUND_MIXER_CAPS: return(ioctl_return((int*)arg, SOUND_CAP_EXCL_INPUT)); default: if(mixer != NULL && mixer->getVolumeLevel != NULL) { vol = mixer->getVolumeLevel(mixer); return(ioctl_return((int*)arg, ((vol >= 0)?(vol):(0)))); } } else { return(-(EINVAL));

ミキサーの初期化処理 デバイスの初期化 void towns_mixer_init(void) { townsSystemMixer.busy = 0; townsSystemMixer.enableDevMask = SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1; townsSystemMixer.recordableMask = SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD; townsSystemMixer.stereoDevices = SOUND_MASK_LINE | SOUND_MASK_CD; if(townsSystemMixer.pcm16enable) { mb87078volumes[0].chan = 0x01; mb87078volumes[0].chan = 0x03; townsSystemMixer.enableDevMask |= SOUND_MASK_ALTPCM; townsSystemMixer.stereoDevices |= SOUND_MASK_ALTPCM; townsMixers[SOUND_MIXER_ALTPCM] = Pcm16mixer; } /********************** * sound.treble = 0; * sound.bass = 0; **********************/ setTownsVolume(LINE_L, MAXIMUM_LEVEL); /* line input-L: enable(0dB) */ setTownsVolume(LINE_R, MAXIMUM_LEVEL); /* line input-R: enable(0dB) */ setTownsVolume(ADDA_L, MAXIMUM_LEVEL); /* 16bit PCM-L:enable(0dB) */ setTownsVolume(ADDA_R, MAXIMUM_LEVEL); /* 16bit PCM-R:enable(0dB) */ } else { mb87078volumes[0].creg_mask &= ~2; townsVolChanel[LINE_L].creg_mask &= ~2; townsVolChanel[LINE_R].creg_mask &= ~2; setTownsVolume(CDDA_L, MAXIMUM_LEVEL); /* CDDA output-L:enable(0dB) */ setTownsVolume(CDDA_R, MAXIMUM_LEVEL); /* CDDA output-R:enable(0dB) */ setTownsVolume(MIC_IN, MINIMUM_LEVEL_MUTE); /* MIC input: enable(mute) */ setTownsVolume(MODEMI, MAXIMUM_LEVEL); /* Modem monitor:enable(0dB) */

参考文献 LINUX マルチメディアガイド 著者:Jeff Tranter 訳者:山形浩生 ISBN4-900900-31-1 改訂3 版FM TOWNS テクニカルデータブック 著者:千葉憲昭 ISBN4-7561-0467-3 改訂版UNIX デバイスドライバ 著者:Janet I. Egan / Thomas J. Teixeira 監訳:三田典玄 翻訳:野中浩一/ 大西照代 Linux デバイスドライバ 第3版 著者:Janathan Corbet / Alessandro Rubini / Greg Kroah-Hartman 訳者:山崎 康宏 / 山崎 邦子 / 長原 宏治 / 長原 陽子

参考資料 JF 文書(http://www.linux.or.jp/JF/) The Linux Sound HOWTO 著者:Jeff Tranter 訳者:小島 三弘/ 水原文 The Linux Sound Playing HOWTO 著者:Yoo C. Chung 訳者:藤原輝嘉