diff options
| author | Clemens Ladisch <clemens@ladisch.de> | 2009-12-24 05:59:57 -0500 |
|---|---|---|
| committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2009-12-29 13:58:17 -0500 |
| commit | 1f8fef7b3388b5a976e80839679b5bae581a1091 (patch) | |
| tree | 40ce4828cee287d6e54f627df787f8d993a21b1e /drivers/firewire | |
| parent | 5d7db0499e5bb13381a7fbfdd0d913b966545e75 (diff) | |
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: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire')
| -rw-r--r-- | drivers/firewire/core-device.c | 110 |
1 files changed, 77 insertions, 33 deletions
diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index 9d0dfcbe2c1c..a39e4344cd58 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c | |||
| @@ -59,6 +59,67 @@ int fw_csr_iterator_next(struct fw_csr_iterator *ci, int *key, int *value) | |||
| 59 | } | 59 | } |
| 60 | EXPORT_SYMBOL(fw_csr_iterator_next); | 60 | EXPORT_SYMBOL(fw_csr_iterator_next); |
| 61 | 61 | ||
| 62 | static u32 *search_leaf(u32 *directory, int search_key) | ||
| 63 | { | ||
| 64 | struct fw_csr_iterator ci; | ||
| 65 | int last_key = 0, key, value; | ||
| 66 | |||
| 67 | fw_csr_iterator_init(&ci, directory); | ||
| 68 | while (fw_csr_iterator_next(&ci, &key, &value)) { | ||
| 69 | if (last_key == search_key && | ||
| 70 | key == (CSR_DESCRIPTOR | CSR_LEAF)) | ||
| 71 | return ci.p - 1 + value; | ||
| 72 | last_key = key; | ||
| 73 | } | ||
| 74 | return NULL; | ||
| 75 | } | ||
| 76 | |||
| 77 | static int textual_leaf_to_string(u32 *block, char *buf, size_t size) | ||
| 78 | { | ||
| 79 | unsigned int quadlets, length; | ||
| 80 | |||
| 81 | if (!size || !buf) | ||
| 82 | return -EINVAL; | ||
| 83 | |||
| 84 | quadlets = min(block[0] >> 16, 256u); | ||
| 85 | if (quadlets < 2) | ||
| 86 | return -ENODATA; | ||
| 87 | |||
| 88 | if (block[1] != 0 || block[2] != 0) | ||
| 89 | /* unknown language/character set */ | ||
| 90 | return -ENODATA; | ||
| 91 | |||
| 92 | block += 3; | ||
| 93 | quadlets -= 2; | ||
| 94 | for (length = 0; length < quadlets * 4 && length + 1 < size; length++) { | ||
| 95 | char c = block[length / 4] >> (24 - 8 * (length % 4)); | ||
| 96 | if (c == '\0') | ||
| 97 | break; | ||
| 98 | buf[length] = c; | ||
| 99 | } | ||
| 100 | buf[length] = '\0'; | ||
| 101 | return length; | ||
| 102 | } | ||
| 103 | |||
| 104 | /** | ||
| 105 | * fw_csr_string - reads a string from the configuration ROM | ||
| 106 | * @directory: device or unit directory; | ||
| 107 | * fw_device->config_rom+5 or fw_unit->directory | ||
| 108 | * @key: the key of the preceding directory entry | ||
| 109 | * @buf: where to put the string | ||
| 110 | * @size: size of @buf, in bytes | ||
| 111 | * | ||
| 112 | * Returns string length (>= 0) or error code (< 0). | ||
| 113 | */ | ||
| 114 | int fw_csr_string(u32 *directory, int key, char *buf, size_t size) | ||
| 115 | { | ||
| 116 | u32 *leaf = search_leaf(directory, key); | ||
| 117 | if (!leaf) | ||
| 118 | return -ENOENT; | ||
| 119 | return textual_leaf_to_string(leaf, buf, size); | ||
| 120 | } | ||
| 121 | EXPORT_SYMBOL(fw_csr_string); | ||
| 122 | |||
| 62 | static bool is_fw_unit(struct device *dev); | 123 | static bool is_fw_unit(struct device *dev); |
| 63 | 124 | ||
| 64 | static int match_unit_directory(u32 *directory, u32 match_flags, | 125 | static int match_unit_directory(u32 *directory, u32 match_flags, |
| @@ -226,10 +287,10 @@ static ssize_t show_text_leaf(struct device *dev, | |||
| 226 | { | 287 | { |
| 227 | struct config_rom_attribute *attr = | 288 | struct config_rom_attribute *attr = |
| 228 | container_of(dattr, struct config_rom_attribute, attr); | 289 | container_of(dattr, struct config_rom_attribute, attr); |
| 229 | struct fw_csr_iterator ci; | 290 | u32 *dir; |
| 230 | u32 *dir, *block = NULL, *p, *end; | 291 | size_t bufsize; |
| 231 | int length, key, value, last_key = 0, ret = -ENOENT; | 292 | char dummy_buf[2]; |
| 232 | char *b; | 293 | int ret; |
| 233 | 294 | ||
| 234 | down_read(&fw_device_rwsem); | 295 | down_read(&fw_device_rwsem); |
| 235 | 296 | ||
| @@ -238,40 +299,23 @@ static ssize_t show_text_leaf(struct device *dev, | |||
| 238 | else | 299 | else |
| 239 | dir = fw_device(dev)->config_rom + 5; | 300 | dir = fw_device(dev)->config_rom + 5; |
| 240 | 301 | ||
| 241 | fw_csr_iterator_init(&ci, dir); | 302 | if (buf) { |
| 242 | while (fw_csr_iterator_next(&ci, &key, &value)) { | 303 | bufsize = PAGE_SIZE - 1; |
| 243 | if (attr->key == last_key && | 304 | } else { |
| 244 | key == (CSR_DESCRIPTOR | CSR_LEAF)) | 305 | buf = dummy_buf; |
| 245 | block = ci.p - 1 + value; | 306 | bufsize = 1; |
| 246 | last_key = key; | ||
| 247 | } | 307 | } |
| 248 | 308 | ||
| 249 | if (block == NULL) | 309 | ret = fw_csr_string(dir, attr->key, buf, bufsize); |
| 250 | goto out; | ||
| 251 | |||
| 252 | length = min(block[0] >> 16, 256U); | ||
| 253 | if (length < 3) | ||
| 254 | goto out; | ||
| 255 | |||
| 256 | if (block[1] != 0 || block[2] != 0) | ||
| 257 | /* Unknown encoding. */ | ||
| 258 | goto out; | ||
| 259 | 310 | ||
| 260 | if (buf == NULL) { | 311 | if (ret >= 0) { |
| 261 | ret = length * 4; | 312 | /* Strip trailing whitespace and add newline. */ |
| 262 | goto out; | 313 | while (ret > 0 && isspace(buf[ret - 1])) |
| 314 | ret--; | ||
| 315 | strcpy(buf + ret, "\n"); | ||
| 316 | ret++; | ||
| 263 | } | 317 | } |
| 264 | 318 | ||
| 265 | b = buf; | ||
| 266 | end = &block[length + 1]; | ||
| 267 | for (p = &block[3]; p < end; p++, b += 4) | ||
| 268 | * (u32 *) b = (__force u32) __cpu_to_be32(*p); | ||
| 269 | |||
| 270 | /* Strip trailing whitespace and add newline. */ | ||
| 271 | while (b--, (isspace(*b) || *b == '\0') && b > buf); | ||
| 272 | strcpy(b + 1, "\n"); | ||
| 273 | ret = b + 2 - buf; | ||
| 274 | out: | ||
| 275 | up_read(&fw_device_rwsem); | 319 | up_read(&fw_device_rwsem); |
| 276 | 320 | ||
| 277 | return ret; | 321 | return ret; |
