ラベル 仮想GPIO の投稿を表示しています。 すべての投稿を表示
ラベル 仮想GPIO の投稿を表示しています。 すべての投稿を表示

2021/04/04

WSL2で仮想GPIOのアクセス権設定

 WSL2では、systemdが動かないのでudevが動いていないため、sysfsのGPIOのアクセス権が設定がされません。

 このため、GPIO操作するために毎度rootになるか、いちいちchmod,chownしないといけません。
 これは結構面倒なので、何とかしたいと思います。

 その前に、sysfsのgpio周りを、RaspberryPIと同じアクセス権設定にしたいと思います。
 まず準備として、gpioグループを作り、ログインユーザーをgpioグループへ追加します。
$ sudo groupadd gpio
$ sudo gpasswd $USER gpio

 idコマンドでgpioグループに入ったことが確認できます。

 そして、udevを使う場合は、gpio用のルールファイルを作っておきます。
 以下のファイルを /etc/udev/rules.d に 99-gpio.rules の名前で保存します。
SUBSYSTEM=="gpio", GROUP="gpio", MODE="0660"
SUBSYSTEM=="gpio*", PROGRAM="/bin/sh -c '\
        chown -R root:gpio /sys/class/gpio && chmod -R 770 /sys/class/gpio;\
        chown -R root:gpio /sys/devices/virtual/gpio && chmod -R 770 /sys/devices/virtual/gpio;\
        chown -R root:gpio /sys$devpath && chmod -R 770 /sys$devpath\
'"


 さて、それでは、アクセス権の設定方法は以下のいずれかが考えられます。
  1. daemonizeとgenieをインストールし、systemdをPID=1で動かす
  2. udevだけ無理やり動かす
  3. スクリプトを作ってexport/unexportで毎回設定する
 1の方法は、インターネットで「WSL2 systemd」で検索するとやり方がでてきます 。
 でも、なんか、ゴチャゴチャ入れたくないなぁ、と思います。

 2の方法ですが、試してみると、PID=1でなくても、動作するようです。
$ /etc/init.d/udev start


 すると、「インタラクティブシェルから実行されてます。多分、意図したことではないので、60秒待ちます。Ctrl+Cで止めてね。」というような文言がでてきますが、かまわず60秒待ちます。その後、

となってudevの起動が成功します。
後は gpio-mockup を modprobe で起動すれば、RaspberryPI で操作するのと同じ感じにできます。

udev起動時に60秒止められるのが嫌なら、/etc/init.d/udev スクリプト内で sleep(60)しているところを消してしまえばいいです(自己責任でお願いします)
 /etc/init.d/udev スクリプトから一部抜粋


 3の方法は、export/unexportするときに、全てのアクセス権を1つずつ設定していくようなスクリプトを作ればよいのでしょうが、そこまでするなら、sysfsインターフェースのgpioは使うのをやめたほうが良いでしょう。新しいgpioインターフェースにしましょう。

 以上が私なりの方法でした。
 まさか、WSL2がsystemd動いていないとは、思いませんでした。
 dockerはどうなんでしょうね。WSLやめてdockerにしようか。。。

2021/04/03

WSL2で仮想GPIO

   WSL2でLinuxカーネルのgpio-mockup機能を使って仮想GPIOを使えるようにします。


●今回の環境

 Windows10


 ディストリビューション

 カーネルバージョン

 今回の環境はまっさらなWSLで説明しますが、前回の仮想CANのカスタムカーネルの環境でもできます。

 Linuxカーネルをビルドできる環境が必要です。下記を参考にしてビルド環境を用意してください。

●方法

 GPIO機能、GPIO-MOCKUP機能を有効にしたカスタムカーネルをビルドします。
 カスタムカーネルができれば、あとはgpio-mockupモジュールをロードし、仮想GPIOデバイスを作成すれば、GPIOが無いPC上でGPIO操作ができるようになります。

●カスタムカーネルの設定手順

 menuconfigを立ち上げます。
$ make menuconfig

手順1:Device Drivers に入ります



手順2:GPIO Support を有効にする

 Device Drivers 内へ入り(リターンで入る)、GPIO Support を有効にします。


手順3:GPIO Testing Driver を有効にする

 GPIO Support 内へ入り、GPIO Testing Driver を有効にする。


 GPIO Testing Drivber が仮想GPIOドライバです。
 また、GPIOのsysfsインターフェースが必要なら、「/sys/class/gpio/... (sysfs interface)」も選択します。
 利用できるGPIOの数は「Maximum nuber of GPIOs for fast path」で設定します。

手順4:設定を保存して終了する

ここまでできたら、忘れずに設定を保存して menuconfig を終了します。
また、Linuxカーネル名を変えておくとよいでしょう。


手順5:カーネルをビルドし、仮想GPIOのLKMをインストールする

$ make
$ sudo make modules_install
 → /lib/modules の下に、カーネル名でディレクトリができます。
  この下の kernel ディレクトリ以下に仮想GPIOの gpio-mockup.ko ファイルが格納されます。

手順6:カスタムカーネルでWSL2を起動する

 今作った vmlinux ファイルでWSL2を再起動します。
 やり方は、以下の手順6~を参考にしてください。

手順7:GPIOを有効にしたカーネルで起動できていることを確認する

$ uname -a
Linux earth 5.4.72-microsoft-standard-WSL2-gpio #1 SMP Wed Mar 2 20:32:16 JST 2021 x86_64 x86_64 x86_64 GNU/Linux

 ここまでが、GPIOを有効にしたカスタムカーネルの作り方でした。


●仮想GPIOデバイスを作成する

gpio-mockupドライバをmodprobeコマンドで読み込みます。

書式:
    # modprobe gpio-mockup gpio_mockup_ranges=[x,y](,…,[x,y]) (gpio_mockup_named_lines)

    x:lineの開始番号、または、-1
    y:lineの終了番号、または xが-1のときlineの個数
 ※x = -1 のとき、割り当ては後ろからになるので注意

例1:gpio0~gpio7 の8つのGPIOを割り当てる
$ sudo modprobe gpio-mockup gpio_mockup_ranges=0,8
 → /dev の下に、gpiochip0 ができ、8line が使用できる
  また、sysfsインターフェースがある場合は、gpio0~7 までexportできる

例2:gpiochip0に4つ、gpiochip1に8つのGPIOを割り当てる
$ sudo modprobe gpio-mockup gpio_mockup_ranges=0,4,8,16
 → /dev の下に、gpiochip0 と gpiochip1 ができ、それぞれ 4line と 8line が使用できる
  また、sysfsインターフェースでは、gpio0~3 と gpio8~15 までexportできる

例3:gpiochip0とgpiochip1に後ろから8つずつGPIOを割り当てる
$ sudo modprobe gpio-mockup gpio_mockup_ranges=-1,8,-1,8
 → /dev の下に、gpiochip0 と gpiochip1 ができ、それぞれ 8line が使用できる
  また、sysfsインターフェースでは、gpio504~511 と gpio496~503 までexportできる

例4:各ラインにラベルを付ける
$ sudo modprobe gpio-mockup gpio_mockup_ranges=0,8,8,16 gpio_mockup_named_lines
 → 各ラインにラベル名が gpio-mockup-A-*、gpio-mockup-B-* という名前が付く
  また、sysfsインターフェースでは、gpio-mockup-A-*、gpio-mockup-B-*というような名前でexportされる


以上で仮想GPIOが使えるようになります。
libgpiod tools も普通に使えます。


●sysfsインターフェースのアクセス権について

 WSLでは、initプロセスが動いていないので、udevが動作していないため、/sys/class/gpio 以下のアクセス権がすべて root:root になってしまいます。
これらの対処法は次回まとめます。

2019/05/06

仮想GPIOドライバひとまず完成

仮想GPIOドライバがひとまず完成しました。

GitHubの以下の場所にアップしています。
vgpio

動作確認は、ubuntu 18.04 (2019/05/06時点のupdate済)でしました。

●使い方
0.ビルドする。
cd vgpio
make
→vgpio.koができる。

1.ドライバをinsmodする。
sudo insmod vgpio.ko
→/sys/kernel/vgpioができる。

2.使いたいGPIOポートを作成する。
(例)GPIO23を作成する場合
echo 23 > /sys/kernel/vgpio/export
→GPIO23ができる。

あとは、通常のGPIOと同じ使い方です。

3.direction、value、edge、active_lowを読み書きする。
(例)GPIO23を入力モードにして、1回読み出す
echo "in" > /sys/kernel/vgpio/gpio23/direction
cat /sys/kernel/vgpio/gpio23/value


4.もう使わない場合は、vgpio.koをrmmodする
sudo rmmod vgpio.ko
→/sys/kernel下のvgpioと、作っていたgpio23が消える。
※作成したGPIOポートは、unexport操作しなくても
rmmodすると自動的にunexportします。

使い方は以上です。

●注意点
※1.本物のGPIOは、入力モードに設定しているので
書き込みはできませんが、この仮想GPIOでは、
入力モードでも書き込みができてしまいます。
デバッグで、GPIOの値を変える場合は、別コンソールで
echoなどでvalueの値を書き換えてください。

※2.select/pollのシステムコールが、現在使えません。
すぐできそうですですが、私がやりたいことは
現状でできたので、とりあえずこのままにします。

----------------------------------------------------------

この仮想GPIOドライバは、linuxカーネルのリポジトリの
以下のファイルを参考にしました。

v4.18
/samples/kobject/kobject-example.c
/drivers/gpio/gpiolib-sysfs.c

・アクセス権について
本物のGPIOでは、otherのアクセス権が読出しのみになっています。
これはカーネルレベルで制限かけられていました。
/include/linux/kernel.h VERIFY_OCTAL_PERMISSIONSマクロ
/fs/sysfs/group.c create_files関数

ラズベリーパイなどでは、GPIO操作用のGPIOグループがあり、
piユーザはGPIOグループに属しており、
GPIOの各属性にGPIOグループで読み書きしているようです。

PCでデバッグするとき、GPIOグループ作って、各属性を
GPIOグループに変えて、GPIOグループへの権限を変えて・・・
ということをいちいちするのは面倒なので、
仮想GPIOドライバではちょっと細工して、otherでも読み書き
できるようにしました。

・select/poll対応について
現在はできません。なので、edgeになにか書き込んでも
特に影響ありません。(書き込み自体はできます)
カーネルの以下の機能を使えばできそうであることはわかっています。
sysfs_get_dirent()  edgeがnone以外が書き込まれたら、kernfs_nodeを取得する
sysfs_notify_dirent() valueが変化したら、kernfs_nodeにイベント発生を通知する
sysfs_put() edge、active_lowが変化したら、kernfs_nodeを解放する

2019/05/03

仮想GPIOを/sys/class以下に作ってみたものの

仮想GPIOドライバをとりあえず作ってみました。
設定値をメモリ上にとって、gpiochipへの読み書きを
メモリの値に置き換えるだけなので、とりあえず動くものは
すぐにできました。

gpiolib-sysfs.cを参考にして、/sys/class以下に
vgpioという名前のディレクトリを作って、
その配下にexport等を作るようにしました。
しかし、同時に/sys/devices/virtual/vgpioもできてしまう。

また、動作させてexportへ任意の番号を書き込むとgpioNができるが、
このエイリアスが/sys/devices/virtual/vgpio/gpioNになり、
direction、value、edge、active_low以外にpower、uevent等
よくわからないものがたくさんできていました。

ただ direction/value/edge/active_low だけ読み書きできれば
良いのですが・・他のはsysfsのclassAPIが作ってるものなので、
なにか意図しないことが起きるかもしれない・・

というわけで、別の方法を模索してみます。

linux kernelのリポジトリを見ると、/samples/kobject があります。
これは、/sys/kernel以下にアトリビュートというファイルのようなものを
作って読み書きするサンプルです。
(sysfsはファイルではなくアトリビュートと言う)
これが、ほぼやりたいことをしているので、これを参考にして作り直します。
こんなのがあったんだな。

2019/04/30

GPIO操作のPCテスト環境について考える

GPIO操作のテスト環境は、ほぼ実機でのテストしかできません。
(と思ってるけど、実はあるのだろうか・・)

例えば、GPIO23に出力するとき、コンソールで以下のようにします。
> echo 23 > /sys/class/gpio/export
> echo "out" > /sys/class/gpio/gpio23/direction
> echo 1 > /sys/class/gpio/gpio23/value

この操作を、実機ではなくPC上で行おうとすると、
最初の exportへ23を書き込む ところで、引数エラーになります。
> echo 23 > /sys/class/gpio/export
echo: 書き込みエラー: 無効な引数です

dmesgで見ると、以下のメッセージが出力されています。
> dmesg
    ~省略~
[  955.6530668] export_store: invalid GPIO 23

これを出してるのは、linux v4.18.0 のカーネルソースを見ると、
以下の場所で出力しているようです。
/drivers/gpio/gpiolib-sysfs.c
453 static ssize_t export_store(struct class *class,
454    struct class_attribute *attr,
455    const char *buf, size_t len)
456 {
    ~省略~
465     desc = gpio_to_desc(gpio);
466     /* reject invalid GPIOs */
467     if (!desc) {
468         pr_warn("%s: invalid GPIO %ld\n", __func__, gpio);
469         return -EINVAL;
470     }

エラーになってるのはgpio_to_desc関数で、引数gpioに対応する
ディスクリプタを返すが、このディスクリプタはカーネルソース内で
チップ情報によって作成されています。つまり、この動作を変えるには、
カーネルソースの再ビルドが必要となりそうです。

なので、GPIO操作のテストには実機が必要だが、現場では数人で
実機を共有しているので、気軽にテストできません。
ようやく実機使えてテストしたら、「不等号の向き間違えた」なんかの
つまらないバグが出ると、時間の無駄だし、気が滅入ってきます。
なので、PC上でコーディングミスレベルの不具合は潰しておきたい。

PC上での方法を考えると、デバッグ時は/sys/class/gpio/gpioN/value を
mkfifo で作ったパイプで代替する方法をしている人が、私の職場でいました。
しかし、これは、書き込み側は動作は変わらないが、読み出し側がパイプを
readしたときに書き込みがあるまで関数がブロックしてしまいます。
また、読み出しが遅い場合、書き込みを、例えば2回1を書き込んだ場合、
読み出すと11になるので、一文字ずつ処理するなど、実機の場合には
不要な処理を足さなくてはならないので面倒です。

カーネルソースのgpiolib-sysfs.cを見ていると、実ポートには出力せず、
メモリ上の値を読み書きするだけの、GPIOの動作を模擬できる
仮想GPIOドライバが作れそうです。

というわけで、このゴールデンウィーク中に仮想GPIOドライバを
作ってみることにしました。