カーネルバージョン4.6-r1から、gpioカーネルサブシステムと対話するための新しいインターフェイスが利用可能になりました。 現在、gpioを操作し、それらから割り込みを取得する公式の方法が3つあります。 このサブシステムのニーズを掘り下げる意味はありません。小さな部分は過酷な日常生活であり、別の部分は楽しい趣味であり、すべての点で相互作用の新しい機会がカーネルで提供されました。
このノートは本質的に人気があります。なぜなら、イノベーションに伴う主な利点、つまりカーネルのコンテキストでgpioを使用する作業の簡素化については触れないからです。
新しいuapi gpioインターフェイス
https://github.com/torvalds/linux/blob/master/include/uapi/linux/gpio.h
まず、 gpiochipは実際にはデバイスであり、 devfsではgpiochipNとして見ることができます。ここで、Nは初期化順序で割り当てられたチップ番号です。 次に、すべての構成はioctlを介して行われます 。 そして第三に、驚くべきことに、読み取りと書き込みは、特別なstruct gpiohandle_data構造の助けを借りて、読み取り/書き込み呼び出しによって行われます。
gpio-mockup
カーネルバージョンv4.9-rc1(実際にはv4.12-rc1よりも古いバージョンでのみ使用可能)以降、 debugfsによる状態管理をサポートする仮想gpioデバイスが利用可能になりました。
たとえば、ユーザー空間でのsysfsとuapiの違いを考えてみましょう 。
gpio-mockupの初期化
パラメータ:
- gpio_mockup_ranges-gpiochipsを初期化する数値のペア。 "base、end"の形式で、baseは開始番号、endは範囲の終了です。
- gpio_mockup_named_lines-ブール値パラメーター(設定されている場合)は、gpio-mockup-A..ZNの形式で各行にラベルを割り当てます。ここで、Nは銀行の行のシリアル番号です。
# modprobe gpio-mockup gpio_mockup_ranges=0,8,8,16
このチームでは、範囲が[0-8]、[8,16)の8行のgpiochipを2つ作成しました。 gpio_mockup_named_linesについては後ほど説明します。
sysfsとuapiの比較
新しいドライバーを使用して、ユーザーの観点から2つのシステムの違いを検討します。
Gpiochipの情報
sysfs
# cat /sys/class/gpio/gpiochip*/base 0 8 # cat /sys/class/gpio/gpiochip*/ngpio 8 8 # cat /sys/class/gpio/gpiochip*/label gpio-mockup-A gpio-mockup-B
ウアピ
struct gpiochip_info chip_info; ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &chip_info);
# ./lsgpio | grep GPIO\ chip GPIO chip: gpiochip1, "gpio-mockup-B", 8 GPIO lines GPIO chip: gpiochip0, "gpio-mockup-A", 8 GPIO lines
行を入力として設定し、値を読み取る
sysfs
# echo in > /sys/class/gpio/gpio0/direction # cat /sys/class/gpio/gpio0/value
ウアピ
struct gpiohandle_request req; req.lineoffsets[0] = 0; req.flags = GPIOHANDLE_REQUEST_INPUT; req.lines = 1; struct gpiohandle_data data; ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req); ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
行を出力として設定する
sysfs
# echo high > /sys/class/gpio/gpio0/direction
ウアピ
struct gpiohandle_request req; req.lineoffsets[0] = 0; req.flags = GPIOHANDLE_REQUEST_OUTPUT; req.default_values[0] = 1; req.lines = 1; ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
エッジ処理
sysfs
# echo both > /sys/class/gpio/gpio0/edge
ウアピ
struct gpioevent_request ereq; ereq.lineoffset = 0; ereq.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES; ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &ereq);
イベントのポーリング
sysfsのカーネルドキュメントでは、 EPOLLPRIおよびEPOLLERR (またはselectのexceptfds )を使用することが示されています。これは、原則として、 gpioサブシステムではなくsysfs_notifyの呼び出しで一般的です。
uapiには、EPOLLINで十分です。
struct epoll_event event; event.data.fd = ereq.fd; event.events = EPOLLIN; epoll_ctl(epollfd, EPOLL_CTL_ADD, ereq.fd, &event);
タイムスタンプ付きのイベントを読み取り、タイプGPIOEVENT_EVENT_RISING_EDGEまたはGPIOEVENT_EVENT_FALLING_EDGEを入力します。
struct gpioevent_data event; read(pin->fd, &event, sizeof(event));
EPOLLET for uapiは 、epollのドキュメントに従って機能します。
ラベル
sysfsについてのちょっとしたコメント
誰もが慣れている連絡先名gpioNは一般に正規ではありませんが、連絡先に名前が付けられていない場合、たとえばデバイスツリーで使用されます。
// drivers/gpio/gpiolib-sysfs.c // int gpiod_export(struct gpio_desc *desc, bool direction_may_change) offset = gpio_chip_hwgpio(desc); if (chip->names && chip->names[offset]) ioname = chip->names[offset]; dev = device_create_with_groups(&gpio_class, &gdev->dev, MKDEV(0, 0), data, gpio_groups, ioname ? ioname : "gpio%u", desc_to_gpio(desc));
gpio_mockup_named_linesオプションを使用してgpio - mockupを試してみましょう。
# modprobe gpio-mockup gpio_mockup_ranges=0,8,8,16 gpio_mockup_named_lines=1 # echo 0 > /sys/class/gpio/export # ls /sys/class/gpio/gpio-mockup-A-0 active_low device direction edge power subsystem uevent value
ご覧のとおり、連絡先名はgpio_chip_label - gpio_offsetになりましたが、これはgpio - mockup ドライバーにのみ当てはまります。
// drivers/gpio/gpio-mockup.c // static int gpio_mockup_name_lines(struct device *dev, struct gpio_mockup_chip *chip) for (i = 0; i < gc->ngpio; i++) names[i] = devm_kasprintf(dev, GFP_KERNEL, "%s-%d", gc->label, i);
uapiを使用せずに連絡先の名前が存在するかどうかを事前に「推測」することはできません。 また 、名前が事前にわからない場合は、エクスポートされた「名前付き」行を見つけることは困難です(これがわかっている場合は、明確な識別のために、既知の名前とオフセットが必要です) gpiochip )。
ウアピ
uapiインターフェイスを使用すると、初期化せずに行名を表示できます。
#./lsgpio | grep gpio-mockup-A GPIO chip: gpiochip0, "gpio-mockup-A", 8 GPIO lines line 0: "gpio-mockup-A-0" unused [output] ... line 7: "gpio-mockup-A-7" unused [output]
対応するデバイスツリーファイル(例はカーネルのドキュメントから取得):
gpio-line-names = "MMC-CD", "MMC-WP", "VDD eth", "RST eth", "LED R", "LED G", "LED B", "Col A", "Col B", "Col C", "Col D", "Row A", "Row B", "Row C", "Row D", "NMI button", "poweroff", "reset";
連絡先ごとにgpioline_info構造体に名前が表示されますが、残念ながら、SBCが広く普及している場合でも、連絡先に名前を付ける人はほとんどいません。
sysfsでは利用できないUapi機能
次に、古いインターフェイスでは利用できない利点をリストします。
私が考える主な利点は、割り込みハンドラーの上半分でイベントに割り当てられるタイムスタンプです。 これは、イベント間の時間を正確に測定する必要があるアプリケーションに不可欠です。
le->timestamp = ktime_get_real_ns();
デバイスドライバーが許可する場合、ラインはさらにオープンコレクター( GPIOLINE_FLAG_OPEN_DRAIN )またはオープンエミッター( GPIOLINE_FLAG_OPEN_SOURCE )として構成できます。このイノベーションはsysfsに簡単に転送できますが、Linus Werleyが反対しているため、これは起こりません。
また、新しいapiを使用すると、初期化中にgpiohandle_requestフィールドconsumer_labelで各連絡先にカスタムラベルを割り当てることができます。
結論として、連絡先の州のグループをすぐに「読み取り」および「書き込み」することができます。
結論の比較
主観的に、 uapiはsysfsよりも扱いにくいように見えますが、標準のGNUユーティリティcatおよびechoおよびCコードを介して制御を比較したことを忘れないでください。各インターフェイスのCコードを比較すると、複雑さとボリュームがほぼ同じであることがわかります。
重要な点は、 sysfsを使用する場合、ユーザーが反対を要求するか、リブートするまで、行は初期化されたままになるということです。 uapiは、ファイル記述子を閉じた直後に行を解放します。
Uapiの利点
- syscallを節約します(gpio / valueを読み取った後、lseekの必要性を忘れないでください)。
- 入力または出力の配列を初期化します。
- 入力または出力の配列の読み取りまたは書き込み。
- オープンドレインとオープンソース
- カスタムタグ
- イベントで送信された「リアルタイムナノ秒タイムスタンプ」
批評UAPI
公式または非公式の批判はありません、または私はそれを見つけませんでした。 したがって、私たち自身の考えをいくつか理解しましょう。
- プッシュプル、デバウンス、プルアップ、プルダウンをバイパスした理由は不明です。
- struct gpioevent_dataで、現在の行の値に値パラメーターを追加すると便利です
sysfs gpioの批判
sysfsインターフェースに対する公式の批判で終わりましょう。 Linus Vaerli(カーネル内のgpioおよびpinctrlサブシステムに付属)のパッチに関するコメントで、次の論文が提案されました。
- 一度に複数の回線をオフにすることは不可能です
- sysfsが機能するためには、カーネル構成で対応するキーを有効にする必要があります
- gpioアプリケーションがクラッシュした場合、回線は「初期化された」状態のままになります
- 必要な行を見つけるのが難しい
- 「sysfsはひどく壊れています」©
一般的に、そして正直に言うと、sysfsはユーザー空間でgpioに割り当てられたタスクにとって非常に正常だと思います。 電気工学の基礎に詳しくない人でもechoを使用してライトを点灯できると、ロマンチックなものが生まれます。 新しいインターフェイスでは、対話に追加のユーティリティが必要になるため、このような直接接続は感じられません。
しかし、GPIOはしばしばグループとして一緒に使用されます。 簡単な例(そして唯一)として、I2Cバスとして使用されるGPIOのペアを考えます。 1行はデータを処理し、もう1行はクロックを処理します。
最初の論文については何も言えません。そのようなニーズに出会ったことはありません。最終的に、連絡先をすぐにデバイスツリーの入力または出力として初期化できます 。 この機能は、 ユーザー空間でビットバンギングが必要な人にとって有用であることを知っていますが、ここには1つありますが、 純粋な Linuxでは、 ビットバンギングは非常に低周波のものにのみ可能であり、少なくともPREEMPT_RTパッチが必要です 。
2番目の論文も奇妙で、sysfsを無効にする必要があるほどのスペース節約は想像できません。
3番目はさらに奇妙です。アプリケーションを「クラッシュ」させる必要はないのでしょうか。
4番目については、基本的に何も本質的に変わっていないと言える。 プラットフォームドライバーまたはgpiochipのデバイスツリーラベルで指定されているものがtrueの場合、「検索」は単純であり、「hell-like」と呼ばれる場合、インターフェイスはここでは役に立ちません。
一般的に、わかりやすい答えは見つかりませんでした。 私は新しいインターフェースに反対しているわけではありませんが、それでも私はそうですが、古いインターフェースをこのように注意深く掘り下げることは個人的に理解できず、不快です。
公益事業
uapiの場合、sysfsほど多くはありません。
Linuxカーネルgpioツール
https://github.com/torvalds/linux/tree/master/tools/gpio
- gpio-event-mon- gpio回線のイベントを追跡するためのユーティリティ
- gpio-hammer-固定周波数でn回ラインのオン/オフを切り替えます
- lsgpio- gpiochipとラインのリストの例
libgpiod
https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/
ドライバーgpio - mockupおよびirq-simの同志Bartosz'aGołaszewskiの作者から。
作成者によってライブラリとして位置付けられ、新しいuapiインターフェイスを介してgpioでの作業を容易にするために、有用なユーティリティのセットも含まれています。
- gpiodetect-名前、ラベル、行数を含むgpiochipリスト
- gpioinfo-名前、オフセット、ラベル、回線ステータスを含むgpiochipリスト
- gpioget-回線の状態を読み取ります
- gpioset-回線状態を設定し、必要に応じて、指定された時間、信号、またはユーザー入力が期限切れになるまで回線を使用中のままにします
- gpiofind-名前で行を検索し、 gpiochipNデバイスと行オフセットを表示します
- gpiomonはgpio-event-monと同じです
素材
- GPIOインターフェイス(レガシー)
- デバイスのGPIO情報を指定する
- カーネルのデバイスモデルの新しい外観
- カーネルのデバイスモデルであるLinus Wのコメント
- シミュレートされた割り込み
- カーネルv4.6のGPIO一括変更