csr1212.c

来自「linux 内核源代码」· C语言 代码 · 共 1,471 行 · 第 1/3 页

C
1,471
字号
	while (cache) {		struct csr1212_csr_rom_cache *oc = cache;		cache = cache->next;		csr1212_remove_cache(csr, oc);	}	/* Go through the list backward so that when done, the correct CRC	 * will be calculated for the Extended ROM areas. */	for (cache = csr->cache_tail; cache; cache = cache->prev) {		/* Only Extended ROM caches should have this set. */		if (cache->ext_rom) {			int leaf_size;			/* Make sure the Extended ROM leaf is a multiple of			 * max_rom in size. */			BUG_ON(csr->max_rom < 1);			leaf_size = (cache->len + (csr->max_rom - 1)) &				~(csr->max_rom - 1);			/* Zero out the unused ROM region */			memset(cache->data + bytes_to_quads(cache->len), 0x00,			       leaf_size - cache->len);			/* Subtract leaf header */			leaf_size -= sizeof(u32);			/* Update the Extended ROM leaf length */			cache->ext_rom->value.leaf.len =				bytes_to_quads(leaf_size);		} else {			/* Zero out the unused ROM region */			memset(cache->data + bytes_to_quads(cache->len), 0x00,			       cache->size - cache->len);		}		/* 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;			u16 len = bytes_to_quads(cache->len) - 1;			kvi->length = cpu_to_be16(len);			kvi->crc = csr1212_crc16(kvi->data, len);		}	}	return CSR1212_SUCCESS;}int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, u32 len){	struct csr1212_csr_rom_cache *cache;	for (cache = csr->cache_head; cache; cache = cache->next)		if (offset >= cache->offset &&		    (offset + len) <= (cache->offset + cache->size)) {			memcpy(buffer, &cache->data[					bytes_to_quads(offset - cache->offset)],			       len);			return CSR1212_SUCCESS;		}	return -ENOENT;}/* Parse a chunk of data as a Config ROM */static int csr1212_parse_bus_info_block(struct csr1212_csr *csr){	struct csr1212_bus_info_block_img *bi;	struct csr1212_cache_region *cr;	int i;	int ret;	/* IEEE 1212 says that the entire bus info block should be readable in	 * a single transaction regardless of the max_rom value.	 * Unfortunately, many IEEE 1394 devices do not abide by that, so the	 * bus info block will be read 1 quadlet at a time.  The rest of the	 * ConfigROM will be read according to the max_rom field. */	for (i = 0; i < csr->bus_info_len; i += sizeof(u32)) {		ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i,			sizeof(u32), &csr->cache_head->data[bytes_to_quads(i)],			csr->private);		if (ret != CSR1212_SUCCESS)			return ret;		/* check ROM header's info_length */		if (i == 0 &&		    be32_to_cpu(csr->cache_head->data[0]) >> 24 !=		    bytes_to_quads(csr->bus_info_len) - 1)			return -EINVAL;	}	bi = (struct csr1212_bus_info_block_img*)csr->cache_head->data;	csr->crc_len = quads_to_bytes(bi->crc_length);	/* IEEE 1212 recommends that crc_len be equal to bus_info_len, but that	 * is not always the case, so read the rest of the crc area 1 quadlet at	 * a time. */	for (i = csr->bus_info_len; i <= csr->crc_len; i += sizeof(u32)) {		ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i,			sizeof(u32), &csr->cache_head->data[bytes_to_quads(i)],			csr->private);		if (ret != CSR1212_SUCCESS)			return ret;	}	/* Apparently there are many different wrong implementations of the CRC	 * algorithm.  We don't fail, we just warn. */	if ((csr1212_crc16(bi->data, bi->crc_length) != bi->crc) &&	    (csr1212_msft_crc16(bi->data, bi->crc_length) != bi->crc))		printk(KERN_DEBUG "IEEE 1394 device has ROM CRC error\n");	cr = CSR1212_MALLOC(sizeof(*cr));	if (!cr)		return -ENOMEM;	cr->next = NULL;	cr->prev = NULL;	cr->offset_start = 0;	cr->offset_end = csr->crc_len + 4;	csr->cache_head->filled_head = cr;	csr->cache_head->filled_tail = cr;	return CSR1212_SUCCESS;}#define CSR1212_KV_KEY(q)	(be32_to_cpu(q) >> CSR1212_KV_KEY_SHIFT)#define CSR1212_KV_KEY_TYPE(q)	(CSR1212_KV_KEY(q) >> CSR1212_KV_KEY_TYPE_SHIFT)#define CSR1212_KV_KEY_ID(q)	(CSR1212_KV_KEY(q) & CSR1212_KV_KEY_ID_MASK)#define CSR1212_KV_VAL_MASK	0xffffff#define CSR1212_KV_VAL(q)	(be32_to_cpu(q) & CSR1212_KV_VAL_MASK)static intcsr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos){	int ret = CSR1212_SUCCESS;	struct csr1212_keyval *k = NULL;	u32 offset;	bool keep_keyval = true;	switch (CSR1212_KV_KEY_TYPE(ki)) {	case CSR1212_KV_TYPE_IMMEDIATE:		k = csr1212_new_immediate(CSR1212_KV_KEY_ID(ki),					  CSR1212_KV_VAL(ki));		if (!k) {			ret = -ENOMEM;			goto out;		}		/* Don't keep local reference when parsing. */		keep_keyval = false;		break;	case CSR1212_KV_TYPE_CSR_OFFSET:		k = csr1212_new_csr_offset(CSR1212_KV_KEY_ID(ki),					   CSR1212_KV_VAL(ki));		if (!k) {			ret = -ENOMEM;			goto out;		}		/* Don't keep local reference when parsing. */		keep_keyval = false;		break;	default:		/* Compute the offset from 0xffff f000 0000. */		offset = quads_to_bytes(CSR1212_KV_VAL(ki)) + kv_pos;		if (offset == kv_pos) {			/* Uh-oh.  Can't have a relative offset of 0 for Leaves			 * or Directories.  The Config ROM image is most likely			 * messed up, so we'll just abort here. */			ret = -EIO;			goto out;		}		k = csr1212_find_keyval_offset(dir, offset);		if (k)			break;		/* Found it. */		if (CSR1212_KV_KEY_TYPE(ki) == CSR1212_KV_TYPE_DIRECTORY)			k = csr1212_new_directory(CSR1212_KV_KEY_ID(ki));		else			k = csr1212_new_leaf(CSR1212_KV_KEY_ID(ki), NULL, 0);		if (!k) {			ret = -ENOMEM;			goto out;		}		/* Don't keep local reference when parsing. */		keep_keyval = false;		/* Contents not read yet so it's not valid. */		k->valid = 0;		k->offset = offset;		k->prev = dir;		k->next = dir->next;		dir->next->prev = k;		dir->next = k;	}	ret = __csr1212_attach_keyval_to_directory(dir, k, keep_keyval);out:	if (ret != CSR1212_SUCCESS && k != NULL)		free_keyval(k);	return ret;}int csr1212_parse_keyval(struct csr1212_keyval *kv,			 struct csr1212_csr_rom_cache *cache){	struct csr1212_keyval_img *kvi;	int i;	int ret = CSR1212_SUCCESS;	int kvi_len;	kvi = (struct csr1212_keyval_img*)		&cache->data[bytes_to_quads(kv->offset - cache->offset)];	kvi_len = be16_to_cpu(kvi->length);	/* Apparently there are many different wrong implementations of the CRC	 * algorithm.  We don't fail, we just warn. */	if ((csr1212_crc16(kvi->data, kvi_len) != kvi->crc) &&	    (csr1212_msft_crc16(kvi->data, kvi_len) != kvi->crc))		printk(KERN_DEBUG "IEEE 1394 device has ROM CRC error\n");	switch (kv->key.type) {	case CSR1212_KV_TYPE_DIRECTORY:		for (i = 0; i < kvi_len; i++) {			u32 ki = kvi->data[i];			/* Some devices put null entries in their unit			 * 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));		}		kv->value.directory.len = kvi_len;		break;	case CSR1212_KV_TYPE_LEAF:		if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) {			size_t size = quads_to_bytes(kvi_len);			kv->value.leaf.data = CSR1212_MALLOC(size);			if (!kv->value.leaf.data) {				ret = -ENOMEM;				goto out;			}			kv->value.leaf.len = kvi_len;			memcpy(kv->value.leaf.data, kvi->data, size);		}		break;	}	kv->valid = 1;out:	return ret;}static intcsr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv){	struct csr1212_cache_region *cr, *ncr, *newcr = NULL;	struct csr1212_keyval_img *kvi = NULL;	struct csr1212_csr_rom_cache *cache;	int cache_index;	u64 addr;	u32 *cache_ptr;	u16 kv_len = 0;	BUG_ON(!csr || !kv || csr->max_rom < 1);	/* First find which cache the data should be in (or go in if not read	 * yet). */	for (cache = csr->cache_head; cache; cache = cache->next)		if (kv->offset >= cache->offset &&		    kv->offset < (cache->offset + cache->size))			break;	if (!cache) {		u32 q, cache_size;		/* Only create a new cache for Extended ROM leaves. */		if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)			return -EINVAL;		if (csr->ops->bus_read(csr,				       CSR1212_REGISTER_SPACE_BASE + kv->offset,				       sizeof(u32), &q, csr->private))			return -EIO;		kv->value.leaf.len = 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 -ENOMEM;		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(*cache->filled_head));		if (!cache->filled_head)			return -ENOMEM;		cache->filled_head->offset_start = 0;		cache->filled_head->offset_end = sizeof(u32);		cache->filled_tail = cache->filled_head;		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(*newcr));			if (!newcr)				return -ENOMEM;			newcr->offset_start = cache_index & ~(csr->max_rom - 1);			newcr->offset_end = newcr->offset_start;			newcr->next = cr;			newcr->prev = cr->prev;			cr->prev = newcr;			cr = newcr;			break;		} else if ((cache_index >= cr->offset_start) &&			   (cache_index < cr->offset_end)) {			kvi = (struct csr1212_keyval_img*)				(&cache->data[bytes_to_quads(cache_index)]);			kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1);			break;		} else if (cache_index == cr->offset_end) {			break;		}	}	if (!cr) {		cr = cache->filled_tail;		newcr = CSR1212_MALLOC(sizeof(*newcr));		if (!newcr)			return -ENOMEM;		newcr->offset_start = cache_index & ~(csr->max_rom - 1);		newcr->offset_end = newcr->offset_start;		newcr->prev = cr;		newcr->next = cr->next;		cr->next = newcr;		cr = newcr;		cache->filled_tail = newcr;	}	while(!kvi || cr->offset_end < cache_index + kv_len) {		cache_ptr = &cache->data[bytes_to_quads(cr->offset_end &							~(csr->max_rom - 1))];		addr = (CSR1212_CSR_ARCH_REG_SPACE_BASE + cache->offset +			cr->offset_end) & ~(csr->max_rom - 1);		if (csr->ops->bus_read(csr, addr, csr->max_rom, cache_ptr,				       csr->private)) {			if (csr->max_rom == 4)				/* We've got problems! */				return -EIO;			/* Apperently the max_rom value was a lie, set it to			 * do quadlet reads and try again. */			csr->max_rom = 4;			continue;		}		cr->offset_end += csr->max_rom - (cr->offset_end &						  (csr->max_rom - 1));		if (!kvi && (cr->offset_end > cache_index)) {			kvi = (struct csr1212_keyval_img*)				(&cache->data[bytes_to_quads(cache_index)]);			kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1);		}		if ((kv_len + (kv->offset - cache->offset)) > cache->size) {			/* The Leaf or Directory claims its length extends			 * beyond the ConfigROM image region and thus beyond the			 * end of our cache region.  Therefore, we abort now			 * rather than seg faulting later. */			return -EIO;		}		ncr = cr->next;		if (ncr && (cr->offset_end >= ncr->offset_start)) {			/* consolidate region entries */			ncr->offset_start = cr->offset_start;			if (cr->prev)				cr->prev->next = cr->next;			ncr->prev = cr->prev;			if (cache->filled_head == cr)				cache->filled_head = ncr;			CSR1212_FREE(cr);			cr = ncr;		}	}	return csr1212_parse_keyval(kv, cache);}struct csr1212_keyval *csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv){	if (!kv)		return NULL;	if (!kv->valid)		if (csr1212_read_keyval(csr, kv) != CSR1212_SUCCESS)			return NULL;	return kv;}int csr1212_parse_csr(struct csr1212_csr *csr){	static const int mr_map[] = { 4, 64, 1024, 0 };	struct csr1212_dentry *dentry;	int ret;	BUG_ON(!csr || !csr->ops || !csr->ops->bus_read);	ret = csr1212_parse_bus_info_block(csr);	if (ret != CSR1212_SUCCESS)		return ret;	if (!csr->ops->get_max_rom) {		csr->max_rom = mr_map[0];	/* default value */	} else {		int i = csr->ops->get_max_rom(csr->bus_info_data,					      csr->private);		if (i & ~0x3)			return -EINVAL;		csr->max_rom = mr_map[i];	}	csr->cache_head->layout_head = csr->root_kv;	csr->cache_head->layout_tail = csr->root_kv;	csr->root_kv->offset = (CSR1212_CONFIG_ROM_SPACE_BASE & 0xffff) +		csr->bus_info_len;	csr->root_kv->valid = 0;	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;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?