csr1212.c
来自「linux 内核源代码」· C语言 代码 · 共 1,471 行 · 第 1/3 页
C
1,471 行
* list manipulation, this routine will descend a directory structure in a * non-recursive manner. */void csr1212_release_keyval(struct csr1212_keyval *kv){ struct csr1212_keyval *k, *a; struct csr1212_dentry dentry; struct csr1212_dentry *head, *tail; if (!atomic_dec_and_test(&kv->refcnt)) return; dentry.kv = kv; dentry.next = NULL; dentry.prev = NULL; head = &dentry; tail = head; while (head) { k = head->kv; while (k) { /* must not dec_and_test kv->refcnt again */ if (k != kv && !atomic_dec_and_test(&k->refcnt)) break; a = k->associate; if (k->key.type == CSR1212_KV_TYPE_DIRECTORY) { /* If the current entry is a directory, move all * the entries to the destruction list. */ if (k->value.directory.dentries_head) { tail->next = k->value.directory.dentries_head; k->value.directory.dentries_head->prev = tail; tail = k->value.directory.dentries_tail; } } free_keyval(k); k = a; } head = head->next; if (head) { if (head->prev && head->prev != &dentry) CSR1212_FREE(head->prev); head->prev = NULL; } else if (tail != &dentry) { CSR1212_FREE(tail); } }}void csr1212_destroy_csr(struct csr1212_csr *csr){ struct csr1212_csr_rom_cache *c, *oc; struct csr1212_cache_region *cr, *ocr; csr1212_release_keyval(csr->root_kv); c = csr->cache_head; while (c) { oc = c; cr = c->filled_head; while (cr) { ocr = cr; cr = cr->next; CSR1212_FREE(ocr); } c = c->next; CSR1212_FREE(oc); } CSR1212_FREE(csr);}/* CSR Image Creation */static int csr1212_append_new_cache(struct csr1212_csr *csr, size_t romsize){ struct csr1212_csr_rom_cache *cache; u64 csr_addr; BUG_ON(!csr || !csr->ops || !csr->ops->allocate_addr_range || !csr->ops->release_addr || csr->max_rom < 1); /* ROM size must be a multiple of csr->max_rom */ romsize = (romsize + (csr->max_rom - 1)) & ~(csr->max_rom - 1); csr_addr = csr->ops->allocate_addr_range(romsize, csr->max_rom, csr->private); if (csr_addr == CSR1212_INVALID_ADDR_SPACE) return -ENOMEM; if (csr_addr < CSR1212_REGISTER_SPACE_BASE) { /* Invalid address returned from allocate_addr_range(). */ csr->ops->release_addr(csr_addr, csr->private); return -ENOMEM; } cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE, romsize); if (!cache) { csr->ops->release_addr(csr_addr, csr->private); return -ENOMEM; } cache->ext_rom = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, CSR1212_KV_ID_EXTENDED_ROM); if (!cache->ext_rom) { csr->ops->release_addr(csr_addr, csr->private); CSR1212_FREE(cache); return -ENOMEM; } if (csr1212_attach_keyval_to_directory(csr->root_kv, cache->ext_rom) != CSR1212_SUCCESS) { csr1212_release_keyval(cache->ext_rom); csr->ops->release_addr(csr_addr, csr->private); CSR1212_FREE(cache); return -ENOMEM; } cache->ext_rom->offset = csr_addr - CSR1212_REGISTER_SPACE_BASE; cache->ext_rom->value.leaf.len = -1; cache->ext_rom->value.leaf.data = cache->data; /* Add cache to tail of cache list */ cache->prev = csr->cache_tail; csr->cache_tail->next = cache; csr->cache_tail = cache; return CSR1212_SUCCESS;}static void csr1212_remove_cache(struct csr1212_csr *csr, struct csr1212_csr_rom_cache *cache){ if (csr->cache_head == cache) csr->cache_head = cache->next; if (csr->cache_tail == cache) csr->cache_tail = cache->prev; if (cache->prev) cache->prev->next = cache->next; if (cache->next) cache->next->prev = cache->prev; if (cache->ext_rom) { csr1212_detach_keyval_from_directory(csr->root_kv, cache->ext_rom); csr1212_release_keyval(cache->ext_rom); } CSR1212_FREE(cache);}static int csr1212_generate_layout_subdir(struct csr1212_keyval *dir, struct csr1212_keyval **layout_tail){ struct csr1212_dentry *dentry; struct csr1212_keyval *dkv; struct csr1212_keyval *last_extkey_spec = NULL; struct csr1212_keyval *last_extkey = NULL; int num_entries = 0; for (dentry = dir->value.directory.dentries_head; dentry; dentry = dentry->next) { for (dkv = dentry->kv; dkv; dkv = dkv->associate) { /* Special Case: Extended Key Specifier_ID */ if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { if (last_extkey_spec == NULL) last_extkey_spec = dkv; else if (dkv->value.immediate != last_extkey_spec->value.immediate) last_extkey_spec = dkv; else continue; /* Special Case: Extended Key */ } else if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY) { if (last_extkey == NULL) last_extkey = dkv; else if (dkv->value.immediate != last_extkey->value.immediate) last_extkey = dkv; else continue; } num_entries += 1; switch (dkv->key.type) { default: case CSR1212_KV_TYPE_IMMEDIATE: case CSR1212_KV_TYPE_CSR_OFFSET: break; case CSR1212_KV_TYPE_LEAF: case CSR1212_KV_TYPE_DIRECTORY: /* Remove from list */ if (dkv->prev && (dkv->prev->next == dkv)) dkv->prev->next = dkv->next; if (dkv->next && (dkv->next->prev == dkv)) dkv->next->prev = dkv->prev; //if (dkv == *layout_tail) // *layout_tail = dkv->prev; /* Special case: Extended ROM leafs */ if (dkv->key.id == CSR1212_KV_ID_EXTENDED_ROM) { dkv->value.leaf.len = -1; /* Don't add Extended ROM leafs in the * layout list, they are handled * differently. */ break; } /* Add to tail of list */ dkv->next = NULL; dkv->prev = *layout_tail; (*layout_tail)->next = dkv; *layout_tail = dkv; break; } } } return num_entries;}static size_t csr1212_generate_layout_order(struct csr1212_keyval *kv){ struct csr1212_keyval *ltail = kv; size_t agg_size = 0; while (kv) { switch (kv->key.type) { case CSR1212_KV_TYPE_LEAF: /* Add 1 quadlet for crc/len field */ agg_size += kv->value.leaf.len + 1; break; case CSR1212_KV_TYPE_DIRECTORY: kv->value.directory.len = csr1212_generate_layout_subdir(kv, <ail); /* Add 1 quadlet for crc/len field */ agg_size += kv->value.directory.len + 1; break; } kv = kv->next; } return quads_to_bytes(agg_size);}static struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, struct csr1212_keyval *start_kv, int start_pos){ struct csr1212_keyval *kv = start_kv; struct csr1212_keyval *okv = start_kv; int pos = start_pos; int kv_len = 0, okv_len = 0; cache->layout_head = kv; while (kv && pos < cache->size) { /* Special case: Extended ROM leafs */ if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) kv->offset = cache->offset + pos; switch (kv->key.type) { case CSR1212_KV_TYPE_LEAF: kv_len = kv->value.leaf.len; break; case CSR1212_KV_TYPE_DIRECTORY: kv_len = kv->value.directory.len; break; default: /* Should never get here */ WARN_ON(1); break; } pos += quads_to_bytes(kv_len + 1); if (pos <= cache->size) { okv = kv; okv_len = kv_len; kv = kv->next; } } cache->layout_tail = okv; cache->len = okv->offset - cache->offset + quads_to_bytes(okv_len + 1); return kv;}#define CSR1212_KV_KEY_SHIFT 24#define CSR1212_KV_KEY_TYPE_SHIFT 6#define CSR1212_KV_KEY_ID_MASK 0x3f#define CSR1212_KV_KEY_TYPE_MASK 0x3 /* after shift */static voidcsr1212_generate_tree_subdir(struct csr1212_keyval *dir, u32 *data_buffer){ struct csr1212_dentry *dentry; struct csr1212_keyval *last_extkey_spec = NULL; struct csr1212_keyval *last_extkey = NULL; int index = 0; for (dentry = dir->value.directory.dentries_head; dentry; dentry = dentry->next) { struct csr1212_keyval *a; for (a = dentry->kv; a; a = a->associate) { u32 value = 0; /* Special Case: Extended Key Specifier_ID */ if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { if (last_extkey_spec == NULL) last_extkey_spec = a; else if (a->value.immediate != last_extkey_spec->value.immediate) last_extkey_spec = a; else continue; /* Special Case: Extended Key */ } else if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY) { if (last_extkey == NULL) last_extkey = a; else if (a->value.immediate != last_extkey->value.immediate) last_extkey = a; else continue; } switch (a->key.type) { case CSR1212_KV_TYPE_IMMEDIATE: value = a->value.immediate; break; case CSR1212_KV_TYPE_CSR_OFFSET: value = a->value.csr_offset; break; case CSR1212_KV_TYPE_LEAF: value = a->offset; value -= dir->offset + quads_to_bytes(1+index); value = bytes_to_quads(value); break; case CSR1212_KV_TYPE_DIRECTORY: value = a->offset; value -= dir->offset + quads_to_bytes(1+index); value = bytes_to_quads(value); break; default: /* Should never get here */ WARN_ON(1); break; } value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) << CSR1212_KV_KEY_SHIFT; value |= (a->key.type & CSR1212_KV_KEY_TYPE_MASK) << (CSR1212_KV_KEY_SHIFT + CSR1212_KV_KEY_TYPE_SHIFT); data_buffer[index] = cpu_to_be32(value); index++; } }}struct csr1212_keyval_img { u16 length; u16 crc; /* Must be last */ u32 data[0]; /* older gcc can't handle [] which is standard */};static void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache){ struct csr1212_keyval *kv, *nkv; struct csr1212_keyval_img *kvi; for (kv = cache->layout_head; kv != cache->layout_tail->next; kv = nkv) { kvi = (struct csr1212_keyval_img *)(cache->data + bytes_to_quads(kv->offset - cache->offset)); switch (kv->key.type) { default: case CSR1212_KV_TYPE_IMMEDIATE: case CSR1212_KV_TYPE_CSR_OFFSET: /* Should never get here */ WARN_ON(1); break; case CSR1212_KV_TYPE_LEAF: /* Don't copy over Extended ROM areas, they are * already filled out! */ if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) memcpy(kvi->data, kv->value.leaf.data, quads_to_bytes(kv->value.leaf.len)); kvi->length = cpu_to_be16(kv->value.leaf.len); kvi->crc = csr1212_crc16(kvi->data, kv->value.leaf.len); break; case CSR1212_KV_TYPE_DIRECTORY: csr1212_generate_tree_subdir(kv, kvi->data); kvi->length = cpu_to_be16(kv->value.directory.len); kvi->crc = csr1212_crc16(kvi->data, kv->value.directory.len); break; } nkv = kv->next; if (kv->prev) kv->prev->next = NULL; if (kv->next) kv->next->prev = NULL; kv->prev = NULL; kv->next = NULL; }}/* This size is arbitrarily chosen. * The struct overhead is subtracted for more economic allocations. */#define CSR1212_EXTENDED_ROM_SIZE (2048 - sizeof(struct csr1212_csr_rom_cache))int csr1212_generate_csr_image(struct csr1212_csr *csr){ struct csr1212_bus_info_block_img *bi; struct csr1212_csr_rom_cache *cache; struct csr1212_keyval *kv; size_t agg_size; int ret; int init_offset; BUG_ON(!csr); cache = csr->cache_head; bi = (struct csr1212_bus_info_block_img*)cache->data; bi->length = bytes_to_quads(csr->bus_info_len) - 1; bi->crc_length = bi->length; bi->crc = csr1212_crc16(bi->data, bi->crc_length); csr->root_kv->next = NULL; csr->root_kv->prev = NULL; agg_size = csr1212_generate_layout_order(csr->root_kv); init_offset = csr->bus_info_len; for (kv = csr->root_kv, cache = csr->cache_head; kv; cache = cache->next) { if (!cache) { /* Estimate approximate number of additional cache * regions needed (it assumes that the cache holding * the first 1K Config ROM space always exists). */ int est_c = agg_size / (CSR1212_EXTENDED_ROM_SIZE - (2 * sizeof(u32))) + 1; /* Add additional cache regions, extras will be * removed later */ for (; est_c; est_c--) { ret = csr1212_append_new_cache(csr, CSR1212_EXTENDED_ROM_SIZE); if (ret != CSR1212_SUCCESS) return ret; } /* Need to re-layout for additional cache regions */ agg_size = csr1212_generate_layout_order(csr->root_kv); kv = csr->root_kv; cache = csr->cache_head; init_offset = csr->bus_info_len; } kv = csr1212_generate_positions(cache, kv, init_offset); agg_size -= cache->len; init_offset = sizeof(u32); } /* Remove unused, excess cache regions */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?