📄 csr1212.c
字号:
struct csr1212_keyval *k, *a;
struct csr1212_dentry dentry;
struct csr1212_dentry *head, *tail;
dentry.kv = kv;
dentry.next = NULL;
dentry.prev = NULL;
head = &dentry;
tail = head;
while (head) {
k = head->kv;
while (k) {
k->refcnt--;
if (k->refcnt > 0)
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_release_keyval(struct csr1212_keyval *kv)
{
if (kv->refcnt > 1)
kv->refcnt--;
else
csr1212_destroy_keyval(kv);
}
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 void
csr1212_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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -