Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1f8fef7b authored by Clemens Ladisch's avatar Clemens Ladisch Committed by Stefan Richter
Browse files

firewire: add fw_csr_string() helper function



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: default avatarClemens Ladisch <clemens@ladisch.de>
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent 5d7db049
Loading
Loading
Loading
Loading
+77 −33
Original line number Diff line number Diff line
@@ -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,
@@ -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);

@@ -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;
+5 −34
Original line number Diff line number Diff line
@@ -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;

+2 −0
Original line number Diff line number Diff line
@@ -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;