Audio in OpenBSD

6月は衣替えの季節。この日本の佳き伝統に倣って、debian機も衣替えをしてあげよう。 と、言っても、ちんけにLXDEのテーマを変えようというのでは、つまらない。もっと大胆にね。

その為のコマンドは、taskselってものらしい。以前、何処かのサイトでお目にかかって、うろ覚えに記憶してたのさ。何でも、このコマンドは、Debianのインストール時に使われて、どんな性格のDebianを作るんかいをユーザーに決定してもらうみたい。

DeskTopが必要?要るなら、KDEにする?Gnomeにする? DeskTopは止めてファイルサーバー(きっと産婆さんが動くのだろう)にする?それともWebサーバーにするっていう、重大な岐路の設定を迫るやつだ。ここで人生決まってしまうんだなあ。

debian:~$ tasksel --list-task
i desktop       Debian desktop environment
u gnome-desktop GNOME
u xfce-desktop  Xfce
u kde-desktop   KDE
u cinnamon-desktop      Cinnamon
u mate-desktop  MATE
i lxde-desktop  LXDE
u web-server    web server
u print-server  print server
u ssh-server    SSH server
u debian-hamradio       Debian Hamradio Pure Blend
u hamradio-antenna      Debian Hamradio Antenna Packages
u hamradio-datamodes    Debian Hamradio Data Modes Packages
u hamradio-digitalvoice Debian Hamradio Digital Voice Packages
u hamradio-logging      Debian Hamradio Logging Packages
i hamradio-morse        Debian Hamradio Morse/CW Packages
u hamradio-nonamateur   Debian Hamradio Non-Amateur Modes Packages
u hamradio-packetmodes  Debian Hamradio Packet Mode Packages
u hamradio-rigcontrol   Debian Hamradio Rig Control Packages
u hamradio-satellite    Debian Hamradio Satellite Operation Packages
u hamradio-sdr  Debian Hamradio Software Defined Radio Packages
u hamradio-tools        Debian Hamradio Tools Packages
u hamradio-training     Debian Hamradio Training Packages
i laptop        laptop

あれ? インストール時にhamradio関係なんて、チラッとも出てこなかったけど、morseを過去に入れたおかげで、オイラーの性向を見抜かれてしたったんだろうな。これぞアマゾン流?

で、インストールは、

debian:~$ sudo tasksel install --new-install xfce-desktop

すれば良い。追加が終了したら、rebootする。guiのログインの右上の所でxfceを選び、ログイン。これにて、衣替え終了。

UBITX

ubitx The QRP HF General Coverage Transceiver you can build

129ドルで、インドから送られてくるそうな。

このページへ飛ぶと、送られてくるであろうボードの写真が目についた。オイラーの注目は、 クリスタルが8個並んでいた事。ひょっとしてクリスタルを並べたラダータイプのフィルターを 構成してないかい?

30MHzまでの信号をミキサーで一端45MHzまで上げ、フィルターを通した後、もう一度変換して12MHzにする。その後クリスタルフィルターを通してから検波するとな。いわゆるダブルコンバージョン方式ね。

おお、回路図も公開されてる。

ああ、7MHz専用のお値段控え目バージョンもあって、こちらは59ドル。全てインドの女性達が 手作業で組み立ていますとな。これらを購入する事は、間接的にインドの女性達の暮らしを支援する事になるんですとの事。

ソフトはアルディーノ用のC言語。ソースが公開されてるので、いじり放題か。 と、思ったら、パターンカットして、回路追加してる方がいた。 uBITX AGC追加改造 オイラーも、現役の頃は得意中の得意だったんよ。懐かしいな。

やや、 FT-818よりDC3001またはuBitx がいい こんな事を言ってる方も居たぞ。

audioctl

前回からの続き。audioが上手く動かないので、基本を確認。

ob6$ audioctl
name=eap0
mode=
pause=1
active=0
nblks=2
blksz=2205
rate=22050
encoding=u8
play.channels=1
play.bytes=0
play.errors=0
record.channels=2
record.bytes=0
record.errors=0
ob6$ audioctl -f /dev/audio0 rate=44100
audioctl: /dev/audio0: Invalid argument
ob6$ echo $?
1

なんだか良さそう。でも、最後に出て来るエラー 無効な引数ってのに納得がいかん。audioctlのソースを拝見。システムコールの後、エラーだったら、そのデバイス名を表示させてる。

エラーの場合は詳細な内容がerrnoだかに載ってくるんではなかろうか? そんな訳で、ちょいとコードを修正。

ob6$ diff -u /usr/src/usr.bin/audioctl/audioctl.c audioctl.c
--- /usr/src/usr.bin/audioctl/audioctl.c        Wed May 31 13:18:58 2017
+++ audioctl.c  Fri Jun  1 15:44:10 2018
@@ -24,6 +24,7 @@
 #include <unistd.h>
 #include <string.h>
 #include <err.h>
+#include <errno.h>

 /*
  * Default bytes per sample for the given bits per sample.
@@ -218,8 +219,11 @@
        argv += optind;

        fd = open(path, O_RDWR);
-       if (fd < 0)
-               err(1, "%s", path);
+       if (fd < 0) {
+       //      err(1, "%s", path);
+               perror(strerror(errno));
+               return errno;
+       }
        if (ioctl(fd, AUDIO_GETSTATUS, &rstatus) < 0)
                err(1, "AUDIO_GETSTATUS");
        if (ioctl(fd, AUDIO_GETDEV, &rname) < 0)

errnoをstrerror関数を使って、文字列に変換。それを表示してmainを抜ける。これで実行しても、やはり無効な引数って言われたぞ。と、ここまでやって、元から有ったerr関数の意味を 調べたら、オイラーが書いたコードを一発でやってくれてた。スマソ。

ob6$ ktrace -tc morseplayer
morseplayer: Could not open sndio device

システムトレースを取ってみる。

ob6$ kdump | grep open
 16508 morseplayer CALL  open(0xf713bb0f7b0,0x10000<O_RDONLY|O_CLOEXEC>)
 16508 morseplayer RET   open 3
   :
 16508 morseplayer CALL  open(0x7f7fffff9bc0,0x10005<O_WRONLY|O_NONBLOCK|O_CLOEXEC>)
 16508 morseplayer RET   open -1 errno 22 Invalid argument

それの結果から注目のopenだけを抜き出してみる。これいいな。なんでこういう便利な物に気が付いたか? 後で、出てきます。

OpenBSD in qemu

これはもう、裏側と言うかkernel側を覗くしかないな。いつかやった、 OpenBSD 探検 に倣って、vboxに入っているi386版のOpenBSDに入れてみるか。

が、インストールに時間がかかりすぎ。原因はi386な環境でi386をエミュレートしてるから? それともvboxが遅い? 乗り掛かった舟なんで、我慢してインストールが終わるのを待ったよ。

そして起動。これもめちゃくちゃ遅い。しかも最後はDISK FULLとか言われたぞ。DISKは700Mを指定したんで、余裕だと思っていたんだけどな。(勿論、X環境は無しです)

ob6$ du -sh /usr/share/relink/*
183M    /usr/share/relink/kernel
21.1M   /usr/share/relink/usr

これが伏兵でした。OpenBSD6.1だかから入った、カーネルと基本ライブラリィーのランダム化の機構の材料が置かれてるんだ。

少なくとも、/etc/rc の最後に有る、/usr/libexec/reorder_kernel ぐらいは、コメントアウトしときましょう。インストール時は、最後にshellに落ちて、捜査すればいいな。 まてよ? インストール時の環境では、viとか使える? せめてsedでも有ればいいんだけど、期待薄かな。

しょうがないので、VMWareに入れているamd64の環境下で、qemuを動かす事にした。下記は、各目的に合わせて、数々の起動方法を列挙した。全部まとめてshellに押し込めちゃった方が良かったかしら? それとも手間のかからない、Makefileかな。

ob6$ cat gui
#!/bin/sh
qemu-system-i386 -m 256 -s \
  -net nic -net user,hostfwd=tcp::2022-:22 disk > /dev/null 2>&1

X環境が有る場合。qemuのGUI画面が使える。

ob6$ cat begin
#!/bin/sh
qemu-system-i386 -m 256 disk -s -S > /dev/null 2>&1

X環境が有る場合。gdbから起動を制御出来る。

ob6$ cat cui
#!/bin/sh
qemu-system-i386 -m 256  -nographic -s \
  -net nic -net user,hostfwd=tcp::2022-:22 disk

Xが無い場合。立ち上がったころ合いを見計らって、sshでログイン

ob6$ cat com
#!/bin/sh
echo cu -l /dev/ttypN
qemu-system-i386 -m 256  -nographic -s \
  -serial pty \
  -net nic -net user,hostfwd=tcp::2022-:22 disk &

Xが無い場合。立ち上がったころ合いを見計らって、シリアルからログイン。ターゲット側の設定は、 Serial connection を参照。

ss63$ df -h
Filesystem     Size    Used   Avail Capacity  Mounted on
/dev/wd0a      686M    464M    188M    71%    /

リリースを重ねる度に容量が増えていくな。

QEMU audio

これで準備が整ったので早速、audioが使えるかFAQに則って確認。

ss63$ audioctl
audioctl: /dev/audioctl0: Device not configured

そんなの無いとな。オーディオはネットワークと違ってオプションの高級品なのね。サウンドカードを山田君の所から取り寄せる? QEMUのFAQに説明あるっしょ。

QEMU audio

ob6$ qemu-system-i386 -version
QEMU emulator version 2.11.1
Copyright (c) 2003-2017 Fabrice Bellard and the QEMU Project developers
ob6$ qemu-system-i386 -soundhw help
Valid sound card names (comma separated):
pcspk       PC speaker
hda         Intel HD Audio
cs4231a     CS4231A
gus         Gravis Ultrasound GF1
adlib       Yamaha YM3812 (OPL2)
ac97        Intel 82801AA AC97 Audio
es1370      ENSONIQ AudioPCI ES1370
sb16        Creative Sound Blaster 16

-soundhw all will enable all of the above

これがqemuでサポートしてるハードと言うかボードとな。古いものが主体で、上記のページでは、hdaとか言うインテル製を勧めていたぞ。

ob6$ qemu-system-i386 -audio-help
   :
Options are settable through environment variables.
Example:
  export QEMU_AUDIO_DRV=wav
  export QEMU_WAV_PATH=$HOME/tune.wav
(for csh replace export with setenv in the above)
  qemu ...

こちらは、隠れ環境変数とかドライバー関係の説明。軟弱なWindowsとかLinux野郎のための解説だな。

オイラーは、インテル嫌いなんで、OpenBSDのサポート具合と勘案して、

ob6$ cat audio
#!/bin/sh
qemu-system-i386 -m 256 -s \
  -soundhw es1370 \
  -net nic -net user,hostfwd=tcp::2022-:22 disk > /dev/null 2>&1

こんな、昔ながらの石を選んでみた。dmesgをみると

eap0 at pci0 dev 4 function 0 "Ensoniq AudioPCI" rev 0x00: apic 0 int 11
audio0 at eap0
midi0 at eap0: <AudioPCI MIDI UART>

こんな風に認識してた。早速ユーザーアプリにて確認

ss63$ audioctl
name=eap0
mode=
pause=0
active=0
nblks=2
blksz=960
rate=48000
encoding=s16le
play.channels=2
play.bytes=0
play.errors=0
record.channels=2
record.bytes=0
record.errors=0

なんだか大丈夫そう。morseplayerをコンパイルして、cwって名前のアプリを作った。

ss63$ echo hello | ./cw
cw: sio_setpar failed
ss63$ echo hello | ./cw
cw: Could not open sndio device

なんだか、エラーだな。初回と2回目の実行で違うエラーを吐いてきた。

ss63$ audioctl
name=eap0
mode=
pause=1
active=0
nblks=2
blksz=2205
rate=22050
encoding=u8
play.channels=1
play.bytes=0
play.errors=0
record.channels=2
record.bytes=0
record.errors=0

もう一度確認すると、少し設定が変わった。気になるのは、pauseってやつ。これって、デバイスがbusyになったままじゃなかろうか?

で、考えた。どのシステムコールで落ちているか調べてみよう。そこで、ktraceですよ。 初回と2回目の結果。

ss63$ kdump -f ktrace.fst
   :
  86691 cw       CALL  ioctl(3,AUDIO_SETPAR,0xcf7e31a8)
  86691 cw       RET   ioctl -1 errno 22 Invalid argument
ss63$ kdump -f ktrace.snd
   :
  5286 cw       CALL  open(0xcf7cf539,0x10005<O_WRONLY|O_NONBLOCK|O_CLOEXEC>)
  5286 cw       RET   open -1 errno 22 Invalid argument

不思議な事に、qemu配下のcwを動かすと母艦のVMWare側に入れたOpenBSDのaudioも動いていたっぽい。動作中になってますよ。

ob6$ audioctl
name=eap0
mode=play
pause=0
active=1
nblks=2
blksz=1024
rate=48000
encoding=s16le
play.channels=2
play.bytes=79835136
play.errors=0
record.channels=2
record.bytes=0
record.errors=0

ここで、立ち止まって考えた。qemuの中のaudioボードと言っても所詮は仮想なもの。音を 出したかったら、qemu側が動いて、上位にお願いしなければいけないはず。

Windows 10 Home edition
   VMWare 12 Player (12.5.9)
     OpenBSD 6.3 amd64
      morseplayer
       qemu-system-i386 2.11.1
         OpenBSD 6.3 i386
	   cw (morseplayer)

VMWare直下のOpenBSD amd64の中で、morseplayerを動かしても、エラーになる。と言う事は、VMWareがサボっていて、Windows下のボードに音を伝えていない可能性があるな。

同じ事は、Windows10に入れた、virtualboxでも言える。そう言えば、virtualboxはLinux主体でしか動かない中途半端な所があるな。ゲスト側のアドインを入れて、機能が拡張されるしね。VMWareだって、ユーザーツールがサポートされてるのは、Linux、FreeBSD、Solarisぐらいだものなあ。

OpenBSDなんて、その他のOSの扱いですよ。 OpenBSD、1985年に追加されたIntelの最新の誇大広告された機能を使わないことにより脆弱性を華麗に回避 こういう、パラノイアには、誰も付き合ってくれないのね。

証拠固め

VMWareには、Debian9が入っているんだ。そしてqemuも入ってるしね。という事で、i386版の仮想DISKを、Debian側に転送。

動かしたら、無事にモールスさんが聞こえましたよ。

気が付くののが遅いって! ポータブルOpenBSDをThinkPad/SL510に刺したら、何事もなく モールスしてたじゃない。この差に思いがいかなかったって事は、仮想を現実と理解してる節があるな。注意しないとな。

追跡

折角kernelの中まで潜っていけるようにしたんで、追ってみる。/dev/audioが、OpenBSD流のオーディオ界を代表するデバイス。(FreeBSDの場合は、/dev/dsp)

だから、audioopenに網を貼ってまってればいいのさ。同じような名前で、audio_openも有ったので、こちらにも網を張る。そして、cwを起動。

#0  audioopen (dev=10752, flags=<optimized out>, mode=8192, p=0xd16f4598) at /usr/src/sys/dev/audio.c:1599
#1  0xd02a6bad in spec_open (v=<optimized out>) at /usr/src/sys/kern/spec_vnops.c:158
#2  0xd0623302 in VOP_OPEN (vp=0xd165fad0, mode=65542, cred=0xd178aba0, p=0xd1772430) at /usr/src/sys/kern/vfs_vops.c:152
#3  0xd06a9a4e in vn_open (ndp=0xf3758f58, fmode=65542, cmode=<optimized out>) at /usr/src/sys/kern/vfs_vnops.c:165
#4  0xd0532a28 in doopenat (p=<optimized out>, fd=<optimized out>, path=<optimized out>, oflags=<optimized out>, mode=<optimized out>, retval=0xf37590a0) at /usr/src/sys/kern/vfs_syscalls.c:934
#5  0xd05328db in sys_open (p=0xd1772430, v=0xf37590a8, retval=0xf37590a0) at /usr/src/sys/kern/vfs_syscalls.c:873
#6  0xd08eb1b0 in mi_syscall (p=<optimized out>, code=<optimized out>, callp=<optimized out>, argp=<optimized out>, retval=<optimized out>) at /usr/src/sys/sys/syscall_mi.h:78
#7  syscall (frame=<optimized out>) at /usr/src/sys/arch/i386/i386/trap.c:610
#8  0xd07ee0c6 in Xsyscall ()
#9  0xf37590e8 in ?? ()

追って行くと、audio_openに行きついて

  audio_open(struct audio_softc *sc, int flags)
  {
          int error;
          int props;

B         if (sc->mode)
                  return EBUSY;
          error = sc->ops->open(sc->arg, flags);
          if (error)
                  return error;
          sc->active = 0;
          sc->pause = 1;
          sc->rec.blocking = 0;
          sc->play.blocking = 0;
           :
  =>      error = audio_setpar(sc);
          if (error)
                  goto bad;

そこから、audio_setparに入って行き

(gdb) bt 3
#0  eap_set_params (addr=0xd17e3200, setmode=1, usemode=<optimized out>, play=0xf3758c98, rec=0xf3758c80) at /usr/src/sys/dev/pci/eap.c:857
#1  0xd034f66d in audio_setpar (sc=<optimized out>) at /usr/src/sys/dev/audio.c:664
#2  0xd035029d in audio_open (sc=0xd17dca00, flags=<optimized out>) at /usr/src/sys/dev/audio.c:1254
(More stack frames follow...)

その中で、アーギュメントのエラーをセットしてる。やっと現場にたどり着いた。現場はPCIバスにぶら下がっている、eapって名前のボードを扱う所だ。

                switch (p->encoding) {
                case AUDIO_ENCODING_SLINEAR_LE:
                        if (p->precision != 16)
                                return EINVAL;
                        break;
                case AUDIO_ENCODING_ULINEAR_LE:
                case AUDIO_ENCODING_ULINEAR_BE:
                        if (p->precision != 8)
=>                              return EINVAL;

何か無理した設定をしてるんかな?