Back to home page

OSCL-LXR

 
 

    


0001 ================================================
0002 Generic bitfield packing and unpacking functions
0003 ================================================
0004 
0005 Problem statement
0006 -----------------
0007 
0008 When working with hardware, one has to choose between several approaches of
0009 interfacing with it.
0010 One can memory-map a pointer to a carefully crafted struct over the hardware
0011 device's memory region, and access its fields as struct members (potentially
0012 declared as bitfields). But writing code this way would make it less portable,
0013 due to potential endianness mismatches between the CPU and the hardware device.
0014 Additionally, one has to pay close attention when translating register
0015 definitions from the hardware documentation into bit field indices for the
0016 structs. Also, some hardware (typically networking equipment) tends to group
0017 its register fields in ways that violate any reasonable word boundaries
0018 (sometimes even 64 bit ones). This creates the inconvenience of having to
0019 define "high" and "low" portions of register fields within the struct.
0020 A more robust alternative to struct field definitions would be to extract the
0021 required fields by shifting the appropriate number of bits. But this would
0022 still not protect from endianness mismatches, except if all memory accesses
0023 were performed byte-by-byte. Also the code can easily get cluttered, and the
0024 high-level idea might get lost among the many bit shifts required.
0025 Many drivers take the bit-shifting approach and then attempt to reduce the
0026 clutter with tailored macros, but more often than not these macros take
0027 shortcuts that still prevent the code from being truly portable.
0028 
0029 The solution
0030 ------------
0031 
0032 This API deals with 2 basic operations:
0033 
0034   - Packing a CPU-usable number into a memory buffer (with hardware
0035     constraints/quirks)
0036   - Unpacking a memory buffer (which has hardware constraints/quirks)
0037     into a CPU-usable number.
0038 
0039 The API offers an abstraction over said hardware constraints and quirks,
0040 over CPU endianness and therefore between possible mismatches between
0041 the two.
0042 
0043 The basic unit of these API functions is the u64. From the CPU's
0044 perspective, bit 63 always means bit offset 7 of byte 7, albeit only
0045 logically. The question is: where do we lay this bit out in memory?
0046 
0047 The following examples cover the memory layout of a packed u64 field.
0048 The byte offsets in the packed buffer are always implicitly 0, 1, ... 7.
0049 What the examples show is where the logical bytes and bits sit.
0050 
0051 1. Normally (no quirks), we would do it like this:
0052 
0053 ::
0054 
0055   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
0056   7                       6                       5                        4
0057   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
0058   3                       2                       1                        0
0059 
0060 That is, the MSByte (7) of the CPU-usable u64 sits at memory offset 0, and the
0061 LSByte (0) of the u64 sits at memory offset 7.
0062 This corresponds to what most folks would regard to as "big endian", where
0063 bit i corresponds to the number 2^i. This is also referred to in the code
0064 comments as "logical" notation.
0065 
0066 
0067 2. If QUIRK_MSB_ON_THE_RIGHT is set, we do it like this:
0068 
0069 ::
0070 
0071   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
0072   7                       6                        5                       4
0073   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
0074   3                       2                        1                       0
0075 
0076 That is, QUIRK_MSB_ON_THE_RIGHT does not affect byte positioning, but
0077 inverts bit offsets inside a byte.
0078 
0079 
0080 3. If QUIRK_LITTLE_ENDIAN is set, we do it like this:
0081 
0082 ::
0083 
0084   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
0085   4                       5                       6                       7
0086   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
0087   0                       1                       2                       3
0088 
0089 Therefore, QUIRK_LITTLE_ENDIAN means that inside the memory region, every
0090 byte from each 4-byte word is placed at its mirrored position compared to
0091 the boundary of that word.
0092 
0093 4. If QUIRK_MSB_ON_THE_RIGHT and QUIRK_LITTLE_ENDIAN are both set, we do it
0094    like this:
0095 
0096 ::
0097 
0098   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
0099   4                       5                       6                       7
0100   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
0101   0                       1                       2                       3
0102 
0103 
0104 5. If just QUIRK_LSW32_IS_FIRST is set, we do it like this:
0105 
0106 ::
0107 
0108   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
0109   3                       2                       1                        0
0110   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
0111   7                       6                       5                        4
0112 
0113 In this case the 8 byte memory region is interpreted as follows: first
0114 4 bytes correspond to the least significant 4-byte word, next 4 bytes to
0115 the more significant 4-byte word.
0116 
0117 
0118 6. If QUIRK_LSW32_IS_FIRST and QUIRK_MSB_ON_THE_RIGHT are set, we do it like
0119    this:
0120 
0121 ::
0122 
0123   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
0124   3                       2                        1                       0
0125   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
0126   7                       6                        5                       4
0127 
0128 
0129 7. If QUIRK_LSW32_IS_FIRST and QUIRK_LITTLE_ENDIAN are set, it looks like
0130    this:
0131 
0132 ::
0133 
0134   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
0135   0                       1                       2                       3
0136   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
0137   4                       5                       6                       7
0138 
0139 
0140 8. If QUIRK_LSW32_IS_FIRST, QUIRK_LITTLE_ENDIAN and QUIRK_MSB_ON_THE_RIGHT
0141    are set, it looks like this:
0142 
0143 ::
0144 
0145   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
0146   0                       1                       2                       3
0147   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
0148   4                       5                       6                       7
0149 
0150 
0151 We always think of our offsets as if there were no quirk, and we translate
0152 them afterwards, before accessing the memory region.
0153 
0154 Intended use
0155 ------------
0156 
0157 Drivers that opt to use this API first need to identify which of the above 3
0158 quirk combinations (for a total of 8) match what the hardware documentation
0159 describes. Then they should wrap the packing() function, creating a new
0160 xxx_packing() that calls it using the proper QUIRK_* one-hot bits set.
0161 
0162 The packing() function returns an int-encoded error code, which protects the
0163 programmer against incorrect API use.  The errors are not expected to occur
0164 durring runtime, therefore it is reasonable for xxx_packing() to return void
0165 and simply swallow those errors. Optionally it can dump stack or print the
0166 error description.