1 .. SPDX-License-Identifier: GPL-2.0+ 2 3 .. include:: ../disclaimer-zh_CN.rst 4 5 :Original: Documentation/core-api/packing.rst 6 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 57 1. 通常情况下(无特殊性),我们会这样做: 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 32 62 7 6 5 4 63 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 0 64 3 2 1 0 65 66 也就是说,CPU可使用的u64的MSByte(7)位于内存偏移量0处,而u64的LSByte(0)位于内存偏移量7处。 67 68 这对应于大多数人认为的“大端”,其中位i对应于数字2^i。这在代码注释中也称为“逻辑”符号。 69 70 71 2. 如果设置了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 39 76 7 6 5 4 77 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 7 78 3 2 1 0 79 80 也就是说,QUIRK_MSB_ON_THE_RIGHT不会影响字节定位,但会反转字节内的位偏移量。 81 82 83 3. 如果设置了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 56 88 4 5 6 7 89 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 24 90 0 1 2 3 91 92 因此,QUIRK_LITTLE_ENDIAN意味着在内存区域内,每个4字节的字的每个字节都被放置在与 93 该字的边界相比的镜像位置。 94 95 96 4. 如果设置了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 63 101 4 5 6 7 102 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 31 103 0 1 2 3 104 105 106 5. 如果只设置了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 0 111 3 2 1 0 112 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 32 113 7 6 5 4 114 115 在这种情况下,8字节内存区域解释如下:前4字节对应最不重要的4字节的字,后4字节对应 116 更重要的4字节的字。 117 118 6. 如果设置了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 7 123 3 2 1 0 124 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 39 125 7 6 5 4 126 127 128 7. 如果设置了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 24 133 0 1 2 3 134 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 56 135 4 5 6 7 136 137 138 8. 如果设置了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 31 144 0 1 2 3 145 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 63 146 4 5 6 7 147 148 149 我们总是认为我们的偏移量好像没有特殊性,然后在访问内存区域之前翻译它们。 150 151 预期用途 152 -------- 153 154 选择使用该API的驱动程序首先需要确定上述3种quirk组合(共8种)中的哪一种与硬件文档 155 中描述的相匹配。然后,他们应该封装packing()函数,创建一个新的xxx_packing(),使用 156 适当的QUIRK_* one-hot 位集合来调用它。 157 158 packing()函数返回一个int类型的错误码,以防止程序员使用不正确的API。这些错误预计不 159 会在运行时发生,因此xxx_packing()返回void并简单地接受这些错误是合理的。它可以选择 160 转储栈或打印错误描述。
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.