X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fieee1394%2Fcsr1212.c;h=9f95337139e3144424aba9c2e452939563f24586;hb=9ea319b61613085f501a79cf8d405cb221d084f3;hp=3c044fc52352a0ae341d6c25b577cd3ef2186dc8;hpb=c94ccf9e3389ff55078a049bfe59b82f854436e8;p=linux-2.6 diff --git a/drivers/ieee1394/csr1212.c b/drivers/ieee1394/csr1212.c index 3c044fc523..9f95337139 100644 --- a/drivers/ieee1394/csr1212.c +++ b/drivers/ieee1394/csr1212.c @@ -34,6 +34,7 @@ */ #include +#include #include #include #include @@ -113,10 +114,7 @@ static u16 csr1212_crc16(const u32 *buffer, size_t length) return cpu_to_be16(crc); } -#if 0 -/* Microsoft computes the CRC with the bytes in reverse order. Therefore we - * have a special version of the CRC algorithm to account for their buggy - * software. */ +/* Microsoft computes the CRC with the bytes in reverse order. */ static u16 csr1212_msft_crc16(const u32 *buffer, size_t length) { int shift; @@ -135,7 +133,6 @@ static u16 csr1212_msft_crc16(const u32 *buffer, size_t length) return cpu_to_be16(crc); } -#endif static struct csr1212_dentry * csr1212_find_keyval(struct csr1212_keyval *dir, struct csr1212_keyval *kv) @@ -221,12 +218,10 @@ static struct csr1212_keyval *csr1212_new_keyval(u8 type, u8 key) if (!kv) return NULL; + atomic_set(&kv->refcnt, 1); kv->key.type = type; kv->key.id = key; - kv->associate = NULL; - kv->refcnt = 1; - kv->next = NULL; kv->prev = NULL; kv->offset = 0; @@ -329,12 +324,13 @@ void csr1212_associate_keyval(struct csr1212_keyval *kv, if (kv->associate) csr1212_release_keyval(kv->associate); - associate->refcnt++; + csr1212_keep_keyval(associate); kv->associate = associate; } -int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, - struct csr1212_keyval *kv) +static int __csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, + struct csr1212_keyval *kv, + bool keep_keyval) { struct csr1212_dentry *dentry; @@ -344,10 +340,10 @@ int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, if (!dentry) return -ENOMEM; + if (keep_keyval) + csr1212_keep_keyval(kv); dentry->kv = kv; - kv->refcnt++; - dentry->next = NULL; dentry->prev = dir->value.directory.dentries_tail; @@ -361,6 +357,12 @@ int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, return CSR1212_SUCCESS; } +int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, + struct csr1212_keyval *kv) +{ + return __csr1212_attach_keyval_to_directory(dir, kv, true); +} + #define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \ (&((kv)->value.leaf.data[1])) @@ -486,15 +488,18 @@ void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir, /* This function is used to free the memory taken by a keyval. If the given * keyval is a directory type, then any keyvals contained in that directory - * will be destroyed as well if their respective refcnts are 0. By means of + * will be destroyed as well if noone holds a reference on them. By means of * list manipulation, this routine will descend a directory structure in a * non-recursive manner. */ -static void csr1212_destroy_keyval(struct csr1212_keyval *kv) +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; @@ -506,9 +511,8 @@ static void csr1212_destroy_keyval(struct csr1212_keyval *kv) k = head->kv; while (k) { - k->refcnt--; - - if (k->refcnt > 0) + /* must not dec_and_test kv->refcnt again */ + if (k != kv && !atomic_dec_and_test(&k->refcnt)) break; a = k->associate; @@ -539,14 +543,6 @@ static void csr1212_destroy_keyval(struct csr1212_keyval *kv) } } -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; @@ -1053,6 +1049,24 @@ int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, u32 len) return -ENOENT; } +/* + * Apparently there are many different wrong implementations of the CRC + * algorithm. We don't fail, we just warn... approximately once per GUID. + */ +static void +csr1212_check_crc(const u32 *buffer, size_t length, u16 crc, __be32 *guid) +{ + static u64 last_bad_eui64; + u64 eui64 = ((u64)be32_to_cpu(guid[0]) << 32) | be32_to_cpu(guid[1]); + + if (csr1212_crc16(buffer, length) == crc || + csr1212_msft_crc16(buffer, length) == crc || + eui64 == last_bad_eui64) + return; + + printk(KERN_DEBUG "ieee1394: config ROM CRC error\n"); + last_bad_eui64 = eui64; +} /* Parse a chunk of data as a Config ROM */ @@ -1096,13 +1110,8 @@ static int csr1212_parse_bus_info_block(struct csr1212_csr *csr) return ret; } -#if 0 - /* 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 -EINVAL; -#endif + csr1212_check_crc(bi->data, bi->crc_length, bi->crc, + &csr->bus_info_data[3]); cr = CSR1212_MALLOC(sizeof(*cr)); if (!cr) @@ -1131,6 +1140,7 @@ csr1212_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: @@ -1138,10 +1148,10 @@ csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos) CSR1212_KV_VAL(ki)); if (!k) { ret = -ENOMEM; - goto fail; + goto out; } - - k->refcnt = 0; /* Don't keep local reference when parsing. */ + /* Don't keep local reference when parsing. */ + keep_keyval = false; break; case CSR1212_KV_TYPE_CSR_OFFSET: @@ -1149,9 +1159,10 @@ csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos) CSR1212_KV_VAL(ki)); if (!k) { ret = -ENOMEM; - goto fail; + goto out; } - k->refcnt = 0; /* Don't keep local reference when parsing. */ + /* Don't keep local reference when parsing. */ + keep_keyval = false; break; default: @@ -1162,7 +1173,7 @@ csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos) * or Directories. The Config ROM image is most likely * messed up, so we'll just abort here. */ ret = -EIO; - goto fail; + goto out; } k = csr1212_find_keyval_offset(dir, offset); @@ -1177,10 +1188,12 @@ csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos) if (!k) { ret = -ENOMEM; - goto fail; + goto out; } - k->refcnt = 0; /* Don't keep local reference when parsing. */ - k->valid = 0; /* Contents not read yet so it's not valid. */ + /* 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; @@ -1188,9 +1201,8 @@ csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos) dir->next->prev = k; dir->next = k; } - ret = csr1212_attach_keyval_to_directory(dir, k); - -fail: + ret = __csr1212_attach_keyval_to_directory(dir, k, keep_keyval); +out: if (ret != CSR1212_SUCCESS && k != NULL) free_keyval(k); return ret; @@ -1208,15 +1220,8 @@ int csr1212_parse_keyval(struct csr1212_keyval *kv, &cache->data[bytes_to_quads(kv->offset - cache->offset)]; kvi_len = be16_to_cpu(kvi->length); -#if 0 - /* 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 = -EINVAL; - goto fail; - } -#endif + /* GUID is wrong in here in case of extended ROM. We don't care. */ + csr1212_check_crc(kvi->data, kvi_len, kvi->crc, &cache->data[3]); switch (kv->key.type) { case CSR1212_KV_TYPE_DIRECTORY: @@ -1241,7 +1246,7 @@ int csr1212_parse_keyval(struct csr1212_keyval *kv, kv->value.leaf.data = CSR1212_MALLOC(size); if (!kv->value.leaf.data) { ret = -ENOMEM; - goto fail; + goto out; } kv->value.leaf.len = kvi_len; @@ -1251,8 +1256,7 @@ int csr1212_parse_keyval(struct csr1212_keyval *kv, } kv->valid = 1; - -fail: +out: return ret; }