162306a36Sopenharmony_ci================================================ 262306a36Sopenharmony_ciGeneric bitfield packing and unpacking functions 362306a36Sopenharmony_ci================================================ 462306a36Sopenharmony_ci 562306a36Sopenharmony_ciProblem statement 662306a36Sopenharmony_ci----------------- 762306a36Sopenharmony_ci 862306a36Sopenharmony_ciWhen working with hardware, one has to choose between several approaches of 962306a36Sopenharmony_ciinterfacing with it. 1062306a36Sopenharmony_ciOne can memory-map a pointer to a carefully crafted struct over the hardware 1162306a36Sopenharmony_cidevice's memory region, and access its fields as struct members (potentially 1262306a36Sopenharmony_cideclared as bitfields). But writing code this way would make it less portable, 1362306a36Sopenharmony_cidue to potential endianness mismatches between the CPU and the hardware device. 1462306a36Sopenharmony_ciAdditionally, one has to pay close attention when translating register 1562306a36Sopenharmony_cidefinitions from the hardware documentation into bit field indices for the 1662306a36Sopenharmony_cistructs. Also, some hardware (typically networking equipment) tends to group 1762306a36Sopenharmony_ciits register fields in ways that violate any reasonable word boundaries 1862306a36Sopenharmony_ci(sometimes even 64 bit ones). This creates the inconvenience of having to 1962306a36Sopenharmony_cidefine "high" and "low" portions of register fields within the struct. 2062306a36Sopenharmony_ciA more robust alternative to struct field definitions would be to extract the 2162306a36Sopenharmony_cirequired fields by shifting the appropriate number of bits. But this would 2262306a36Sopenharmony_cistill not protect from endianness mismatches, except if all memory accesses 2362306a36Sopenharmony_ciwere performed byte-by-byte. Also the code can easily get cluttered, and the 2462306a36Sopenharmony_cihigh-level idea might get lost among the many bit shifts required. 2562306a36Sopenharmony_ciMany drivers take the bit-shifting approach and then attempt to reduce the 2662306a36Sopenharmony_ciclutter with tailored macros, but more often than not these macros take 2762306a36Sopenharmony_cishortcuts that still prevent the code from being truly portable. 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ciThe solution 3062306a36Sopenharmony_ci------------ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciThis API deals with 2 basic operations: 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci - Packing a CPU-usable number into a memory buffer (with hardware 3562306a36Sopenharmony_ci constraints/quirks) 3662306a36Sopenharmony_ci - Unpacking a memory buffer (which has hardware constraints/quirks) 3762306a36Sopenharmony_ci into a CPU-usable number. 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ciThe API offers an abstraction over said hardware constraints and quirks, 4062306a36Sopenharmony_ciover CPU endianness and therefore between possible mismatches between 4162306a36Sopenharmony_cithe two. 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciThe basic unit of these API functions is the u64. From the CPU's 4462306a36Sopenharmony_ciperspective, bit 63 always means bit offset 7 of byte 7, albeit only 4562306a36Sopenharmony_cilogically. The question is: where do we lay this bit out in memory? 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ciThe following examples cover the memory layout of a packed u64 field. 4862306a36Sopenharmony_ciThe byte offsets in the packed buffer are always implicitly 0, 1, ... 7. 4962306a36Sopenharmony_ciWhat the examples show is where the logical bytes and bits sit. 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci1. Normally (no quirks), we would do it like this: 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci:: 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci 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 5662306a36Sopenharmony_ci 7 6 5 4 5762306a36Sopenharmony_ci 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 5862306a36Sopenharmony_ci 3 2 1 0 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ciThat is, the MSByte (7) of the CPU-usable u64 sits at memory offset 0, and the 6162306a36Sopenharmony_ciLSByte (0) of the u64 sits at memory offset 7. 6262306a36Sopenharmony_ciThis corresponds to what most folks would regard to as "big endian", where 6362306a36Sopenharmony_cibit i corresponds to the number 2^i. This is also referred to in the code 6462306a36Sopenharmony_cicomments as "logical" notation. 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci2. If QUIRK_MSB_ON_THE_RIGHT is set, we do it like this: 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci:: 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci 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 7262306a36Sopenharmony_ci 7 6 5 4 7362306a36Sopenharmony_ci 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 7462306a36Sopenharmony_ci 3 2 1 0 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ciThat is, QUIRK_MSB_ON_THE_RIGHT does not affect byte positioning, but 7762306a36Sopenharmony_ciinverts bit offsets inside a byte. 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci3. If QUIRK_LITTLE_ENDIAN is set, we do it like this: 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci:: 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci 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 8562306a36Sopenharmony_ci 4 5 6 7 8662306a36Sopenharmony_ci 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 8762306a36Sopenharmony_ci 0 1 2 3 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ciTherefore, QUIRK_LITTLE_ENDIAN means that inside the memory region, every 9062306a36Sopenharmony_cibyte from each 4-byte word is placed at its mirrored position compared to 9162306a36Sopenharmony_cithe boundary of that word. 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci4. If QUIRK_MSB_ON_THE_RIGHT and QUIRK_LITTLE_ENDIAN are both set, we do it 9462306a36Sopenharmony_ci like this: 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci:: 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci 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 9962306a36Sopenharmony_ci 4 5 6 7 10062306a36Sopenharmony_ci 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 10162306a36Sopenharmony_ci 0 1 2 3 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci5. If just QUIRK_LSW32_IS_FIRST is set, we do it like this: 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci:: 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci 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 10962306a36Sopenharmony_ci 3 2 1 0 11062306a36Sopenharmony_ci 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 11162306a36Sopenharmony_ci 7 6 5 4 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ciIn this case the 8 byte memory region is interpreted as follows: first 11462306a36Sopenharmony_ci4 bytes correspond to the least significant 4-byte word, next 4 bytes to 11562306a36Sopenharmony_cithe more significant 4-byte word. 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci6. If QUIRK_LSW32_IS_FIRST and QUIRK_MSB_ON_THE_RIGHT are set, we do it like 11962306a36Sopenharmony_ci this: 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci:: 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci 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 12462306a36Sopenharmony_ci 3 2 1 0 12562306a36Sopenharmony_ci 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 12662306a36Sopenharmony_ci 7 6 5 4 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci7. If QUIRK_LSW32_IS_FIRST and QUIRK_LITTLE_ENDIAN are set, it looks like 13062306a36Sopenharmony_ci this: 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci:: 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci 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 13562306a36Sopenharmony_ci 0 1 2 3 13662306a36Sopenharmony_ci 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 13762306a36Sopenharmony_ci 4 5 6 7 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci8. If QUIRK_LSW32_IS_FIRST, QUIRK_LITTLE_ENDIAN and QUIRK_MSB_ON_THE_RIGHT 14162306a36Sopenharmony_ci are set, it looks like this: 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci:: 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci 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 14662306a36Sopenharmony_ci 0 1 2 3 14762306a36Sopenharmony_ci 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 14862306a36Sopenharmony_ci 4 5 6 7 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ciWe always think of our offsets as if there were no quirk, and we translate 15262306a36Sopenharmony_cithem afterwards, before accessing the memory region. 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciIntended use 15562306a36Sopenharmony_ci------------ 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ciDrivers that opt to use this API first need to identify which of the above 3 15862306a36Sopenharmony_ciquirk combinations (for a total of 8) match what the hardware documentation 15962306a36Sopenharmony_cidescribes. Then they should wrap the packing() function, creating a new 16062306a36Sopenharmony_cixxx_packing() that calls it using the proper QUIRK_* one-hot bits set. 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ciThe packing() function returns an int-encoded error code, which protects the 16362306a36Sopenharmony_ciprogrammer against incorrect API use. The errors are not expected to occur 16462306a36Sopenharmony_ciduring runtime, therefore it is reasonable for xxx_packing() to return void 16562306a36Sopenharmony_ciand simply swallow those errors. Optionally it can dump stack or print the 16662306a36Sopenharmony_cierror description. 167