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, &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 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 + -
显示快捷键?