Shad Ansari | 2f7f9be | 2017-06-07 13:34:53 -0700 | [diff] [blame^] | 1 | /* |
| 2 | <:copyright-BRCM:2016:DUAL/GPL:standard |
| 3 | |
| 4 | Broadcom Proprietary and Confidential.(c) 2016 Broadcom |
| 5 | All Rights Reserved |
| 6 | |
| 7 | Unless you and Broadcom execute a separate written software license |
| 8 | agreement governing use of this software, this software is licensed |
| 9 | to you under the terms of the GNU General Public License version 2 |
| 10 | (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, |
| 11 | with the following added to such license: |
| 12 | |
| 13 | As a special exception, the copyright holders of this software give |
| 14 | you permission to link this software with independent modules, and |
| 15 | to copy and distribute the resulting executable under terms of your |
| 16 | choice, provided that you also meet, for each linked independent |
| 17 | module, the terms and conditions of the license of that module. |
| 18 | An independent module is a module which is not derived from this |
| 19 | software. The special exception does not apply to any modifications |
| 20 | of the software. |
| 21 | |
| 22 | Not withstanding the above, under no circumstances may you combine |
| 23 | this software in any way with any other Broadcom software provided |
| 24 | under a license other than the GPL, without Broadcom's express prior |
| 25 | written consent. |
| 26 | |
| 27 | :> |
| 28 | */ |
| 29 | |
| 30 | |
| 31 | #include "bcmos_system.h" |
| 32 | #include "math.h" |
| 33 | #include "bcmolt_bit_utils.h" |
| 34 | |
| 35 | |
| 36 | /** dynamically allocate space for an array of `nbits' bits and initalize |
| 37 | * the bits to all be zero. |
| 38 | * |
| 39 | * \param[out] bv pointer to a bit_vector struct. |
| 40 | * \param[in] nbits number of bits to allocate. |
| 41 | * \return FALSE if space was not available, otherwise TRUE. |
| 42 | */ |
| 43 | bcmos_bool bcmolt_bv_new(bit_vector *bv, const uint32_t nbits) |
| 44 | { |
| 45 | uint32_t nwords = BCMOS_DIVIDE_ROUND_UP(nbits, (BITS_SZ)); |
| 46 | |
| 47 | bv->nbits = nbits; |
| 48 | bv->vector = (bv_bits *)calloc(nwords, sizeof(bv_bits)); |
| 49 | return (bv->vector != NULL); |
| 50 | } |
| 51 | |
| 52 | |
| 53 | /** return the value of the `offset'th bit element of the bit vector. |
| 54 | * |
| 55 | * \param[in] bv pointer to a bit_vector struct. |
| 56 | * \param[in] offset offset of bit to test. |
| 57 | * \return FALSE if the bit offset is out of range, otherwise TRUE. |
| 58 | */ |
| 59 | bcmos_bool bcmolt_bv_get(const bit_vector *bv, const uint32_t offset) |
| 60 | { |
| 61 | bcmos_bool rv = BCMOS_FALSE; |
| 62 | |
| 63 | if (offset <= bv->nbits) |
| 64 | { |
| 65 | rv = test_bits_set(bv->vector[(offset / BITS_SZ)], |
| 66 | (1 << (offset % BITS_SZ))); |
| 67 | } |
| 68 | else |
| 69 | { |
| 70 | BCMOS_TRACE_ERR("out of range %u\n", offset); |
| 71 | } |
| 72 | return rv; |
| 73 | } |
| 74 | |
| 75 | |
| 76 | /** set or clear the bit in position `offset' of the bit vector. |
| 77 | * bv->vector[bit_pos] is to be set (assigned to 1) if value is TRUE, |
| 78 | * otherwise it is to be cleared (assigned to 0). |
| 79 | * |
| 80 | * \param[in] bv pointer to a bit_vector struct. |
| 81 | * \param[in] offset offset of bit to set or clear. |
| 82 | * \param[in] value boolean value. TRUE for set, BCMOS_FALSE for clear. |
| 83 | * \return FALSE if the bit offset is out of range, otherwise TRUE. |
| 84 | */ |
| 85 | void bcmolt_bv_assign(bit_vector *bv, |
| 86 | const uint32_t offset, |
| 87 | const bcmos_bool value) |
| 88 | { |
| 89 | if (offset <= bv->nbits) |
| 90 | { |
| 91 | if (value) |
| 92 | { |
| 93 | bv->vector[offset / BITS_SZ] |= (1 << (offset % BITS_SZ)); |
| 94 | } |
| 95 | else |
| 96 | { |
| 97 | bv->vector[offset / BITS_SZ] &= ~(1 << (offset % BITS_SZ)); |
| 98 | } |
| 99 | } |
| 100 | else |
| 101 | { |
| 102 | BCMOS_TRACE_ERR("out of range %u\n", offset); |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | |
| 107 | /** toggle the bit in position `offset' of the bit vector. |
| 108 | * i.e. if it was 1 it is 0; if it was 0 it is 1. |
| 109 | * |
| 110 | * \param[in] bv pointer to a bit_vector struct. |
| 111 | * \param[in] offset offset of bit to toggle. |
| 112 | * \return FALSE if the bit offset is out of range, otherwise TRUE. |
| 113 | */ |
| 114 | void bcmolt_bv_toggle(bit_vector *bv, const uint32_t offset) |
| 115 | { |
| 116 | if (offset <= bv->nbits) |
| 117 | { |
| 118 | bv->vector[offset / BITS_SZ] ^= (1 << (offset % BITS_SZ)); |
| 119 | } |
| 120 | else |
| 121 | { |
| 122 | BCMOS_TRACE_ERR("out of range %u\n", offset); |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | |
| 127 | /** copy bit vector from 'src' to 'dst'. |
| 128 | * |
| 129 | * \param[out] dst pointer to a bit_vector struct to copy to. |
| 130 | * \param[in] src pointer to a bit_vector struct to copy from. |
| 131 | * \param[in] nbits number of bits to copy. |
| 132 | * \return none. |
| 133 | */ |
| 134 | void bcmolt_bv_copy(bit_vector *dst, |
| 135 | const bit_vector *src, |
| 136 | const uint32_t nbits) |
| 137 | { |
| 138 | uint32_t i; |
| 139 | uint32_t nwords = nbits / BITS_SZ; |
| 140 | bv_bits bit_remainder = nbits % BITS_SZ; |
| 141 | |
| 142 | if ((nbits <= dst->nbits) && (nbits <= src->nbits)) |
| 143 | { |
| 144 | for (i = 0; i < nwords; i++) |
| 145 | { |
| 146 | dst->vector[i] = src->vector[i]; |
| 147 | } |
| 148 | |
| 149 | if (0 != bit_remainder) |
| 150 | { |
| 151 | dst->vector[nwords] = (dst->vector[nwords] & ~((2^bit_remainder) - 1)) | |
| 152 | (src->vector[nwords] & ((2^bit_remainder) - 1)); |
| 153 | } |
| 154 | } |
| 155 | else |
| 156 | { |
| 157 | BCMOS_TRACE_ERR("out of range %u\n", nbits); |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | |
| 162 | /** Print bit pattern of word FORMATTED to string. |
| 163 | * |
| 164 | * \param[in] value value to transform to bit string. |
| 165 | * \param[in] bitcnt count of bits to be shown. |
| 166 | * \param[out] outstr pointer to a output buffer to store the string. |
| 167 | * \return none |
| 168 | * \warning this fn doesn't check the size of 'outstr'. |
| 169 | * the caller should ensure 'outstr' has enough room for the |
| 170 | * bit string, space characters and null terminator. |
| 171 | */ |
| 172 | void bcmolt_bit_string(const bv_bits value, const uint32_t bitcnt, char *outstr) |
| 173 | { |
| 174 | uint32_t offset; |
| 175 | |
| 176 | if (bitcnt <= BITS_SZ) |
| 177 | { |
| 178 | for (offset = 0; offset < bitcnt; offset++) |
| 179 | { |
| 180 | *outstr++ = ((value >> offset) & 1) + '0'; |
| 181 | if ((((offset + 1) % 4) == 0)) |
| 182 | { |
| 183 | *outstr++ = ' '; |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | *outstr = '\0'; |
| 189 | } |
| 190 | |
| 191 | |
| 192 | /** Print all bits in the bit vector. |
| 193 | * |
| 194 | * \param[in] bv pointer to a bit_vector struct. |
| 195 | * \return none. |
| 196 | */ |
| 197 | void bcmolt_bv_dump(const bit_vector *bv) |
| 198 | { |
| 199 | uint32_t idx; /* word idx */ |
| 200 | char outstr[BITS_SZ + 8 + 1]; /* 8 spaces + null */ |
| 201 | |
| 202 | for (idx = 0; idx < (bv->nbits / BITS_SZ); idx++) |
| 203 | { |
| 204 | bcmolt_bit_string(bv->vector[idx], BITS_SZ, outstr); |
| 205 | bcmos_printf("%s\n", outstr); |
| 206 | } |
| 207 | |
| 208 | if (0 != (bv->nbits % BITS_SZ)) |
| 209 | { |
| 210 | bcmolt_bit_string(bv->vector[idx], (bv->nbits % BITS_SZ), outstr); |
| 211 | bcmos_printf("%s\n", outstr); |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | |
| 216 | /** Count the number of bits set in a long integer. |
| 217 | * |
| 218 | * \param[in] num integer value. |
| 219 | * \return number of bits set. |
| 220 | */ |
| 221 | uint32_t bcmolt_bit_count(uint32_t num) |
| 222 | { |
| 223 | num = ((num & 0xAAAAAAAAL) >> 1) + (num & 0x55555555L); |
| 224 | num = ((num & 0xCCCCCCCCL) >> 2) + (num & 0x33333333L); |
| 225 | num = ((num & 0xF0F0F0F0L) >> 4) + (num & 0x0F0F0F0FL); |
| 226 | num = ((num & 0xFF00FF00L) >> 8) + (num & 0x00FF00FFL); |
| 227 | num = ((num & 0xFFFF0000L) >> 16) + (num & 0x0000FFFFL); |
| 228 | return num; |
| 229 | } |
| 230 | |
| 231 | |
| 232 | /** Count the number of bits set in the whole bit vector. |
| 233 | * |
| 234 | * \param[in] bv pointer to the bit vector struct. |
| 235 | * \return number of bits set. |
| 236 | */ |
| 237 | uint32_t bcmolt_bv_bit_count(const bit_vector *bv) |
| 238 | { |
| 239 | uint32_t nwords = BCMOS_DIVIDE_ROUND_UP(bv->nbits, (BITS_SZ)); |
| 240 | uint32_t cnt = 0; |
| 241 | |
| 242 | while (0 != nwords--) |
| 243 | { |
| 244 | cnt += bcmolt_bit_count(bv->vector[nwords]); |
| 245 | } |
| 246 | |
| 247 | return cnt; |
| 248 | } |
| 249 | |
| 250 | |
| 251 | |
| 252 | /** Copy bit range from a 32-bit word array to an arbitrary bit position of |
| 253 | * the destination 32-bit word array. |
| 254 | * |
| 255 | * \param[in] dst destination buffer |
| 256 | * \param[in] dst_bytes destination buffer size in bytes |
| 257 | * \param[in] dst_bit_pos least bit position to dest |
| 258 | * \param[in] src source buffer |
| 259 | * \param[in] n_bits how many bits to copy from source |
| 260 | * \note src is in little endian and dst is in big endian. |
| 261 | */ |
| 262 | void bcmos_bit_range_set(uint32_t *dst, uint32_t dst_bytes, uint16_t dst_bit_pos, |
| 263 | uint32_t *src, uint16_t n_bits) |
| 264 | { |
| 265 | uint16_t bp = dst_bit_pos; |
| 266 | uint16_t len; |
| 267 | uint32_t wp; |
| 268 | uint32_t mask; |
| 269 | uint32_t src_idx; |
| 270 | uint32_t dst_idx; |
| 271 | |
| 272 | wp = bp / 32; |
| 273 | bp = bp & (32 - 1); |
| 274 | src_idx = 0; |
| 275 | |
| 276 | for (len = n_bits; len > 0; len -= 32) |
| 277 | { |
| 278 | if (bp != 0) |
| 279 | { |
| 280 | mask = (len < 32) ? (1 << len) - 1 : 0xFFFFFFFF; |
| 281 | dst_idx = wp; |
| 282 | dst[dst_idx] &= ~(mask << bp); |
| 283 | dst[dst_idx] |= src[src_idx] << bp; |
| 284 | wp++; |
| 285 | if (len > (32 - bp)) |
| 286 | { |
| 287 | dst_idx = wp; |
| 288 | dst[dst_idx] &= ~(mask >> (32 - bp)); |
| 289 | dst[dst_idx] |= src[src_idx] >> (32 - bp) & ((1 << bp) - 1); |
| 290 | } |
| 291 | } |
| 292 | else |
| 293 | { |
| 294 | dst_idx = wp; |
| 295 | if (len < 32) |
| 296 | { |
| 297 | mask = (1 << len) - 1; |
| 298 | dst[dst_idx] &= ~mask; |
| 299 | dst[dst_idx] |= src[src_idx] << bp; |
| 300 | } |
| 301 | else |
| 302 | { |
| 303 | dst[dst_idx] = src[src_idx]; |
| 304 | } |
| 305 | wp++; |
| 306 | } |
| 307 | src_idx++; |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | |
| 312 | /** Get bit range at an arbitrary bit position of a 32-bit word array |
| 313 | * |
| 314 | * \param[in] src source buffer (e.g. dataport) |
| 315 | * \param[in] src_bytes source buffer size in bytes |
| 316 | * \param[in] src_bit_pos least bit position of the source |
| 317 | * \param[in] dst destination buffer to store the bit value |
| 318 | * \param[in] n_bits how many bits to copy from srouce |
| 319 | * \note src is in big endian and dst is in little endian. |
| 320 | */ |
| 321 | void bcmos_bit_range_get(const uint32_t *src, uint32_t src_bytes, |
| 322 | uint16_t src_bit_pos, uint32_t *dst, uint16_t n_bits) |
| 323 | { |
| 324 | uint16_t bp = src_bit_pos; /* for readability */ |
| 325 | uint16_t len = n_bits; |
| 326 | uint32_t wp; |
| 327 | uint32_t src_idx; |
| 328 | uint32_t dst_idx; |
| 329 | |
| 330 | if (n_bits == 1) |
| 331 | { |
| 332 | wp = bp / 32; |
| 333 | bp = bp & (32 - 1); |
| 334 | src_idx = BCMOS_DIVIDE_ROUND_UP(src_bytes, 4) - 1 - wp; |
| 335 | dst[0] = ((src[src_idx] & (1 << bp)) != 0) ? 1: 0; |
| 336 | return; |
| 337 | } |
| 338 | |
| 339 | wp = bp / 32; |
| 340 | bp = bp & (32 - 1); |
| 341 | dst_idx = 0; |
| 342 | |
| 343 | for (; len > 0; len -= 32) |
| 344 | { |
| 345 | if (bp != 0) |
| 346 | { |
| 347 | src_idx = BCMOS_DIVIDE_ROUND_UP(src_bytes, 4) - 1 - wp; |
| 348 | dst[dst_idx] = src[src_idx] >> bp & ((1 << (32 - bp)) - 1); |
| 349 | wp++; |
| 350 | if (len > (32 - bp)) |
| 351 | { |
| 352 | src_idx = BCMOS_DIVIDE_ROUND_UP(src_bytes, 4) - 1 - wp; |
| 353 | dst[dst_idx] |= src[src_idx] << (32 - bp); |
| 354 | } |
| 355 | } |
| 356 | else |
| 357 | { |
| 358 | src_idx = BCMOS_DIVIDE_ROUND_UP(src_bytes, 4) - 1 - wp; |
| 359 | dst[dst_idx] = src[src_idx]; |
| 360 | wp++; |
| 361 | } |
| 362 | |
| 363 | if (len < 32) |
| 364 | { |
| 365 | dst[dst_idx] &= ((1 << len) - 1); |
| 366 | } |
| 367 | dst_idx++; |
| 368 | } |
| 369 | } |
| 370 | |