aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire/fw-device.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firewire/fw-device.c')
-rw-r--r--drivers/firewire/fw-device.c151
1 files changed, 147 insertions, 4 deletions
diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c
index 59451f524fc3..71bb9d1e8196 100644
--- a/drivers/firewire/fw-device.c
+++ b/drivers/firewire/fw-device.c
@@ -28,6 +28,7 @@
28#include <linux/idr.h> 28#include <linux/idr.h>
29#include <linux/rwsem.h> 29#include <linux/rwsem.h>
30#include <asm/semaphore.h> 30#include <asm/semaphore.h>
31#include <linux/ctype.h>
31#include "fw-transaction.h" 32#include "fw-transaction.h"
32#include "fw-topology.h" 33#include "fw-topology.h"
33#include "fw-device.h" 34#include "fw-device.h"
@@ -193,6 +194,129 @@ int fw_device_enable_phys_dma(struct fw_device *device)
193} 194}
194EXPORT_SYMBOL(fw_device_enable_phys_dma); 195EXPORT_SYMBOL(fw_device_enable_phys_dma);
195 196
197struct config_rom_attribute {
198 struct device_attribute attr;
199 u32 key;
200};
201
202static ssize_t
203show_immediate(struct device *dev, struct device_attribute *dattr, char *buf)
204{
205 struct config_rom_attribute *attr =
206 container_of(dattr, struct config_rom_attribute, attr);
207 struct fw_csr_iterator ci;
208 u32 *dir;
209 int key, value;
210
211 if (is_fw_unit(dev))
212 dir = fw_unit(dev)->directory;
213 else
214 dir = fw_device(dev)->config_rom + 5;
215
216 fw_csr_iterator_init(&ci, dir);
217 while (fw_csr_iterator_next(&ci, &key, &value))
218 if (attr->key == key)
219 return snprintf(buf, buf ? PAGE_SIZE : 0,
220 "0x%06x\n", value);
221
222 return -ENOENT;
223}
224
225#define IMMEDIATE_ATTR(name, key) \
226 { __ATTR(name, S_IRUGO, show_immediate, NULL), key }
227
228static ssize_t
229show_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf)
230{
231 struct config_rom_attribute *attr =
232 container_of(dattr, struct config_rom_attribute, attr);
233 struct fw_csr_iterator ci;
234 u32 *dir, *block = NULL, *p, *end;
235 int length, key, value, last_key = 0;
236 char *b;
237
238 if (is_fw_unit(dev))
239 dir = fw_unit(dev)->directory;
240 else
241 dir = fw_device(dev)->config_rom + 5;
242
243 fw_csr_iterator_init(&ci, dir);
244 while (fw_csr_iterator_next(&ci, &key, &value)) {
245 if (attr->key == last_key &&
246 key == (CSR_DESCRIPTOR | CSR_LEAF))
247 block = ci.p - 1 + value;
248 last_key = key;
249 }
250
251 if (block == NULL)
252 return -ENOENT;
253
254 length = min(block[0] >> 16, 256U);
255 if (length < 3)
256 return -ENOENT;
257
258 if (block[1] != 0 || block[2] != 0)
259 /* Unknown encoding. */
260 return -ENOENT;
261
262 if (buf == NULL)
263 return length * 4;
264
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
274 return b + 2 - buf;
275}
276
277#define TEXT_LEAF_ATTR(name, key) \
278 { __ATTR(name, S_IRUGO, show_text_leaf, NULL), key }
279
280static struct config_rom_attribute config_rom_attributes[] = {
281 IMMEDIATE_ATTR(vendor, CSR_VENDOR),
282 IMMEDIATE_ATTR(hardware_version, CSR_HARDWARE_VERSION),
283 IMMEDIATE_ATTR(specifier_id, CSR_SPECIFIER_ID),
284 IMMEDIATE_ATTR(version, CSR_VERSION),
285 IMMEDIATE_ATTR(model, CSR_MODEL),
286 TEXT_LEAF_ATTR(vendor_name, CSR_VENDOR),
287 TEXT_LEAF_ATTR(model_name, CSR_MODEL),
288 TEXT_LEAF_ATTR(hardware_version_name, CSR_HARDWARE_VERSION),
289};
290
291static void
292remove_config_rom_attributes(struct device *dev)
293{
294 int i;
295
296 for (i = 0; i < ARRAY_SIZE(config_rom_attributes); i++)
297 device_remove_file(dev, &config_rom_attributes[i].attr);
298}
299
300static int
301add_config_rom_attributes(struct device *dev)
302{
303 struct device_attribute *attr;
304 int i, err = 0;
305
306 for (i = 0; i < ARRAY_SIZE(config_rom_attributes); i++) {
307 attr = &config_rom_attributes[i].attr;
308 if (attr->show(dev, attr, NULL) < 0)
309 continue;
310 err = device_create_file(dev, attr);
311 if (err < 0) {
312 remove_config_rom_attributes(dev);
313 break;
314 }
315 }
316
317 return err;
318}
319
196static ssize_t 320static ssize_t
197modalias_show(struct device *dev, 321modalias_show(struct device *dev,
198 struct device_attribute *attr, char *buf) 322 struct device_attribute *attr, char *buf)
@@ -399,15 +523,26 @@ static void create_units(struct fw_device *device)
399 snprintf(unit->device.bus_id, sizeof unit->device.bus_id, 523 snprintf(unit->device.bus_id, sizeof unit->device.bus_id,
400 "%s.%d", device->device.bus_id, i++); 524 "%s.%d", device->device.bus_id, i++);
401 525
402 if (device_register(&unit->device) < 0) { 526 if (device_register(&unit->device) < 0)
403 kfree(unit); 527 goto skip_unit;
404 continue; 528
405 } 529 if (add_config_rom_attributes(&unit->device) < 0)
530 goto skip_unregister;
531
532 continue;
533
534 skip_unregister:
535 device_unregister(&unit->device);
536 skip_unit:
537 kfree(unit);
406 } 538 }
407} 539}
408 540
409static int shutdown_unit(struct device *device, void *data) 541static int shutdown_unit(struct device *device, void *data)
410{ 542{
543 struct fw_unit *unit = fw_unit(device);
544
545 remove_config_rom_attributes(&unit->device);
411 device_unregister(device); 546 device_unregister(device);
412 547
413 return 0; 548 return 0;
@@ -437,6 +572,8 @@ static void fw_device_shutdown(struct work_struct *work)
437 idr_remove(&fw_device_idr, minor); 572 idr_remove(&fw_device_idr, minor);
438 up_write(&fw_bus_type.subsys.rwsem); 573 up_write(&fw_bus_type.subsys.rwsem);
439 574
575 remove_config_rom_attributes(&device->device);
576
440 fw_device_cdev_remove(device); 577 fw_device_cdev_remove(device);
441 device_for_each_child(&device->device, NULL, shutdown_unit); 578 device_for_each_child(&device->device, NULL, shutdown_unit);
442 device_unregister(&device->device); 579 device_unregister(&device->device);
@@ -504,6 +641,10 @@ static void fw_device_init(struct work_struct *work)
504 goto error_with_cdev; 641 goto error_with_cdev;
505 } 642 }
506 643
644 err = add_config_rom_attributes(&device->device);
645 if (err < 0)
646 goto error_with_register;
647
507 create_units(device); 648 create_units(device);
508 649
509 /* Transition the device to running state. If it got pulled 650 /* Transition the device to running state. If it got pulled
@@ -530,6 +671,8 @@ static void fw_device_init(struct work_struct *work)
530 671
531 return; 672 return;
532 673
674 error_with_register:
675 device_unregister(&device->device);
533 error_with_cdev: 676 error_with_cdev:
534 down_write(&fw_bus_type.subsys.rwsem); 677 down_write(&fw_bus_type.subsys.rwsem);
535 idr_remove(&fw_device_idr, minor); 678 idr_remove(&fw_device_idr, minor);