aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/scsi_devinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/scsi_devinfo.c')
-rw-r--r--drivers/scsi/scsi_devinfo.c248
1 files changed, 226 insertions, 22 deletions
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index b13481369642..93c2622cb969 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -24,6 +24,13 @@ struct scsi_dev_info_list {
24 unsigned compatible; /* for use with scsi_static_device_list entries */ 24 unsigned compatible; /* for use with scsi_static_device_list entries */
25}; 25};
26 26
27struct scsi_dev_info_list_table {
28 struct list_head node; /* our node for being on the master list */
29 struct list_head scsi_dev_info_list; /* head of dev info list */
30 const char *name; /* name of list for /proc (NULL for global) */
31 int key; /* unique numeric identifier */
32};
33
27 34
28static const char spaces[] = " "; /* 16 of them */ 35static const char spaces[] = " "; /* 16 of them */
29static unsigned scsi_default_dev_flags; 36static unsigned scsi_default_dev_flags;
@@ -225,6 +232,7 @@ static struct {
225 {"SGI", "Universal Xport", "*", BLIST_NO_ULD_ATTACH}, 232 {"SGI", "Universal Xport", "*", BLIST_NO_ULD_ATTACH},
226 {"IBM", "Universal Xport", "*", BLIST_NO_ULD_ATTACH}, 233 {"IBM", "Universal Xport", "*", BLIST_NO_ULD_ATTACH},
227 {"SUN", "Universal Xport", "*", BLIST_NO_ULD_ATTACH}, 234 {"SUN", "Universal Xport", "*", BLIST_NO_ULD_ATTACH},
235 {"DELL", "Universal Xport", "*", BLIST_NO_ULD_ATTACH},
228 {"SMSC", "USB 2 HS-CF", NULL, BLIST_SPARSELUN | BLIST_INQUIRY_36}, 236 {"SMSC", "USB 2 HS-CF", NULL, BLIST_SPARSELUN | BLIST_INQUIRY_36},
229 {"SONY", "CD-ROM CDU-8001", NULL, BLIST_BORKEN}, 237 {"SONY", "CD-ROM CDU-8001", NULL, BLIST_BORKEN},
230 {"SONY", "TSL", NULL, BLIST_FORCELUN}, /* DDS3 & DDS4 autoloaders */ 238 {"SONY", "TSL", NULL, BLIST_FORCELUN}, /* DDS3 & DDS4 autoloaders */
@@ -246,6 +254,22 @@ static struct {
246 { NULL, NULL, NULL, 0 }, 254 { NULL, NULL, NULL, 0 },
247}; 255};
248 256
257static struct scsi_dev_info_list_table *scsi_devinfo_lookup_by_key(int key)
258{
259 struct scsi_dev_info_list_table *devinfo_table;
260 int found = 0;
261
262 list_for_each_entry(devinfo_table, &scsi_dev_info_list, node)
263 if (devinfo_table->key == key) {
264 found = 1;
265 break;
266 }
267 if (!found)
268 return ERR_PTR(-EINVAL);
269
270 return devinfo_table;
271}
272
249/* 273/*
250 * scsi_strcpy_devinfo: called from scsi_dev_info_list_add to copy into 274 * scsi_strcpy_devinfo: called from scsi_dev_info_list_add to copy into
251 * devinfo vendor and model strings. 275 * devinfo vendor and model strings.
@@ -295,7 +319,38 @@ static void scsi_strcpy_devinfo(char *name, char *to, size_t to_length,
295static int scsi_dev_info_list_add(int compatible, char *vendor, char *model, 319static int scsi_dev_info_list_add(int compatible, char *vendor, char *model,
296 char *strflags, int flags) 320 char *strflags, int flags)
297{ 321{
322 return scsi_dev_info_list_add_keyed(compatible, vendor, model,
323 strflags, flags,
324 SCSI_DEVINFO_GLOBAL);
325}
326
327/**
328 * scsi_dev_info_list_add_keyed - add one dev_info list entry.
329 * @compatible: if true, null terminate short strings. Otherwise space pad.
330 * @vendor: vendor string
331 * @model: model (product) string
332 * @strflags: integer string
333 * @flags: if strflags NULL, use this flag value
334 * @key: specify list to use
335 *
336 * Description:
337 * Create and add one dev_info entry for @vendor, @model,
338 * @strflags or @flag in list specified by @key. If @compatible,
339 * add to the tail of the list, do not space pad, and set
340 * devinfo->compatible. The scsi_static_device_list entries are
341 * added with @compatible 1 and @clfags NULL.
342 *
343 * Returns: 0 OK, -error on failure.
344 **/
345int scsi_dev_info_list_add_keyed(int compatible, char *vendor, char *model,
346 char *strflags, int flags, int key)
347{
298 struct scsi_dev_info_list *devinfo; 348 struct scsi_dev_info_list *devinfo;
349 struct scsi_dev_info_list_table *devinfo_table =
350 scsi_devinfo_lookup_by_key(key);
351
352 if (IS_ERR(devinfo_table))
353 return PTR_ERR(devinfo_table);
299 354
300 devinfo = kmalloc(sizeof(*devinfo), GFP_KERNEL); 355 devinfo = kmalloc(sizeof(*devinfo), GFP_KERNEL);
301 if (!devinfo) { 356 if (!devinfo) {
@@ -316,12 +371,15 @@ static int scsi_dev_info_list_add(int compatible, char *vendor, char *model,
316 devinfo->compatible = compatible; 371 devinfo->compatible = compatible;
317 372
318 if (compatible) 373 if (compatible)
319 list_add_tail(&devinfo->dev_info_list, &scsi_dev_info_list); 374 list_add_tail(&devinfo->dev_info_list,
375 &devinfo_table->scsi_dev_info_list);
320 else 376 else
321 list_add(&devinfo->dev_info_list, &scsi_dev_info_list); 377 list_add(&devinfo->dev_info_list,
378 &devinfo_table->scsi_dev_info_list);
322 379
323 return 0; 380 return 0;
324} 381}
382EXPORT_SYMBOL(scsi_dev_info_list_add_keyed);
325 383
326/** 384/**
327 * scsi_dev_info_list_add_str - parse dev_list and add to the scsi_dev_info_list. 385 * scsi_dev_info_list_add_str - parse dev_list and add to the scsi_dev_info_list.
@@ -381,22 +439,48 @@ static int scsi_dev_info_list_add_str(char *dev_list)
381 * @model: model name 439 * @model: model name
382 * 440 *
383 * Description: 441 * Description:
384 * Search the scsi_dev_info_list for an entry matching @vendor and 442 * Search the global scsi_dev_info_list (specified by list zero)
385 * @model, if found, return the matching flags value, else return 443 * for an entry matching @vendor and @model, if found, return the
386 * the host or global default settings. Called during scan time. 444 * matching flags value, else return the host or global default
445 * settings. Called during scan time.
387 **/ 446 **/
388int scsi_get_device_flags(struct scsi_device *sdev, 447int scsi_get_device_flags(struct scsi_device *sdev,
389 const unsigned char *vendor, 448 const unsigned char *vendor,
390 const unsigned char *model) 449 const unsigned char *model)
391{ 450{
451 return scsi_get_device_flags_keyed(sdev, vendor, model,
452 SCSI_DEVINFO_GLOBAL);
453}
454
455
456/**
457 * get_device_flags_keyed - get device specific flags from the dynamic device list.
458 * @sdev: &scsi_device to get flags for
459 * @vendor: vendor name
460 * @model: model name
461 * @key: list to look up
462 *
463 * Description:
464 * Search the scsi_dev_info_list specified by @key for an entry
465 * matching @vendor and @model, if found, return the matching
466 * flags value, else return the host or global default settings.
467 * Called during scan time.
468 **/
469int scsi_get_device_flags_keyed(struct scsi_device *sdev,
470 const unsigned char *vendor,
471 const unsigned char *model,
472 int key)
473{
392 struct scsi_dev_info_list *devinfo; 474 struct scsi_dev_info_list *devinfo;
393 unsigned int bflags; 475 struct scsi_dev_info_list_table *devinfo_table;
476
477 devinfo_table = scsi_devinfo_lookup_by_key(key);
394 478
395 bflags = sdev->sdev_bflags; 479 if (IS_ERR(devinfo_table))
396 if (!bflags) 480 return PTR_ERR(devinfo_table);
397 bflags = scsi_default_dev_flags;
398 481
399 list_for_each_entry(devinfo, &scsi_dev_info_list, dev_info_list) { 482 list_for_each_entry(devinfo, &devinfo_table->scsi_dev_info_list,
483 dev_info_list) {
400 if (devinfo->compatible) { 484 if (devinfo->compatible) {
401 /* 485 /*
402 * Behave like the older version of get_device_flags. 486 * Behave like the older version of get_device_flags.
@@ -446,32 +530,89 @@ int scsi_get_device_flags(struct scsi_device *sdev,
446 return devinfo->flags; 530 return devinfo->flags;
447 } 531 }
448 } 532 }
449 return bflags; 533 /* nothing found, return nothing */
534 if (key != SCSI_DEVINFO_GLOBAL)
535 return 0;
536
537 /* except for the global list, where we have an exception */
538 if (sdev->sdev_bflags)
539 return sdev->sdev_bflags;
540
541 return scsi_default_dev_flags;
450} 542}
543EXPORT_SYMBOL(scsi_get_device_flags_keyed);
451 544
452#ifdef CONFIG_SCSI_PROC_FS 545#ifdef CONFIG_SCSI_PROC_FS
546struct double_list {
547 struct list_head *top;
548 struct list_head *bottom;
549};
550
453static int devinfo_seq_show(struct seq_file *m, void *v) 551static int devinfo_seq_show(struct seq_file *m, void *v)
454{ 552{
553 struct double_list *dl = v;
554 struct scsi_dev_info_list_table *devinfo_table =
555 list_entry(dl->top, struct scsi_dev_info_list_table, node);
455 struct scsi_dev_info_list *devinfo = 556 struct scsi_dev_info_list *devinfo =
456 list_entry(v, struct scsi_dev_info_list, dev_info_list); 557 list_entry(dl->bottom, struct scsi_dev_info_list,
558 dev_info_list);
559
560 if (devinfo_table->scsi_dev_info_list.next == dl->bottom &&
561 devinfo_table->name)
562 seq_printf(m, "[%s]:\n", devinfo_table->name);
457 563
458 seq_printf(m, "'%.8s' '%.16s' 0x%x\n", 564 seq_printf(m, "'%.8s' '%.16s' 0x%x\n",
459 devinfo->vendor, devinfo->model, devinfo->flags); 565 devinfo->vendor, devinfo->model, devinfo->flags);
460 return 0; 566 return 0;
461} 567}
462 568
463static void * devinfo_seq_start(struct seq_file *m, loff_t *pos) 569static void *devinfo_seq_start(struct seq_file *m, loff_t *ppos)
464{ 570{
465 return seq_list_start(&scsi_dev_info_list, *pos); 571 struct double_list *dl = kmalloc(sizeof(*dl), GFP_KERNEL);
572 loff_t pos = *ppos;
573
574 if (!dl)
575 return NULL;
576
577 list_for_each(dl->top, &scsi_dev_info_list) {
578 struct scsi_dev_info_list_table *devinfo_table =
579 list_entry(dl->top, struct scsi_dev_info_list_table,
580 node);
581 list_for_each(dl->bottom, &devinfo_table->scsi_dev_info_list)
582 if (pos-- == 0)
583 return dl;
584 }
585
586 kfree(dl);
587 return NULL;
466} 588}
467 589
468static void * devinfo_seq_next(struct seq_file *m, void *v, loff_t *pos) 590static void *devinfo_seq_next(struct seq_file *m, void *v, loff_t *ppos)
469{ 591{
470 return seq_list_next(v, &scsi_dev_info_list, pos); 592 struct double_list *dl = v;
593 struct scsi_dev_info_list_table *devinfo_table =
594 list_entry(dl->top, struct scsi_dev_info_list_table, node);
595
596 ++*ppos;
597 dl->bottom = dl->bottom->next;
598 while (&devinfo_table->scsi_dev_info_list == dl->bottom) {
599 dl->top = dl->top->next;
600 if (dl->top == &scsi_dev_info_list) {
601 kfree(dl);
602 return NULL;
603 }
604 devinfo_table = list_entry(dl->top,
605 struct scsi_dev_info_list_table,
606 node);
607 dl->bottom = devinfo_table->scsi_dev_info_list.next;
608 }
609
610 return dl;
471} 611}
472 612
473static void devinfo_seq_stop(struct seq_file *m, void *v) 613static void devinfo_seq_stop(struct seq_file *m, void *v)
474{ 614{
615 kfree(v);
475} 616}
476 617
477static const struct seq_operations scsi_devinfo_seq_ops = { 618static const struct seq_operations scsi_devinfo_seq_ops = {
@@ -548,19 +689,78 @@ MODULE_PARM_DESC(default_dev_flags,
548 **/ 689 **/
549void scsi_exit_devinfo(void) 690void scsi_exit_devinfo(void)
550{ 691{
551 struct list_head *lh, *lh_next;
552 struct scsi_dev_info_list *devinfo;
553
554#ifdef CONFIG_SCSI_PROC_FS 692#ifdef CONFIG_SCSI_PROC_FS
555 remove_proc_entry("scsi/device_info", NULL); 693 remove_proc_entry("scsi/device_info", NULL);
556#endif 694#endif
557 695
558 list_for_each_safe(lh, lh_next, &scsi_dev_info_list) { 696 scsi_dev_info_remove_list(SCSI_DEVINFO_GLOBAL);
697}
698
699/**
700 * scsi_dev_info_add_list - add a new devinfo list
701 * @key: key of the list to add
702 * @name: Name of the list to add (for /proc/scsi/device_info)
703 *
704 * Adds the requested list, returns zero on success, -EEXIST if the
705 * key is already registered to a list, or other error on failure.
706 */
707int scsi_dev_info_add_list(int key, const char *name)
708{
709 struct scsi_dev_info_list_table *devinfo_table =
710 scsi_devinfo_lookup_by_key(key);
711
712 if (!IS_ERR(devinfo_table))
713 /* list already exists */
714 return -EEXIST;
715
716 devinfo_table = kmalloc(sizeof(*devinfo_table), GFP_KERNEL);
717
718 if (!devinfo_table)
719 return -ENOMEM;
720
721 INIT_LIST_HEAD(&devinfo_table->node);
722 INIT_LIST_HEAD(&devinfo_table->scsi_dev_info_list);
723 devinfo_table->name = name;
724 devinfo_table->key = key;
725 list_add_tail(&devinfo_table->node, &scsi_dev_info_list);
726
727 return 0;
728}
729EXPORT_SYMBOL(scsi_dev_info_add_list);
730
731/**
732 * scsi_dev_info_remove_list - destroy an added devinfo list
733 * @key: key of the list to destroy
734 *
735 * Iterates over the entire list first, freeing all the values, then
736 * frees the list itself. Returns 0 on success or -EINVAL if the key
737 * can't be found.
738 */
739int scsi_dev_info_remove_list(int key)
740{
741 struct list_head *lh, *lh_next;
742 struct scsi_dev_info_list_table *devinfo_table =
743 scsi_devinfo_lookup_by_key(key);
744
745 if (IS_ERR(devinfo_table))
746 /* no such list */
747 return -EINVAL;
748
749 /* remove from the master list */
750 list_del(&devinfo_table->node);
751
752 list_for_each_safe(lh, lh_next, &devinfo_table->scsi_dev_info_list) {
753 struct scsi_dev_info_list *devinfo;
754
559 devinfo = list_entry(lh, struct scsi_dev_info_list, 755 devinfo = list_entry(lh, struct scsi_dev_info_list,
560 dev_info_list); 756 dev_info_list);
561 kfree(devinfo); 757 kfree(devinfo);
562 } 758 }
759 kfree(devinfo_table);
760
761 return 0;
563} 762}
763EXPORT_SYMBOL(scsi_dev_info_remove_list);
564 764
565/** 765/**
566 * scsi_init_devinfo - set up the dynamic device list. 766 * scsi_init_devinfo - set up the dynamic device list.
@@ -576,10 +776,14 @@ int __init scsi_init_devinfo(void)
576#endif 776#endif
577 int error, i; 777 int error, i;
578 778
579 error = scsi_dev_info_list_add_str(scsi_dev_flags); 779 error = scsi_dev_info_add_list(SCSI_DEVINFO_GLOBAL, NULL);
580 if (error) 780 if (error)
581 return error; 781 return error;
582 782
783 error = scsi_dev_info_list_add_str(scsi_dev_flags);
784 if (error)
785 goto out;
786
583 for (i = 0; scsi_static_device_list[i].vendor; i++) { 787 for (i = 0; scsi_static_device_list[i].vendor; i++) {
584 error = scsi_dev_info_list_add(1 /* compatibile */, 788 error = scsi_dev_info_list_add(1 /* compatibile */,
585 scsi_static_device_list[i].vendor, 789 scsi_static_device_list[i].vendor,