*/
#include <linux/errno.h>
+#include <linux/kernel.h>
#include <linux/string.h>
#include <asm/bug.h>
#include <asm/byteorder.h>
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;
return cpu_to_be16(crc);
}
-#endif
static struct csr1212_dentry *
csr1212_find_keyval(struct csr1212_keyval *dir, struct csr1212_keyval *kv)
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;
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;
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;
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]))
CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, dtype);
CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, specifier_id);
- if (data) {
+ if (data)
memcpy(CSR1212_DESCRIPTOR_LEAF_DATA(kv), data, data_len);
- }
-
- return kv;
-}
-
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, width) \
- ((kv)->value.leaf.data[1] = \
- ((kv)->value.leaf.data[1] & \
- cpu_to_be32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK << \
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT))) | \
- cpu_to_be32(((width) & CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK) << \
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT))
-
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, char_set) \
- ((kv)->value.leaf.data[1] = \
- ((kv)->value.leaf.data[1] & \
- cpu_to_be32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK << \
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT))) | \
- cpu_to_be32(((char_set) & \
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) << \
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT))
-
-#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language) \
- ((kv)->value.leaf.data[1] = \
- ((kv)->value.leaf.data[1] & \
- cpu_to_be32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK))) | \
- cpu_to_be32(((language) & \
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK)))
-
-static struct csr1212_keyval *
-csr1212_new_textual_descriptor_leaf(u8 cwidth, u16 cset, u16 language,
- const void *data, size_t data_len)
-{
- struct csr1212_keyval *kv;
- char *lstr;
-
- kv = csr1212_new_descriptor_leaf(0, 0, NULL, data_len +
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD);
- if (!kv)
- return NULL;
-
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, cwidth);
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, cset);
- CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language);
-
- lstr = (char*)CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv);
-
- /* make sure last quadlet is zeroed out */
- *((u32*)&(lstr[(data_len - 1) & ~0x3])) = 0;
-
- /* don't copy the NUL terminator */
- memcpy(lstr, data, data_len);
return kv;
}
+/* Check if string conforms to minimal ASCII as per IEEE 1212 clause 7.4 */
static int csr1212_check_minimal_ascii(const char *s)
{
static const char minimal_ascii_table[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
- 0x00, 0x00, 0x0a, 0x00, 0x0C, 0x0D, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x21, 0x22, 0x00, 0x00, 0x25, 0x26, 0x27,
- 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
- 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
- 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
- 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
- 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
- 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
- 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x5f,
- 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
- 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
- 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
- 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 1 2 4 8 16 32 64 128 */
+ 128, /* --, --, --, --, --, --, --, 07, */
+ 4 + 16 + 32, /* --, --, 0a, --, 0C, 0D, --, --, */
+ 0, /* --, --, --, --, --, --, --, --, */
+ 0, /* --, --, --, --, --, --, --, --, */
+ 255 - 8 - 16, /* 20, 21, 22, --, --, 25, 26, 27, */
+ 255, /* 28, 29, 2a, 2b, 2c, 2d, 2e, 2f, */
+ 255, /* 30, 31, 32, 33, 34, 35, 36, 37, */
+ 255, /* 38, 39, 3a, 3b, 3c, 3d, 3e, 3f, */
+ 255, /* 40, 41, 42, 43, 44, 45, 46, 47, */
+ 255, /* 48, 49, 4a, 4b, 4c, 4d, 4e, 4f, */
+ 255, /* 50, 51, 52, 53, 54, 55, 56, 57, */
+ 1 + 2 + 4 + 128, /* 58, 59, 5a, --, --, --, --, 5f, */
+ 255 - 1, /* --, 61, 62, 63, 64, 65, 66, 67, */
+ 255, /* 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, */
+ 255, /* 70, 71, 72, 73, 74, 75, 76, 77, */
+ 1 + 2 + 4, /* 78, 79, 7a, --, --, --, --, --, */
};
+ int i, j;
+
for (; *s; s++) {
- if (minimal_ascii_table[*s & 0x7F] != *s)
- return -1; /* failed */
+ i = *s >> 3; /* i = *s / 8; */
+ j = 1 << (*s & 3); /* j = 1 << (*s % 8); */
+
+ if (i >= ARRAY_SIZE(minimal_ascii_table) ||
+ !(minimal_ascii_table[i] & j))
+ return -EINVAL;
}
- /* String conforms to minimal-ascii, as specified by IEEE 1212,
- * par. 7.4 */
return 0;
}
+/* IEEE 1212 clause 7.5.4.1 textual descriptors (English, minimal ASCII) */
struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s)
{
- /* Check if string conform to minimal_ascii format */
- if (csr1212_check_minimal_ascii(s))
+ struct csr1212_keyval *kv;
+ u32 *text;
+ size_t str_len, quads;
+
+ if (!s || !*s || csr1212_check_minimal_ascii(s))
+ return NULL;
+
+ str_len = strlen(s);
+ quads = bytes_to_quads(str_len);
+ kv = csr1212_new_descriptor_leaf(0, 0, NULL, quads_to_bytes(quads) +
+ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD);
+ if (!kv)
return NULL;
- /* IEEE 1212, par. 7.5.4.1 Textual descriptors (minimal ASCII) */
- return csr1212_new_textual_descriptor_leaf(0, 0, 0, s, strlen(s));
+ kv->value.leaf.data[1] = 0; /* width, character_set, language */
+ text = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv);
+ text[quads - 1] = 0; /* padding */
+ memcpy(text, s, str_len);
+
+ return kv;
}
/* 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;
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;
}
}
-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;
default:
/* Should never get here */
+ WARN_ON(1);
break;
}
break;
default:
/* Should never get here */
- break; /* GDB breakpoint */
+ WARN_ON(1);
+ break;
}
value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) <<
case CSR1212_KV_TYPE_IMMEDIATE:
case CSR1212_KV_TYPE_CSR_OFFSET:
/* Should never get here */
- break; /* GDB breakpoint */
+ WARN_ON(1);
+ break;
case CSR1212_KV_TYPE_LEAF:
/* Don't copy over Extended ROM areas, they are
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 */
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)
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:
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:
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:
* 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);
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;
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;
&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:
kv->value.leaf.data = CSR1212_MALLOC(size);
if (!kv->value.leaf.data) {
ret = -ENOMEM;
- goto fail;
+ goto out;
}
kv->value.leaf.len = kvi_len;
}
kv->valid = 1;
-
-fail:
+out:
return ret;
}