コードをきれいにしましょう:Linuxで64ビットハードウェアレジスタを操作する

多くの場合、ドライバーを作成するプログラマーは、64ビット形式のデータを交換する際にいくつかの困難を経験します。 いくつかの状況を見てみましょう。



参加する代わりに



実際、64ビットレジスタの操作画面では、いくつかの問題が隠されています。



まず 、64ビットレジスタを使用する機器のメンテナンスは、64ビットコアと32ビットコアの両方で利用できる必要があります。



第二に 、すべての64ビットレジスタが1つのコマンドで記述できるわけではありません。これは、ハードウェアでの実装の微妙な違いによるものです。



第三に 、確立された標準とプロトコルに関連して、64ビットの数値を2つの32ビットの数値と比較する必要がある場合があります。



自転車の発明を回避し、コードをより簡潔に、より簡潔に、より美しくする方法を見てみましょう。



64ビットデータ交換



もちろん、多くの人がwritel()



readl()



など、機器のレジスタを読み書きするためのよく知られたコマンドにreadl()







これらは、従来のバイト、ダブルバイト、および4バイトのレジスタ操作をカバーしています。 一度に8バイトを読み書きする必要がある場合はどうしますか?



一部の64ビットアーキテクチャでは、 writeq()



およびreadq()



コマンドがreadq()



ます。



従来、32ビットプラットフォームと64ビットプラットフォームを採用するプログラマーは、コードで似たようなものを作成していました。

 static inline void writeq(u64 val, void __iomem *addr) { writel(val, addr); writel(val >> 32, addr + 4); }
      
      





そしてそれに応じて読書。

 static inline u64 readq(void __iomem *addr) { u32 low, high; low = readl(addr); high = readl(addr + 4); return low + ((u64)high << 32); }
      
      







そして、そのようなコピーが数百ではないとしても数十あると想像してください。 自転車の発明を避けるために、特別なファイルには/ linux / io-64-nonatomic-hi-lo.hが 含まれ、/ linux / io-64-nonatomic-lo-hi.hがカーネルに追加されました



これらのファイルの機能は何ですか:

  1. 特定の関数は、アトミックにではなく呼び出しを実行します。
  2. 上記に関連して、カーネルには2つのファイルがあります。ジュニア-シニアとその逆-シニア-ジュニアを記録するためです。
  3. どちらのファイルも、「最初に起きた人はスニーカーだ」という原則に従って、 writeq()



    readq()



    宣言します。 最初に、それらがすでに定義されているかどうかがチェックされますか? そうでない場合は、再定義します。 したがって、ヘッダーファイルが含まれる順序が重要です。
  4. 式8 = 4 + 4によって明らかに行われる処理。


したがって、4 + 4という形式の呼び出しのみを理解する機器がある場合は、 lo_hi_writeq()



およびlo_hi_readq()



またはhi_lo_writeq()



およびhi_lo_readq()



ます。 理想的なケースでは、 writeq()



およびreadq()



です。



64ビット数の比較



場合によっては、オプション8の64ビット数とオプション4 + 4の数を比較する必要があります。



額に直接ソリューション:

 u32 hi = Y, lo = Z; u64 value = X, tmp; tmp = (Y << 32) | X; return value == tmp;
      
      





さて、あなたは32ビットアーキテクチャ上にどれだけのコードがあるのか​​理解しています。



このケースは、次の2つの比較に分けることができます。

 return (value >> 32) == hi && (u32)value == lo;
      
      





簡単に思えますが、1つ問題があります。 カーネルには、特定のプラットフォームに直接依存するタイプ、つまりphys_addr_tresource_size_tdma_addr_tなどがあります。



このコードを書いたらどうなると思いますか:

 u32 hi = Y, lo = Z; resource_size_t value = X; return (value >> 32) == hi && (u32)value == lo;
      
      





もちろん、64ビットアーキテクチャでは、すべて問題ありません。 しかし、32ビット版では、コンパイラーはシフトvalue >> 32



について文句を言います。



コンパイラが満足し、ユーザーがlower_32_bits()



ないようにするために、次のマクロがカーネルに追加されました: lower_32_bits()



および

upper_32_bits()



、それぞれ下位および上位4バイト。



その結果、比較は次のようになります。

 return upper_32_bits(value) == hi && lower_32_bits(value) == lo;
      
      







これらの操作はアトミックではないことを忘れないでください!



All Articles