KernelNewbiesJP:

このページはWritingPortableDriversセクションの一部です。

エンディアンに関する問題

原文: EndianIssues

プロセッサは内部データをリトルエンディアンとビッグエンディアンのどちらかの方法で格納します。 リトルエンディアンのプロセッサは最右端のバイト(アドレス値が大きい方)が最上位となるように格納し、 ビッグエンディアンのプロセッサは最左端のバイト(アドレス値が小さい方)が最上位となるように格納します。

以下の表は、ふたつのエンディアンの異なるプロセッサ上で、10進数の684686が4バイトの整数として、 どのように格納されるかを表しています。 (684686 10進数 = a72be 16進数 = 00000000 00001010 01110010 10001110 2進数)

Address

ビッグエンディアン

リトルエンディアン

0

00000000

10001110

1

00001010

01110010

2

01110010

00001010

3

10001110

00000000

i386やIA-64といったIntelプロセッサはリトルエンディアンであり、SPARCプロセッサはビッグエンディアンです。 PowerPCプロセッサはリトルエンディアンでもビッグエンディアンでも動作させることができますが、Linuxでは ビッグエンディアンモードで動作するように定義されています。ARMプロセッサは使用されているARMチップにより どちらのエンディアンでも動作させることができますが、通常こちらもビッグエンディアンモードで動作します。

プロセッサによりエンディアンの種類は異なるので、外部からデータを受け取るときはその順序に注意する 必要があります。たとえば、USB仕様では、複数バイトで表現されるデータフィールドはリトルエンディアンと 決められています。したがって、USBコネクションから複数バイトのデータを読み込むUSBドライバは、そのデータを プロセッサネイティブなフォーマットに変換する必要があります。プロセッサがリトルエンディアンであるという 前提のもとに書かれているコードであれば、USBコネクションから来たデータのフォーマットを無視することもできる でしょうが、そのようなコードはPowerPCプロセッサやARMプロセッサでは動作しないでしょうし、異なるプラット フォーム上でドライバが問題を起こす主な原因となります。

幸いなことに、エンディアンの変換を簡単にできる便利なマクロがいくつかあります。 以下に示すマクロはすべてヘッダファイルasm/byteorder.hの中にあります。

プロセッサネイティブなフォーマットからリトルエンディアンに変換するには、次の関数を使用することができます。

   1 __le64 cpu_to_le64(u64);
   2 __le32 cpu_to_le32(u32);
   3 __le16 cpu_to_le16(u16);

リトルエンディアンからプロセッサネイティブなフォーマットに変換するには、次の関数を使用することができます。

   1 u64 le64_to_cpu(__le64);
   2 u32 le32_to_cpu(__le32);
   3 u16 le16_to_cpu(__le16);

ビッグエンディアンには、次の関数を利用できます。

   1 __be64 cpu_to_be64(u64);
   2 __be32 cpu_to_be32(u32);
   3 __be16 cpu_to_be16(u16);
   4 u64 be64_to_cpu(__be64);
   5 u32 be32_to_cpu(__be32);
   6 u16 be16_to_cpu(__be16);

ポインタが指す先の値を変換する場合は、次の関数を使用してください。

   1 __le64 cpu_to_le64p(u64 *);
   2 __le32 cpu_to_le32p(u32 *);
   3 __le16 cpu_to_le16p(u16 *);
   4 u64 le64_to_cpup(__le64 *);
   5 u32 le32_to_cpup(__le32 *);
   6 u16 le16_to_cpup(__le16 *);
   7 __be64 cpu_to_be64p(u64 *);
   8 __be32 cpu_to_be32p(u32 *);
   9 __be16 cpu_to_be16p(u16 *);
  10 u64 be64_to_cpup(__be64 *);
  11 u32 be32_to_cpup(__be32 *);
  12 u16 be16_to_cpup(__be16 *);

変数の値を変換し、変換後の値を同じ変数(元の場所)に格納したいときは、次の関数を使用してください。

   1 void cpu_to_le64s(u64 *);
   2 void cpu_to_le32s(u32 *);
   3 void cpu_to_le16s(u16 *);
   4 void le64_to_cpus(u64 *);
   5 void le32_to_cpus(u32 *);
   6 void le16_to_cpus(u16 *);
   7 void cpu_to_be64s(u64 *);
   8 void cpu_to_be32s(u32 *);
   9 void cpu_to_be16s(u16 *);
  10 void be64_to_cpus(u64 *);
  11 void be32_to_cpus(u32 *);
  12 void be16_to_cpus(u16 *);

前述したように、USBプロトコルはリトルエンディアンフォーマットを使用しています。 以下に示すdrivers/usb/serial/visor.cのコードの断片は、USBコネクションから構造体データを読み込み、 適切なCPUフォーマットに変換する方法を示しています。

   1 struct visor_connection_info *connection_info;
   2 
   3 /* send a get connection info request */
   4 usb_control_msg(serial->dev,
   5                 usb_rcvctrlpipe(serial->dev, 0),
   6                 VISOR_GET_CONNECTION_INFORMATION,
   7                 0xc2, 0x0000, 0x0000,
   8                 transfer_buffer, 0x12, 300);
   9 
  10 connection_info = (struct visor_connection_info *)transfer_buffer;
  11 
  12 num_ports = le16_to_cpu(connection_info->num_ports);

KernelNewbiesJP: EndianIssues (last edited 2017-12-30 08:13:16 by localhost)