brintos

brintos / linux-shallow public Read only

0
0
Text · 7.1 KiB · c0aab3a Raw
161 lines · plain
1.. SPDX-License-Identifier: GPL-2.0+2 3.. include:: ../disclaimer-zh_CN.rst4 5:Original: Documentation/core-api/packing.rst6 7:翻译:8 9 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn>10 11:校译:12 13 司延腾 Yanteng Si <siyanteng@loongson.cn>14 吴想成 Wu Xiangcheng <bobwxc@email.cn>15 时奎亮 Alex Shi <alexs@kernel.org>16 17========================18通用的位域打包和解包函数19========================20 21问题陈述22--------23 24使用硬件时,必须在几种与其交互的方法之间进行选择。25 26可以将指针映射到在硬件设备的内存区上精心设计的结构体,并将其字段作为结构成员(可27能声明为位域)访问。但是由于CPU和硬件设备之间潜在的字节顺序不匹配,以这种方式编写28代码会降低其可移植性。29 30此外,必须密切注意将硬件文档中的寄存器定义转换为结构的位域索引。此外,一些硬件31(通常是网络设备)倾向于以违反任何合理字边界(有时甚至是64位)的方式对其寄存器字32段进行分组。这就造成了不得不在结构中定义寄存器字段的“高”和“低”部分的不便。33 34结构域定义的更可靠的替代方法是通过移动适当数量的位来提取所需的字段。但这仍然不能35防止字节顺序不匹配,除非所有内存访问都是逐字节执行的。此外,代码很容易变得杂乱无36章,同时可能会在所需的许多位移操作中丢失一些高层次的想法。37 38许多驱动程序采用了位移的方法,然后试图用定制的宏来减少杂乱无章的东西,但更多的时39候,这些宏所采用的捷径依旧妨碍了代码真正的可移植性。40 41解决方案42--------43 44该API涉及2个基本操作:45 46  - 将一个CPU可使用的数字打包到内存缓冲区中(具有硬件约束/特殊性)。47  - 将内存缓冲区(具有硬件约束/特殊性)解压缩为一个CPU可使用的数字。48 49该API提供了对所述硬件约束和特殊性以及CPU字节序的抽象,因此这两者之间可能不匹配。50 51这些API函数的基本单元是u64。从CPU的角度来看,位63总是意味着字节7的位偏移量7,尽管52只是逻辑上的。问题是:我们将这个比特放在内存的什么位置?53 54以下示例介绍了打包u64字段的内存布局。打包缓冲区中的字节偏移量始终默认为0,1...7。55示例显示的是逻辑字节和位所在的位置。56 571. 通常情况下(无特殊性),我们会这样做:58 59::60 61  63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 3262  7                       6                       5                        463  31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  064  3                       2                       1                        065 66也就是说,CPU可使用的u64的MSByte(7)位于内存偏移量0处,而u64的LSByte(0)位于内存偏移量7处。67 68这对应于大多数人认为的“大端”,其中位i对应于数字2^i。这在代码注释中也称为“逻辑”符号。69 70 712. 如果设置了QUIRK_MSB_ON_THE_RIGHT,我们按如下方式操作:72 73::74 75  56 57 58 59 60 61 62 63 48 49 50 51 52 53 54 55 40 41 42 43 44 45 46 47 32 33 34 35 36 37 38 3976  7                       6                        5                       477  24 25 26 27 28 29 30 31 16 17 18 19 20 21 22 23  8  9 10 11 12 13 14 15  0  1  2  3  4  5  6  778  3                       2                        1                       079 80也就是说,QUIRK_MSB_ON_THE_RIGHT不会影响字节定位,但会反转字节内的位偏移量。81 82 833. 如果设置了QUIRK_LITTLE_ENDIAN,我们按如下方式操作:84 85::86 87  39 38 37 36 35 34 33 32 47 46 45 44 43 42 41 40 55 54 53 52 51 50 49 48 63 62 61 60 59 58 57 5688  4                       5                       6                       789  7  6  5  4  3  2  1  0  15 14 13 12 11 10  9  8 23 22 21 20 19 18 17 16 31 30 29 28 27 26 25 2490  0                       1                       2                       391 92因此,QUIRK_LITTLE_ENDIAN意味着在内存区域内,每个4字节的字的每个字节都被放置在与93该字的边界相比的镜像位置。94 95 964. 如果设置了QUIRK_MSB_ON_THE_RIGHT和QUIRK_LITTLE_ENDIAN,我们这样做:97 98::99 100  32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63101  4                       5                       6                       7102  0  1  2  3  4  5  6  7  8   9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31103  0                       1                       2                       3104 105 1065. 如果只设置了QUIRK_LSW32_IS_FIRST,我们这样做:107 108::109 110  31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0111  3                       2                       1                        0112  63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32113  7                       6                       5                        4114 115在这种情况下,8字节内存区域解释如下:前4字节对应最不重要的4字节的字,后4字节对应116更重要的4字节的字。117 1186. 如果设置了QUIRK_LSW32_IS_FIRST和QUIRK_MSB_ON_THE_RIGHT,我们这样做:119 120::121 122  24 25 26 27 28 29 30 31 16 17 18 19 20 21 22 23  8  9 10 11 12 13 14 15  0  1  2  3  4  5  6  7123  3                       2                        1                       0124  56 57 58 59 60 61 62 63 48 49 50 51 52 53 54 55 40 41 42 43 44 45 46 47 32 33 34 35 36 37 38 39125  7                       6                        5                       4126 127 1287. 如果设置了QUIRK_LSW32_IS_FIRST和QUIRK_LITTLE_ENDIAN,则如下所示:129 130::131 132  7  6  5  4  3  2  1  0  15 14 13 12 11 10  9  8 23 22 21 20 19 18 17 16 31 30 29 28 27 26 25 24133  0                       1                       2                       3134  39 38 37 36 35 34 33 32 47 46 45 44 43 42 41 40 55 54 53 52 51 50 49 48 63 62 61 60 59 58 57 56135  4                       5                       6                       7136 137 1388. 如果设置了QUIRK_LSW32_IS_FIRST,QUIRK_LITTLE_ENDIAN和QUIRK_MSB_ON_THE_RIGHT,139   则如下所示:140 141::142 143  0  1  2  3  4  5  6  7  8   9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31144  0                       1                       2                       3145  32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63146  4                       5                       6                       7147 148 149我们总是认为我们的偏移量好像没有特殊性,然后在访问内存区域之前翻译它们。150 151预期用途152--------153 154选择使用该API的驱动程序首先需要确定上述3种quirk组合(共8种)中的哪一种与硬件文档155中描述的相匹配。然后,他们应该封装packing()函数,创建一个新的xxx_packing(),使用156适当的QUIRK_* one-hot 位集合来调用它。157 158packing()函数返回一个int类型的错误码,以防止程序员使用不正确的API。这些错误预计不159会在运行时发生,因此xxx_packing()返回void并简单地接受这些错误是合理的。它可以选择160转储栈或打印错误描述。161