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; |