⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 csr1212.c

📁 Ieee1394驱动实现
💻 C
📖 第 1 页 / 共 3 页
字号:
	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, &ltail);
			/* 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 + -