''このページはWritingPortableDriversセクションの一部です。''<<BR>>
== エンディアンに関する問題 ==
原文: [[http://kernelnewbies.org/EndianIssues|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の中にあります。
プロセッサネイティブなフォーマットからリトルエンディアンに変換するには、次の関数を使用することができます。
{{{
#!cplusplus
__le64 cpu_to_le64(u64);
__le32 cpu_to_le32(u32);
__le16 cpu_to_le16(u16);
}}}
リトルエンディアンからプロセッサネイティブなフォーマットに変換するには、次の関数を使用することができます。
{{{
#!cplusplus
u64 le64_to_cpu(__le64);
u32 le32_to_cpu(__le32);
u16 le16_to_cpu(__le16);
}}}
ビッグエンディアンには、次の関数を利用できます。
{{{
#!cplusplus
__be64 cpu_to_be64(u64);
__be32 cpu_to_be32(u32);
__be16 cpu_to_be16(u16);
u64 be64_to_cpu(__be64);
u32 be32_to_cpu(__be32);
u16 be16_to_cpu(__be16);
}}}
ポインタが指す先の値を変換する場合は、次の関数を使用してください。
{{{
#!cplusplus
__le64 cpu_to_le64p(u64 *);
__le32 cpu_to_le32p(u32 *);
__le16 cpu_to_le16p(u16 *);
u64 le64_to_cpup(__le64 *);
u32 le32_to_cpup(__le32 *);
u16 le16_to_cpup(__le16 *);
__be64 cpu_to_be64p(u64 *);
__be32 cpu_to_be32p(u32 *);
__be16 cpu_to_be16p(u16 *);
u64 be64_to_cpup(__be64 *);
u32 be32_to_cpup(__be32 *);
u16 be16_to_cpup(__be16 *);
}}}
変数の値を変換し、変換後の値を同じ変数(元の場所)に格納したいときは、次の関数を使用してください。
{{{
#!cplusplus
void cpu_to_le64s(u64 *);
void cpu_to_le32s(u32 *);
void cpu_to_le16s(u16 *);
void le64_to_cpus(u64 *);
void le32_to_cpus(u32 *);
void le16_to_cpus(u16 *);
void cpu_to_be64s(u64 *);
void cpu_to_be32s(u32 *);
void cpu_to_be16s(u16 *);
void be64_to_cpus(u64 *);
void be32_to_cpus(u32 *);
void be16_to_cpus(u16 *);
}}}
前述したように、USBプロトコルはリトルエンディアンフォーマットを使用しています。
以下に示す{{{drivers/usb/serial/visor.c}}}のコードの断片は、USBコネクションから構造体データを読み込み、
適切なCPUフォーマットに変換する方法を示しています。
{{{
#!cplusplus
struct visor_connection_info *connection_info;
/* send a get connection info request */
usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
VISOR_GET_CONNECTION_INFORMATION,
0xc2, 0x0000, 0x0000,
transfer_buffer, 0x12, 300);
connection_info = (struct visor_connection_info *)transfer_buffer;
num_ports = le16_to_cpu(connection_info->num_ports);
}}}