static inline void free_keyval(struct csr1212_keyval *kv)
{
- if (kv->key.type == CSR1212_KV_TYPE_LEAF)
+ if ((kv->key.type == CSR1212_KV_TYPE_LEAF) &&
+ (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM))
CSR1212_FREE(kv->value.leaf.data);
CSR1212_FREE(kv);
{
struct csr1212_keyval *kv;
- for (kv = kv_list; kv != NULL; kv = kv->next) {
+ for (kv = kv_list->next; kv && (kv != kv_list); kv = kv->next) {
if (kv->offset == offset)
return kv;
}
return NULL;
}
- /* The keyval key id is not used for the root node, but a valid key id
- * that can be used for a directory needs to be passed to
- * csr1212_new_directory(). */
+ /* The keyval key id is not used for the root node, but a valid key id
+ * that can be used for a directory needs to be passed to
+ * csr1212_new_directory(). */
csr->root_kv = csr1212_new_directory(CSR1212_KV_ID_VENDOR);
if (!csr->root_kv) {
CSR1212_FREE(csr->cache_head);
{
static const int mr_map[] = { 4, 64, 1024, 0 };
+#ifdef __KERNEL__
+ BUG_ON(max_rom & ~0x3);
csr->max_rom = mr_map[max_rom];
+#else
+ if (max_rom & ~0x3) /* caller supplied invalid argument */
+ csr->max_rom = 0;
+ else
+ csr->max_rom = mr_map[max_rom];
+#endif
memcpy(csr->bus_info_data, bus_info_data, csr->bus_info_len);
}
static const int pd[4] = { 0, 4, 16, 256 };
static const int cs[16] = { 4, 2 };
struct csr1212_keyval *kv;
- int palette_size = pd[palette_depth] * cs[color_space];
+ int palette_size;
int pixel_size = (hscan * vscan + 3) & ~0x3;
- if ((palette_depth && !palette) || !pixels)
+ if (!pixels || (!palette && palette_depth) ||
+ (palette_depth & ~0x3) || (color_space & ~0xf))
return NULL;
+ palette_size = pd[palette_depth] * cs[color_space];
+
kv = csr1212_new_descriptor_leaf(1, 0, NULL,
palette_size + pixel_size +
CSR1212_ICON_DESCRIPTOR_LEAF_OVERHEAD);
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;
struct csr1212_csr_rom_cache *cache;
u_int64_t csr_addr;
- if (!csr || !csr->ops->allocate_addr_range ||
- !csr->ops->release_addr)
- return CSR1212_ENOMEM;
+ if (!csr || !csr->ops || !csr->ops->allocate_addr_range ||
+ !csr->ops->release_addr || csr->max_rom < 1)
+ return CSR1212_EINVAL;
/* 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 == ~0ULL) {
+ if (csr_addr == CSR1212_INVALID_ADDR_SPACE) {
return CSR1212_ENOMEM;
}
if (csr_addr < CSR1212_REGISTER_SPACE_BASE) {
return CSR1212_ENOMEM;
}
cache->ext_rom->offset = csr_addr - CSR1212_REGISTER_SPACE_BASE;
- cache->ext_rom->value.leaf.len = 0;
+ 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;
default:
case CSR1212_KV_TYPE_IMMEDIATE:
case CSR1212_KV_TYPE_CSR_OFFSET:
- continue;
+ break;
case CSR1212_KV_TYPE_LEAF:
case CSR1212_KV_TYPE_DIRECTORY:
/* Remove from list */
- if (dkv->prev)
+ if (dkv->prev && (dkv->prev->next == dkv))
dkv->prev->next = dkv->next;
- if (dkv->next)
+ if (dkv->next && (dkv->next->prev == dkv))
dkv->next->prev = dkv->prev;
- if (dkv == *layout_tail)
- *layout_tail = 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 = 0; /* initialize to zero */
+ dkv->value.leaf.len = -1;
/* Don't add Extended ROM leafs in the layout list,
* they are handled differently. */
break;
}
struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache,
- struct csr1212_keyval *start_kv,
- int start_pos)
+ struct csr1212_keyval *start_kv,
+ int start_pos)
{
struct csr1212_keyval *kv = start_kv;
struct csr1212_keyval *okv = start_kv;
cache->layout_head = kv;
while(kv && pos < cache->size) {
- kv->offset = cache->offset + pos;
+ /* 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:
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;
/* Make sure the Extended ROM leaf is a multiple of
* max_rom in size. */
+ if (csr->max_rom < 1)
+ return CSR1212_EINVAL;
leaf_size = (cache->len + (csr->max_rom - 1)) &
~(csr->max_rom - 1);
/* Copy the data into the cache buffer */
csr1212_fill_cache(cache);
+
+ if (cache != csr->cache_head) {
+ /* Set the length and CRC of the extended ROM. */
+ struct csr1212_keyval_img *kvi =
+ (struct csr1212_keyval_img*)cache->data;
+
+ kvi->length = CSR1212_CPU_TO_BE16(bytes_to_quads(cache->len) - 1);
+ kvi->crc = csr1212_crc16(kvi->data,
+ bytes_to_quads(cache->len) - 1);
+
+ }
}
return CSR1212_SUCCESS;
&cache->data[bytes_to_quads(offset - cache->offset)],
len);
return CSR1212_SUCCESS;
- } else if (((offset < cache->offset) &&
- ((offset + len) >= cache->offset)) ||
- ((offset >= cache->offset) &&
- ((offset + len) > (cache->offset + cache->size)))) {
- return CSR1212_EINVAL;
}
}
return CSR1212_ENOENT;
return CSR1212_EINVAL;
#if 0
- /* Apparently there are too many differnt wrong implementations of the
- * CRC algorithm that verifying them is moot. */
+ /* Apparently there are too many differnt wrong implementations of the
+ * CRC algorithm that verifying them is moot. */
if ((csr1212_crc16(bi->data, bi->crc_length) != bi->crc) &&
(csr1212_msft_crc16(bi->data, bi->crc_length) != bi->crc))
return CSR1212_EINVAL;
#endif
- cr = CSR1212_MALLOC(sizeof(struct csr1212_cache_region));
+ cr = CSR1212_MALLOC(sizeof(*cr));
if (!cr)
return CSR1212_ENOMEM;
return CSR1212_SUCCESS;
}
-static inline int csr1212_parse_dir_entry(struct csr1212_keyval *dir,
- csr1212_quad_t ki,
- u_int32_t kv_pos,
- struct csr1212_csr_rom_cache *cache)
+static int csr1212_parse_dir_entry(struct csr1212_keyval *dir,
+ csr1212_quad_t ki,
+ u_int32_t kv_pos)
{
int ret = CSR1212_SUCCESS;
struct csr1212_keyval *k = NULL;
goto fail;
}
- k = csr1212_find_keyval_offset(cache->layout_head, offset);
+ k = csr1212_find_keyval_offset(dir, offset);
if (k)
break; /* Found it. */
k->valid = 0; /* Contents not read yet so it's not valid. */
k->offset = offset;
- k->prev = cache->layout_tail;
- k->next = NULL;
- if (cache->layout_tail)
- cache->layout_tail->next = k;
- cache->layout_tail = k;
+ k->prev = dir;
+ k->next = dir->next;
+ dir->next->prev = k;
+ dir->next = k;
}
ret = csr1212_attach_keyval_to_directory(dir, k);
return ret;
}
+
int csr1212_parse_keyval(struct csr1212_keyval *kv,
struct csr1212_csr_rom_cache *cache)
{
kvi_len = CSR1212_BE16_TO_CPU(kvi->length);
#if 0
- /* Apparently there are too many differnt wrong implementations of the
- * CRC algorithm that verifying them is moot. */
+ /* Apparently there are too many differnt wrong implementations of the
+ * CRC algorithm that verifying them is moot. */
if ((csr1212_crc16(kvi->data, kvi_len) != kvi->crc) &&
(csr1212_msft_crc16(kvi->data, kvi_len) != kvi->crc)) {
ret = CSR1212_EINVAL;
csr1212_quad_t ki = kvi->data[i];
/* Some devices put null entries in their unit
- * directories. If we come across such and entry,
+ * directories. If we come across such an entry,
* then skip it. */
if (ki == 0x0)
continue;
ret = csr1212_parse_dir_entry(kv, ki,
(kv->offset +
- quads_to_bytes(i + 1)),
- cache);
+ quads_to_bytes(i + 1)));
}
kv->value.directory.len = kvi_len;
break;
case CSR1212_KV_TYPE_LEAF:
- if (kv->key.id == CSR1212_KV_ID_EXTENDED_ROM) {
- kv->value.leaf.data = cache->data;
- } else {
+ if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) {
kv->value.leaf.data = CSR1212_MALLOC(quads_to_bytes(kvi_len));
- if (!kv->value.leaf.data)
- {
+ if (!kv->value.leaf.data) {
ret = CSR1212_ENOMEM;
goto fail;
}
u_int32_t *cache_ptr;
u_int16_t kv_len = 0;
-
- if (!csr || !kv)
+ if (!csr || !kv || csr->max_rom < 1)
return CSR1212_EINVAL;
/* First find which cache the data should be in (or go in if not read
if (!cache) {
csr1212_quad_t q;
- struct csr1212_csr_rom_cache *nc;
+ u_int32_t cache_size;
/* Only create a new cache for Extended ROM leaves. */
if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)
return CSR1212_EIO;
}
- kv->value.leaf.len = quads_to_bytes(CSR1212_BE32_TO_CPU(q)>>16);
+ kv->value.leaf.len = CSR1212_BE32_TO_CPU(q) >> 16;
+
+ cache_size = (quads_to_bytes(kv->value.leaf.len + 1) +
+ (csr->max_rom - 1)) & ~(csr->max_rom - 1);
+
+ cache = csr1212_rom_cache_malloc(kv->offset, cache_size);
+ if (!cache)
+ return CSR1212_ENOMEM;
- nc = csr1212_rom_cache_malloc(kv->offset, kv->value.leaf.len);
- cache->next = nc;
- nc->prev = cache;
- csr->cache_tail = nc;
+ kv->value.leaf.data = &cache->data[1];
+ csr->cache_tail->next = cache;
+ cache->prev = csr->cache_tail;
+ cache->next = NULL;
+ csr->cache_tail = cache;
cache->filled_head =
- CSR1212_MALLOC(sizeof(struct csr1212_cache_region));
+ CSR1212_MALLOC(sizeof(*cache->filled_head));
if (!cache->filled_head) {
return CSR1212_ENOMEM;
}
cache->filled_head->next = NULL;
cache->filled_head->prev = NULL;
cache->data[0] = q;
+
+ /* Don't read the entire extended ROM now. Pieces of it will
+ * be read when entries inside it are read. */
+ return csr1212_parse_keyval(kv, cache);
}
cache_index = kv->offset - cache->offset;
/* Now seach read portions of the cache to see if it is there. */
for (cr = cache->filled_head; cr; cr = cr->next) {
if (cache_index < cr->offset_start) {
- newcr = CSR1212_MALLOC(sizeof(struct csr1212_cache_region));
+ newcr = CSR1212_MALLOC(sizeof(*newcr));
if (!newcr)
return CSR1212_ENOMEM;
if (!cr) {
cr = cache->filled_tail;
- newcr = CSR1212_MALLOC(sizeof(struct csr1212_cache_region));
+ newcr = CSR1212_MALLOC(sizeof(*newcr));
if (!newcr)
return CSR1212_ENOMEM;
int csr1212_parse_csr(struct csr1212_csr *csr)
{
static const int mr_map[] = { 4, 64, 1024, 0 };
+ struct csr1212_dentry *dentry;
int ret;
- if (!csr || !csr->ops->bus_read)
+ if (!csr || !csr->ops || !csr->ops->bus_read)
return CSR1212_EINVAL;
ret = csr1212_parse_bus_info_block(csr);
if (!csr->ops->get_max_rom)
csr->max_rom = mr_map[0]; /* default value */
- else
- csr->max_rom = mr_map[csr->ops->get_max_rom(csr->bus_info_data,
- csr->private)];
+ else {
+ int i = csr->ops->get_max_rom(csr->bus_info_data,
+ csr->private);
+ if (i & ~0x3)
+ return CSR1212_EINVAL;
+ csr->max_rom = mr_map[i];
+ }
csr->cache_head->layout_head = csr->root_kv;
csr->cache_head->layout_tail = csr->root_kv;
csr->bus_info_len;
csr->root_kv->valid = 0;
- csr1212_get_keyval(csr, csr->root_kv);
+ csr->root_kv->next = csr->root_kv;
+ csr->root_kv->prev = csr->root_kv;
+ ret = _csr1212_read_keyval(csr, csr->root_kv);
+ if (ret != CSR1212_SUCCESS)
+ return ret;
+
+ /* Scan through the Root directory finding all extended ROM regions
+ * and make cache regions for them */
+ for (dentry = csr->root_kv->value.directory.dentries_head;
+ dentry; dentry = dentry->next) {
+ if (dentry->kv->key.id == CSR1212_KV_ID_EXTENDED_ROM &&
+ !dentry->kv->valid) {
+ ret = _csr1212_read_keyval(csr, dentry->kv);
+ if (ret != CSR1212_SUCCESS)
+ return ret;
+ }
+ }
return CSR1212_SUCCESS;
}