Skip to content

Commit

Permalink
firewire: add fw_csr_string() helper function
Browse files Browse the repository at this point in the history
The core (sysfs attributes), the firedtv driver, and possible future
drivers all read strings from some configuration ROM directory.  Factor
out the generic code from show_text_leaf() into a new helper function,
modified slightly to handle arbitrary buffer sizes.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
  • Loading branch information
Clemens Ladisch authored and Stefan Richter committed Dec 29, 2009
1 parent 5d7db04 commit 1f8fef7
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 67 deletions.
110 changes: 77 additions & 33 deletions drivers/firewire/core-device.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,67 @@ int fw_csr_iterator_next(struct fw_csr_iterator *ci, int *key, int *value)
}
EXPORT_SYMBOL(fw_csr_iterator_next);

static u32 *search_leaf(u32 *directory, int search_key)
{
struct fw_csr_iterator ci;
int last_key = 0, key, value;

fw_csr_iterator_init(&ci, directory);
while (fw_csr_iterator_next(&ci, &key, &value)) {
if (last_key == search_key &&
key == (CSR_DESCRIPTOR | CSR_LEAF))
return ci.p - 1 + value;
last_key = key;
}
return NULL;
}

static int textual_leaf_to_string(u32 *block, char *buf, size_t size)
{
unsigned int quadlets, length;

if (!size || !buf)
return -EINVAL;

quadlets = min(block[0] >> 16, 256u);
if (quadlets < 2)
return -ENODATA;

if (block[1] != 0 || block[2] != 0)
/* unknown language/character set */
return -ENODATA;

block += 3;
quadlets -= 2;
for (length = 0; length < quadlets * 4 && length + 1 < size; length++) {
char c = block[length / 4] >> (24 - 8 * (length % 4));
if (c == '\0')
break;
buf[length] = c;
}
buf[length] = '\0';
return length;
}

/**
* fw_csr_string - reads a string from the configuration ROM
* @directory: device or unit directory;
* fw_device->config_rom+5 or fw_unit->directory
* @key: the key of the preceding directory entry
* @buf: where to put the string
* @size: size of @buf, in bytes
*
* Returns string length (>= 0) or error code (< 0).
*/
int fw_csr_string(u32 *directory, int key, char *buf, size_t size)
{
u32 *leaf = search_leaf(directory, key);
if (!leaf)
return -ENOENT;
return textual_leaf_to_string(leaf, buf, size);
}
EXPORT_SYMBOL(fw_csr_string);

static bool is_fw_unit(struct device *dev);

static int match_unit_directory(u32 *directory, u32 match_flags,
Expand Down Expand Up @@ -226,10 +287,10 @@ static ssize_t show_text_leaf(struct device *dev,
{
struct config_rom_attribute *attr =
container_of(dattr, struct config_rom_attribute, attr);
struct fw_csr_iterator ci;
u32 *dir, *block = NULL, *p, *end;
int length, key, value, last_key = 0, ret = -ENOENT;
char *b;
u32 *dir;
size_t bufsize;
char dummy_buf[2];
int ret;

down_read(&fw_device_rwsem);

Expand All @@ -238,40 +299,23 @@ static ssize_t show_text_leaf(struct device *dev,
else
dir = fw_device(dev)->config_rom + 5;

fw_csr_iterator_init(&ci, dir);
while (fw_csr_iterator_next(&ci, &key, &value)) {
if (attr->key == last_key &&
key == (CSR_DESCRIPTOR | CSR_LEAF))
block = ci.p - 1 + value;
last_key = key;
if (buf) {
bufsize = PAGE_SIZE - 1;
} else {
buf = dummy_buf;
bufsize = 1;
}

if (block == NULL)
goto out;

length = min(block[0] >> 16, 256U);
if (length < 3)
goto out;

if (block[1] != 0 || block[2] != 0)
/* Unknown encoding. */
goto out;
ret = fw_csr_string(dir, attr->key, buf, bufsize);

if (buf == NULL) {
ret = length * 4;
goto out;
if (ret >= 0) {
/* Strip trailing whitespace and add newline. */
while (ret > 0 && isspace(buf[ret - 1]))
ret--;
strcpy(buf + ret, "\n");
ret++;
}

b = buf;
end = &block[length + 1];
for (p = &block[3]; p < end; p++, b += 4)
* (u32 *) b = (__force u32) __cpu_to_be32(*p);

/* Strip trailing whitespace and add newline. */
while (b--, (isspace(*b) || *b == '\0') && b > buf);
strcpy(b + 1, "\n");
ret = b + 2 - buf;
out:
up_read(&fw_device_rwsem);

return ret;
Expand Down
39 changes: 5 additions & 34 deletions drivers/media/dvb/firewire/firedtv-fw.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,47 +239,18 @@ static const struct fw_address_region fcp_region = {
};

/* Adjust the template string if models with longer names appear. */
#define MAX_MODEL_NAME_LEN ((int)DIV_ROUND_UP(sizeof("FireDTV ????"), 4))

static size_t model_name(u32 *directory, __be32 *buffer)
{
struct fw_csr_iterator ci;
int i, length, key, value, last_key = 0;
u32 *block = NULL;

fw_csr_iterator_init(&ci, directory);
while (fw_csr_iterator_next(&ci, &key, &value)) {
if (last_key == CSR_MODEL &&
key == (CSR_DESCRIPTOR | CSR_LEAF))
block = ci.p - 1 + value;
last_key = key;
}

if (block == NULL)
return 0;

length = min((int)(block[0] >> 16) - 2, MAX_MODEL_NAME_LEN);
if (length <= 0)
return 0;

/* fast-forward to text string */
block += 3;

for (i = 0; i < length; i++)
buffer[i] = cpu_to_be32(block[i]);

return length * 4;
}
#define MAX_MODEL_NAME_LEN sizeof("FireDTV ????")

static int node_probe(struct device *dev)
{
struct firedtv *fdtv;
__be32 name[MAX_MODEL_NAME_LEN];
char name[MAX_MODEL_NAME_LEN];
int name_len, err;

name_len = model_name(fw_unit(dev)->directory, name);
name_len = fw_csr_string(fw_unit(dev)->directory, CSR_MODEL,
name, sizeof(name));

fdtv = fdtv_alloc(dev, &backend, (char *)name, name_len);
fdtv = fdtv_alloc(dev, &backend, name, name_len >= 0 ? name_len : 0);
if (!fdtv)
return -ENOMEM;

Expand Down
2 changes: 2 additions & 0 deletions include/linux/firewire.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ struct fw_csr_iterator {
void fw_csr_iterator_init(struct fw_csr_iterator *ci, u32 *p);
int fw_csr_iterator_next(struct fw_csr_iterator *ci, int *key, int *value);

int fw_csr_string(u32 *directory, int key, char *buf, size_t size);

extern struct bus_type fw_bus_type;

struct fw_card_driver;
Expand Down

0 comments on commit 1f8fef7

Please sign in to comment.