aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2009-12-24 05:59:57 -0500
committerStefan Richter <stefanr@s5r6.in-berlin.de>2009-12-29 13:58:17 -0500
commit1f8fef7b3388b5a976e80839679b5bae581a1091 (patch)
tree40ce4828cee287d6e54f627df787f8d993a21b1e /drivers/firewire
parent5d7db0499e5bb13381a7fbfdd0d913b966545e75 (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.c110
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}
60EXPORT_SYMBOL(fw_csr_iterator_next); 60EXPORT_SYMBOL(fw_csr_iterator_next);
61 61
62static 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
77static 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 */
114int 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}
121EXPORT_SYMBOL(fw_csr_string);
122
62static bool is_fw_unit(struct device *dev); 123static bool is_fw_unit(struct device *dev);
63 124
64static int match_unit_directory(u32 *directory, u32 match_flags, 125static 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;