aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2007-08-23 13:04:28 -0400
committerJaroslav Kysela <perex@perex.cz>2007-10-16 09:59:26 -0400
commitdfe495d0a51e20325b51760f34a2f53bfe1f3b52 (patch)
tree7345b903c8c22734ae249ab339aa9cd1b8723941 /sound
parent889c43955115ea7412d71335e3ceff6bad118dce (diff)
[ALSA] hda-codec - Fix Dell laptops support with STAC codecs
Fixed Dell laptops support with STAC92xx codecs. Many pin-config models are introduced. See ALSA-Configuration.txt for details. The patch taken from ALSA bug#3319, originally by Jorg Prante: https://bugtrack.alsa-project.org/alsa-bug/view.php?id=3319 Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound')
-rw-r--r--sound/pci/hda/patch_sigmatel.c345
1 files changed, 313 insertions, 32 deletions
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 76ec32a375c0..adca2854e50b 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -39,11 +39,22 @@
39 39
40enum { 40enum {
41 STAC_REF, 41 STAC_REF,
42 STAC_9200_DELL_D21,
43 STAC_9200_DELL_D22,
44 STAC_9200_DELL_D23,
45 STAC_9200_DELL_M21,
46 STAC_9200_DELL_M22,
47 STAC_9200_DELL_M23,
48 STAC_9200_DELL_M24,
49 STAC_9200_DELL_M25,
50 STAC_9200_DELL_M26,
51 STAC_9200_DELL_M27,
42 STAC_9200_MODELS 52 STAC_9200_MODELS
43}; 53};
44 54
45enum { 55enum {
46 STAC_9205_REF, 56 STAC_9205_REF,
57 STAC_9205_DELL_M42,
47 STAC_9205_DELL_M43, 58 STAC_9205_DELL_M43,
48 STAC_9205_DELL_M44, 59 STAC_9205_DELL_M44,
49 STAC_9205_M43xx, 60 STAC_9205_M43xx,
@@ -62,19 +73,22 @@ enum {
62 STAC_D945_REF, 73 STAC_D945_REF,
63 STAC_D945GTP3, 74 STAC_D945GTP3,
64 STAC_D945GTP5, 75 STAC_D945GTP5,
65 STAC_922X_DELL,
66 STAC_INTEL_MAC_V1, 76 STAC_INTEL_MAC_V1,
67 STAC_INTEL_MAC_V2, 77 STAC_INTEL_MAC_V2,
68 STAC_INTEL_MAC_V3, 78 STAC_INTEL_MAC_V3,
69 STAC_INTEL_MAC_V4, 79 STAC_INTEL_MAC_V4,
70 STAC_INTEL_MAC_V5, 80 STAC_INTEL_MAC_V5,
71 /* for backward compitability */ 81 /* for backward compatibility */
72 STAC_MACMINI, 82 STAC_MACMINI,
73 STAC_MACBOOK, 83 STAC_MACBOOK,
74 STAC_MACBOOK_PRO_V1, 84 STAC_MACBOOK_PRO_V1,
75 STAC_MACBOOK_PRO_V2, 85 STAC_MACBOOK_PRO_V2,
76 STAC_IMAC_INTEL, 86 STAC_IMAC_INTEL,
77 STAC_IMAC_INTEL_20, 87 STAC_IMAC_INTEL_20,
88 STAC_922X_DELL_D81,
89 STAC_922X_DELL_D82,
90 STAC_922X_DELL_M81,
91 STAC_922X_DELL_M82,
78 STAC_922X_MODELS 92 STAC_922X_MODELS
79}; 93};
80 94
@@ -456,12 +470,144 @@ static unsigned int ref9200_pin_configs[8] = {
456 0x02a19020, 0x01a19021, 0x90100140, 0x01813122, 470 0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
457}; 471};
458 472
473/*
474 STAC 9200 pin configs for
475 102801A8
476 102801DE
477 102801E8
478*/
479static unsigned int dell9200_d21_pin_configs[8] = {
480 0x400001f0, 0x400001f1, 0x01a19021, 0x90100140,
481 0x01813122, 0x02214030, 0x01014010, 0x02a19020,
482};
483
484/*
485 STAC 9200 pin configs for
486 102801C0
487 102801C1
488*/
489static unsigned int dell9200_d22_pin_configs[8] = {
490 0x400001f0, 0x400001f1, 0x02a19021, 0x90100140,
491 0x400001f2, 0x0221401f, 0x01014010, 0x01813020,
492};
493
494/*
495 STAC 9200 pin configs for
496 102801C4 (Dell Dimension E310)
497 102801C5
498 102801C7
499 102801D9
500 102801DA
501 102801E3
502*/
503static unsigned int dell9200_d23_pin_configs[8] = {
504 0x400001f0, 0x400001f1, 0x01a19021, 0x90100140,
505 0x400001f2, 0x0221401f, 0x01014010, 0x01813020,
506};
507
508
509/*
510 STAC 9200-32 pin configs for
511 102801B5 (Dell Inspiron 630m)
512 102801D8 (Dell Inspiron 640m)
513*/
514static unsigned int dell9200_m21_pin_configs[8] = {
515 0x40c003fa, 0x03441340, 0x03a11020, 0x401003fc,
516 0x403003fd, 0x0321121f, 0x0321121f, 0x408003fb,
517};
518
519/*
520 STAC 9200-32 pin configs for
521 102801C2 (Dell Latitude D620)
522 102801C8
523 102801CC (Dell Latitude D820)
524 102801D4
525 102801D6
526*/
527static unsigned int dell9200_m22_pin_configs[8] = {
528 0x40c003fa, 0x0144131f, 0x03A11020, 0x401003fb,
529 0x40f000fc, 0x0321121f, 0x90170310, 0x90a70321,
530};
531
532/*
533 STAC 9200-32 pin configs for
534 102801CE (Dell XPS M1710)
535 102801CF (Dell Precision M90)
536*/
537static unsigned int dell9200_m23_pin_configs[8] = {
538 0x40c003fa, 0x01441340, 0x0421421f, 0x90170310,
539 0x408003fb, 0x04a1102e, 0x90170311, 0x403003fc,
540};
541
542/*
543 STAC 9200-32 pin configs for
544 102801C9
545 102801CA
546 102801CB (Dell Latitude 120L)
547 102801D3
548*/
549static unsigned int dell9200_m24_pin_configs[8] = {
550 0x40c003fa, 0x404003fb, 0x03a11020, 0x401003fd,
551 0x403003fe, 0x0321121f, 0x90170310, 0x408003fc,
552};
553
554/*
555 STAC 9200-32 pin configs for
556 102801BD (Dell Inspiron E1505n)
557 102801EE
558 102801EF
559*/
560static unsigned int dell9200_m25_pin_configs[8] = {
561 0x40c003fa, 0x01441340, 0x04a11020, 0x401003fc,
562 0x403003fd, 0x0421121f, 0x90170310, 0x408003fb,
563};
564
565/*
566 STAC 9200-32 pin configs for
567 102801F5 (Dell Inspiron 1501)
568 102801F6
569*/
570static unsigned int dell9200_m26_pin_configs[8] = {
571 0x40c003fa, 0x404003fb, 0x04a11020, 0x401003fd,
572 0x403003fe, 0x0421121f, 0x90170310, 0x408003fc,
573};
574
575/*
576 STAC 9200-32
577 102801CD (Dell Inspiron E1705/9400)
578*/
579static unsigned int dell9200_m27_pin_configs[8] = {
580 0x40c003fa, 0x01441340, 0x04a11020, 0x90170310,
581 0x40f003fc, 0x0421121f, 0x90170310, 0x408003fb,
582};
583
584
459static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = { 585static unsigned int *stac9200_brd_tbl[STAC_9200_MODELS] = {
460 [STAC_REF] = ref9200_pin_configs, 586 [STAC_REF] = ref9200_pin_configs,
587 [STAC_9200_DELL_D21] = dell9200_d21_pin_configs,
588 [STAC_9200_DELL_D22] = dell9200_d22_pin_configs,
589 [STAC_9200_DELL_D23] = dell9200_d23_pin_configs,
590 [STAC_9200_DELL_M21] = dell9200_m21_pin_configs,
591 [STAC_9200_DELL_M22] = dell9200_m22_pin_configs,
592 [STAC_9200_DELL_M23] = dell9200_m23_pin_configs,
593 [STAC_9200_DELL_M24] = dell9200_m24_pin_configs,
594 [STAC_9200_DELL_M25] = dell9200_m25_pin_configs,
595 [STAC_9200_DELL_M26] = dell9200_m26_pin_configs,
596 [STAC_9200_DELL_M27] = dell9200_m27_pin_configs,
461}; 597};
462 598
463static const char *stac9200_models[STAC_9200_MODELS] = { 599static const char *stac9200_models[STAC_9200_MODELS] = {
464 [STAC_REF] = "ref", 600 [STAC_REF] = "ref",
601 [STAC_9200_DELL_D21] = "dell-d21",
602 [STAC_9200_DELL_D22] = "dell-d22",
603 [STAC_9200_DELL_D23] = "dell-d23",
604 [STAC_9200_DELL_M21] = "dell-m21",
605 [STAC_9200_DELL_M22] = "dell-m22",
606 [STAC_9200_DELL_M23] = "dell-m23",
607 [STAC_9200_DELL_M24] = "dell-m24",
608 [STAC_9200_DELL_M25] = "dell-m25",
609 [STAC_9200_DELL_M26] = "dell-m26",
610 [STAC_9200_DELL_M27] = "dell-m27",
465}; 611};
466 612
467static struct snd_pci_quirk stac9200_cfg_tbl[] = { 613static struct snd_pci_quirk stac9200_cfg_tbl[] = {
@@ -469,27 +615,64 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = {
469 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, 615 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
470 "DFI LanParty", STAC_REF), 616 "DFI LanParty", STAC_REF),
471 /* Dell laptops have BIOS problem */ 617 /* Dell laptops have BIOS problem */
618 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a8,
619 "unknown Dell", STAC_9200_DELL_D21),
472 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01b5, 620 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01b5,
473 "Dell Inspiron 630m", STAC_REF), 621 "Dell Inspiron 630m", STAC_9200_DELL_M21),
622 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bd,
623 "Dell Inspiron E1505n", STAC_9200_DELL_M25),
624 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c0,
625 "unknown Dell", STAC_9200_DELL_D22),
626 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c1,
627 "unknown Dell", STAC_9200_DELL_D22),
474 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c2, 628 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c2,
475 "Dell Latitude D620", STAC_REF), 629 "Dell Latitude D620", STAC_9200_DELL_M22),
630 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c5,
631 "unknown Dell", STAC_9200_DELL_D23),
632 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c7,
633 "unknown Dell", STAC_9200_DELL_D23),
634 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c8,
635 "unknown Dell", STAC_9200_DELL_M22),
636 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01c9,
637 "unknown Dell", STAC_9200_DELL_M24),
638 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ca,
639 "unknown Dell", STAC_9200_DELL_M24),
476 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cb, 640 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cb,
477 "Dell Latitude 120L", STAC_REF), 641 "Dell Latitude 120L", STAC_9200_DELL_M24),
478 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cc, 642 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cc,
479 "Dell Latitude D820", STAC_REF), 643 "Dell Latitude D820", STAC_9200_DELL_M22),
480 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cd, 644 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cd,
481 "Dell Inspiron E1705/9400", STAC_REF), 645 "Dell Inspiron E1705/9400", STAC_9200_DELL_M27),
482 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ce, 646 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ce,
483 "Dell XPS M1710", STAC_REF), 647 "Dell XPS M1710", STAC_9200_DELL_M23),
484 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cf, 648 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01cf,
485 "Dell Precision M90", STAC_REF), 649 "Dell Precision M90", STAC_9200_DELL_M23),
650 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d3,
651 "unknown Dell", STAC_9200_DELL_M22),
652 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d4,
653 "unknown Dell", STAC_9200_DELL_M22),
486 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d6, 654 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d6,
487 "unknown Dell", STAC_REF), 655 "unknown Dell", STAC_9200_DELL_M22),
488 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d8, 656 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d8,
489 "Dell Inspiron 640m", STAC_REF), 657 "Dell Inspiron 640m", STAC_9200_DELL_M21),
658 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d9,
659 "unknown Dell", STAC_9200_DELL_D23),
660 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01da,
661 "unknown Dell", STAC_9200_DELL_D23),
662 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01de,
663 "unknown Dell", STAC_9200_DELL_D21),
664 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e3,
665 "unknown Dell", STAC_9200_DELL_D23),
666 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01e8,
667 "unknown Dell", STAC_9200_DELL_D21),
668 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ee,
669 "unknown Dell", STAC_9200_DELL_M25),
670 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ef,
671 "unknown Dell", STAC_9200_DELL_M25),
490 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f5, 672 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f5,
491 "Dell Inspiron 1501", STAC_REF), 673 "Dell Inspiron 1501", STAC_9200_DELL_M26),
492 674 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f6,
675 "unknown Dell", STAC_9200_DELL_M26),
493 /* Panasonic */ 676 /* Panasonic */
494 SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_REF), 677 SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-74", STAC_REF),
495 678
@@ -548,6 +731,51 @@ static unsigned int ref922x_pin_configs[10] = {
548 0x40000100, 0x40000100, 731 0x40000100, 0x40000100,
549}; 732};
550 733
734/*
735 STAC 922X pin configs for
736 102801A7
737 102801AB
738 102801A9
739 102801D1
740 102801D2
741*/
742static unsigned int dell_922x_d81_pin_configs[10] = {
743 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
744 0x02a19020, 0x01117011, 0x400001f0, 0x400001f1,
745 0x01813122, 0x400001f2,
746};
747
748/*
749 STAC 922X pin configs for
750 102801AC
751 102801D0
752*/
753static unsigned int dell_922x_d82_pin_configs[10] = {
754 0x02214030, 0x01a19021, 0x01111012, 0x01114010,
755 0x02a19020, 0x01117011, 0x01451140, 0x400001f0,
756 0x01813122, 0x400001f1,
757};
758
759/*
760 STAC 922X pin configs for
761 102801BF
762*/
763static unsigned int dell_922x_m81_pin_configs[10] = {
764 0x0321101f, 0x01112024, 0x01111222, 0x91174220,
765 0x03a11050, 0x01116221, 0x90a70330, 0x01452340,
766 0x40C003f1, 0x405003f0,
767};
768
769/*
770 STAC 9221 A1 pin configs for
771 102801D7 (Dell XPS M1210)
772*/
773static unsigned int dell_922x_m82_pin_configs[10] = {
774 0x0221121f, 0x408103ff, 0x02111212, 0x90100310,
775 0x408003f1, 0x02111211, 0x03451340, 0x40c003f2,
776 0x508003f3, 0x405003f4,
777};
778
551static unsigned int d945gtp3_pin_configs[10] = { 779static unsigned int d945gtp3_pin_configs[10] = {
552 0x0221401f, 0x01a19022, 0x01813021, 0x01014010, 780 0x0221401f, 0x01a19022, 0x01813021, 0x01014010,
553 0x40000100, 0x40000100, 0x40000100, 0x40000100, 781 0x40000100, 0x40000100, 0x40000100, 0x40000100,
@@ -590,48 +818,49 @@ static unsigned int intel_mac_v5_pin_configs[10] = {
590 0x400000fc, 0x400000fb, 818 0x400000fc, 0x400000fb,
591}; 819};
592 820
593static unsigned int stac922x_dell_pin_configs[10] = {
594 0x0221121e, 0x408103ff, 0x02a1123e, 0x90100310,
595 0x408003f1, 0x0221122f, 0x03451340, 0x40c003f2,
596 0x50a003f3, 0x405003f4
597};
598 821
599static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = { 822static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
600 [STAC_D945_REF] = ref922x_pin_configs, 823 [STAC_D945_REF] = ref922x_pin_configs,
601 [STAC_D945GTP3] = d945gtp3_pin_configs, 824 [STAC_D945GTP3] = d945gtp3_pin_configs,
602 [STAC_D945GTP5] = d945gtp5_pin_configs, 825 [STAC_D945GTP5] = d945gtp5_pin_configs,
603 [STAC_922X_DELL] = stac922x_dell_pin_configs,
604 [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs, 826 [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs,
605 [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs, 827 [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs,
606 [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs, 828 [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs,
607 [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs, 829 [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs,
608 [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs, 830 [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs,
609 /* for backward compitability */ 831 /* for backward compatibility */
610 [STAC_MACMINI] = intel_mac_v3_pin_configs, 832 [STAC_MACMINI] = intel_mac_v3_pin_configs,
611 [STAC_MACBOOK] = intel_mac_v5_pin_configs, 833 [STAC_MACBOOK] = intel_mac_v5_pin_configs,
612 [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs, 834 [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs,
613 [STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs, 835 [STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs,
614 [STAC_IMAC_INTEL] = intel_mac_v2_pin_configs, 836 [STAC_IMAC_INTEL] = intel_mac_v2_pin_configs,
615 [STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs, 837 [STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs,
838 [STAC_922X_DELL_D81] = dell_922x_d81_pin_configs,
839 [STAC_922X_DELL_D82] = dell_922x_d82_pin_configs,
840 [STAC_922X_DELL_M81] = dell_922x_m81_pin_configs,
841 [STAC_922X_DELL_M82] = dell_922x_m82_pin_configs,
616}; 842};
617 843
618static const char *stac922x_models[STAC_922X_MODELS] = { 844static const char *stac922x_models[STAC_922X_MODELS] = {
619 [STAC_D945_REF] = "ref", 845 [STAC_D945_REF] = "ref",
620 [STAC_D945GTP5] = "5stack", 846 [STAC_D945GTP5] = "5stack",
621 [STAC_D945GTP3] = "3stack", 847 [STAC_D945GTP3] = "3stack",
622 [STAC_922X_DELL] = "dell",
623 [STAC_INTEL_MAC_V1] = "intel-mac-v1", 848 [STAC_INTEL_MAC_V1] = "intel-mac-v1",
624 [STAC_INTEL_MAC_V2] = "intel-mac-v2", 849 [STAC_INTEL_MAC_V2] = "intel-mac-v2",
625 [STAC_INTEL_MAC_V3] = "intel-mac-v3", 850 [STAC_INTEL_MAC_V3] = "intel-mac-v3",
626 [STAC_INTEL_MAC_V4] = "intel-mac-v4", 851 [STAC_INTEL_MAC_V4] = "intel-mac-v4",
627 [STAC_INTEL_MAC_V5] = "intel-mac-v5", 852 [STAC_INTEL_MAC_V5] = "intel-mac-v5",
628 /* for backward compitability */ 853 /* for backward compatibility */
629 [STAC_MACMINI] = "macmini", 854 [STAC_MACMINI] = "macmini",
630 [STAC_MACBOOK] = "macbook", 855 [STAC_MACBOOK] = "macbook",
631 [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1", 856 [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1",
632 [STAC_MACBOOK_PRO_V2] = "macbook-pro", 857 [STAC_MACBOOK_PRO_V2] = "macbook-pro",
633 [STAC_IMAC_INTEL] = "imac-intel", 858 [STAC_IMAC_INTEL] = "imac-intel",
634 [STAC_IMAC_INTEL_20] = "imac-intel-20", 859 [STAC_IMAC_INTEL_20] = "imac-intel-20",
860 [STAC_922X_DELL_D81] = "dell-d81",
861 [STAC_922X_DELL_D82] = "dell-d82",
862 [STAC_922X_DELL_M81] = "dell-m81",
863 [STAC_922X_DELL_M82] = "dell-m82",
635}; 864};
636 865
637static struct snd_pci_quirk stac922x_cfg_tbl[] = { 866static struct snd_pci_quirk stac922x_cfg_tbl[] = {
@@ -695,9 +924,25 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
695 /* Apple Mac Mini (early 2006) */ 924 /* Apple Mac Mini (early 2006) */
696 SND_PCI_QUIRK(0x8384, 0x7680, 925 SND_PCI_QUIRK(0x8384, 0x7680,
697 "Mac Mini", STAC_INTEL_MAC_V3), 926 "Mac Mini", STAC_INTEL_MAC_V3),
698 /* Dell */ 927 /* Dell systems */
699 SND_PCI_QUIRK(0x1028, 0x01d7, "Dell XPS M1210", STAC_922X_DELL), 928 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a7,
700 929 "unknown Dell", STAC_922X_DELL_D81),
930 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01a9,
931 "unknown Dell", STAC_922X_DELL_D81),
932 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ab,
933 "unknown Dell", STAC_922X_DELL_D81),
934 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ac,
935 "unknown Dell", STAC_922X_DELL_D82),
936 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01bf,
937 "unknown Dell", STAC_922X_DELL_M81),
938 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d0,
939 "unknown Dell", STAC_922X_DELL_D82),
940 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d1,
941 "unknown Dell", STAC_922X_DELL_D81),
942 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d2,
943 "unknown Dell", STAC_922X_DELL_D81),
944 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01d7,
945 "Dell XPS M1210", STAC_922X_DELL_M82),
701 {} /* terminator */ 946 {} /* terminator */
702}; 947};
703 948
@@ -768,7 +1013,7 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = {
768 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST), 1013 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST),
769 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST), 1014 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST),
770 /* Dell 3 stack systems */ 1015 /* Dell 3 stack systems */
771 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell E520", STAC_DELL_3ST), 1016 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell Dimension E520", STAC_DELL_3ST),
772 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ed, "Dell ", STAC_DELL_3ST), 1017 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ed, "Dell ", STAC_DELL_3ST),
773 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f4, "Dell ", STAC_DELL_3ST), 1018 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f4, "Dell ", STAC_DELL_3ST),
774 /* 965 based 5 stack systems */ 1019 /* 965 based 5 stack systems */
@@ -790,28 +1035,54 @@ static unsigned int ref9205_pin_configs[12] = {
790 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030 1035 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030
791}; 1036};
792 1037
793static unsigned int dell_m43_9205_pin_configs[12] = { 1038/*
1039 STAC 9205 pin configs for
1040 102801F1
1041 102801F2
1042 102801FC
1043 102801FD
1044 10280204
1045 1028021F
1046*/
1047static unsigned int dell_9205_m42_pin_configs[12] = {
1048 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310,
1049 0x400003FB, 0x400003FC, 0x400003FD, 0x40F000F9,
1050 0x90A60330, 0x400003FF, 0x0144131F, 0x40C003FE,
1051};
1052
1053/*
1054 STAC 9205 pin configs for
1055 102801F9
1056 102801FA
1057 102801FE
1058 102801FF (Dell Precision M4300)
1059 10280206
1060 10280200
1061 10280201
1062*/
1063static unsigned int dell_9205_m43_pin_configs[12] = {
794 0x0321101f, 0x03a11020, 0x90a70330, 0x90170310, 1064 0x0321101f, 0x03a11020, 0x90a70330, 0x90170310,
795 0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9, 1065 0x400000fe, 0x400000ff, 0x400000fd, 0x40f000f9,
796 0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8, 1066 0x400000fa, 0x400000fc, 0x0144131f, 0x40c003f8,
797}; 1067};
798 1068
799static unsigned int dell_m44_9205_pin_configs[12] = { 1069static unsigned int dell_9205_m44_pin_configs[12] = {
800 0x0421101f, 0x04a11020, 0x400003fa, 0x90170310, 1070 0x0421101f, 0x04a11020, 0x400003fa, 0x90170310,
801 0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9, 1071 0x400003fb, 0x400003fc, 0x400003fd, 0x400003f9,
802 0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe, 1072 0x90a60330, 0x400003ff, 0x01441340, 0x40c003fe,
803}; 1073};
804 1074
805
806static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = { 1075static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
807 [STAC_9205_REF] = ref9205_pin_configs, 1076 [STAC_9205_REF] = ref9205_pin_configs,
808 [STAC_9205_DELL_M43] = dell_m43_9205_pin_configs, 1077 [STAC_9205_DELL_M42] = dell_9205_m42_pin_configs,
809 [STAC_9205_DELL_M44] = dell_m44_9205_pin_configs, 1078 [STAC_9205_DELL_M43] = dell_9205_m43_pin_configs,
1079 [STAC_9205_DELL_M44] = dell_9205_m44_pin_configs,
810 [STAC_9205_M43xx] = NULL, 1080 [STAC_9205_M43xx] = NULL,
811}; 1081};
812 1082
813static const char *stac9205_models[STAC_9205_MODELS] = { 1083static const char *stac9205_models[STAC_9205_MODELS] = {
814 [STAC_9205_REF] = "ref", 1084 [STAC_9205_REF] = "ref",
1085 [STAC_9205_DELL_M42] = "dell-m42",
815 [STAC_9205_DELL_M43] = "dell-m43", 1086 [STAC_9205_DELL_M43] = "dell-m43",
816 [STAC_9205_DELL_M44] = "dell-m44", 1087 [STAC_9205_DELL_M44] = "dell-m44",
817}; 1088};
@@ -820,16 +1091,24 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
820 /* SigmaTel reference board */ 1091 /* SigmaTel reference board */
821 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, 1092 SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
822 "DFI LanParty", STAC_9205_REF), 1093 "DFI LanParty", STAC_9205_REF),
1094 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
1095 "unknown Dell", STAC_9205_DELL_M42),
1096 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f2,
1097 "unknown Dell", STAC_9205_DELL_M42),
823 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8, 1098 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f8,
824 "Dell Precision", STAC_9205_M43xx), 1099 "Dell Precision", STAC_9205_M43xx),
825 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9, 1100 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f9,
826 "Dell Precision", STAC_9205_DELL_M43), 1101 "Dell Precision", STAC_9205_DELL_M43),
827 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa, 1102 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fa,
828 "Dell Precision", STAC_9205_DELL_M43), 1103 "Dell Precision", STAC_9205_DELL_M43),
1104 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fc,
1105 "unknown Dell", STAC_9205_DELL_M42),
1106 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
1107 "unknown Dell", STAC_9205_DELL_M42),
829 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fe, 1108 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fe,
830 "Dell Precision", STAC_9205_DELL_M43), 1109 "Dell Precision", STAC_9205_DELL_M43),
831 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff, 1110 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ff,
832 "Dell Precision", STAC_9205_DELL_M43), 1111 "Dell Precision M4300", STAC_9205_DELL_M43),
833 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206, 1112 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0206,
834 "Dell Precision", STAC_9205_DELL_M43), 1113 "Dell Precision", STAC_9205_DELL_M43),
835 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1, 1114 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f1,
@@ -840,6 +1119,8 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
840 "Dell Inspiron", STAC_9205_DELL_M44), 1119 "Dell Inspiron", STAC_9205_DELL_M44),
841 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd, 1120 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01fd,
842 "Dell Inspiron", STAC_9205_DELL_M44), 1121 "Dell Inspiron", STAC_9205_DELL_M44),
1122 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0204,
1123 "unknown Dell", STAC_9205_DELL_M42),
843 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f, 1124 SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f,
844 "Dell Inspiron", STAC_9205_DELL_M44), 1125 "Dell Inspiron", STAC_9205_DELL_M44),
845 {} /* terminator */ 1126 {} /* terminator */
>); static void sdebug_remove_adapter(void); static void sdebug_max_tgts_luns(void) { struct sdebug_host_info *sdbg_host; struct Scsi_Host *hpnt; spin_lock(&sdebug_host_list_lock); list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) { hpnt = sdbg_host->shost; if ((hpnt->this_id >= 0) && (scsi_debug_num_tgts > hpnt->this_id)) hpnt->max_id = scsi_debug_num_tgts + 1; else hpnt->max_id = scsi_debug_num_tgts; /* scsi_debug_max_luns; */ hpnt->max_lun = SAM2_WLUN_REPORT_LUNS; } spin_unlock(&sdebug_host_list_lock); } static void mk_sense_buffer(struct sdebug_dev_info *devip, int key, int asc, int asq) { unsigned char *sbuff; sbuff = devip->sense_buff; memset(sbuff, 0, SDEBUG_SENSE_LEN); scsi_build_sense_buffer(scsi_debug_dsense, sbuff, key, asc, asq); if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: [sense_key,asc,ascq]: " "[0x%x,0x%x,0x%x]\n", key, asc, asq); } static void get_data_transfer_info(unsigned char *cmd, unsigned long long *lba, unsigned int *num, u32 *ei_lba) { *ei_lba = 0; switch (*cmd) { case VARIABLE_LENGTH_CMD: *lba = (u64)cmd[19] | (u64)cmd[18] << 8 | (u64)cmd[17] << 16 | (u64)cmd[16] << 24 | (u64)cmd[15] << 32 | (u64)cmd[14] << 40 | (u64)cmd[13] << 48 | (u64)cmd[12] << 56; *ei_lba = (u32)cmd[23] | (u32)cmd[22] << 8 | (u32)cmd[21] << 16 | (u32)cmd[20] << 24; *num = (u32)cmd[31] | (u32)cmd[30] << 8 | (u32)cmd[29] << 16 | (u32)cmd[28] << 24; break; case WRITE_SAME_16: case WRITE_16: case READ_16: *lba = (u64)cmd[9] | (u64)cmd[8] << 8 | (u64)cmd[7] << 16 | (u64)cmd[6] << 24 | (u64)cmd[5] << 32 | (u64)cmd[4] << 40 | (u64)cmd[3] << 48 | (u64)cmd[2] << 56; *num = (u32)cmd[13] | (u32)cmd[12] << 8 | (u32)cmd[11] << 16 | (u32)cmd[10] << 24; break; case WRITE_12: case READ_12: *lba = (u32)cmd[5] | (u32)cmd[4] << 8 | (u32)cmd[3] << 16 | (u32)cmd[2] << 24; *num = (u32)cmd[9] | (u32)cmd[8] << 8 | (u32)cmd[7] << 16 | (u32)cmd[6] << 24; break; case WRITE_SAME: case WRITE_10: case READ_10: case XDWRITEREAD_10: *lba = (u32)cmd[5] | (u32)cmd[4] << 8 | (u32)cmd[3] << 16 | (u32)cmd[2] << 24; *num = (u32)cmd[8] | (u32)cmd[7] << 8; break; case WRITE_6: case READ_6: *lba = (u32)cmd[3] | (u32)cmd[2] << 8 | (u32)(cmd[1] & 0x1f) << 16; *num = (0 == cmd[4]) ? 256 : cmd[4]; break; default: break; } } static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg) { if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) { printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd); } return -EINVAL; /* return -ENOTTY; // correct return but upsets fdisk */ } static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only, struct sdebug_dev_info * devip) { if (devip->reset) { if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: Reporting Unit " "attention: power on reset\n"); devip->reset = 0; mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0); return check_condition_result; } if ((0 == reset_only) && devip->stopped) { if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: Reporting Not " "ready: initializing command required\n"); mk_sense_buffer(devip, NOT_READY, LOGICAL_UNIT_NOT_READY, 0x2); return check_condition_result; } return 0; } /* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */ static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr, int arr_len) { int act_len; struct scsi_data_buffer *sdb = scsi_in(scp); if (!sdb->length) return 0; if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE)) return (DID_ERROR << 16); act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents, arr, arr_len); if (sdb->resid) sdb->resid -= act_len; else sdb->resid = scsi_bufflen(scp) - act_len; return 0; } /* Returns number of bytes fetched into 'arr' or -1 if error. */ static int fetch_to_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr, int arr_len) { if (!scsi_bufflen(scp)) return 0; if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_TO_DEVICE)) return -1; return scsi_sg_copy_to_buffer(scp, arr, arr_len); } static const char * inq_vendor_id = "Linux "; static const char * inq_product_id = "scsi_debug "; static const char * inq_product_rev = "0004"; static int inquiry_evpd_83(unsigned char * arr, int port_group_id, int target_dev_id, int dev_id_num, const char * dev_id_str, int dev_id_str_len) { int num, port_a; char b[32]; port_a = target_dev_id + 1; /* T10 vendor identifier field format (faked) */ arr[0] = 0x2; /* ASCII */ arr[1] = 0x1; arr[2] = 0x0; memcpy(&arr[4], inq_vendor_id, 8); memcpy(&arr[12], inq_product_id, 16); memcpy(&arr[28], dev_id_str, dev_id_str_len); num = 8 + 16 + dev_id_str_len; arr[3] = num; num += 4; if (dev_id_num >= 0) { /* NAA-5, Logical unit identifier (binary) */ arr[num++] = 0x1; /* binary (not necessarily sas) */ arr[num++] = 0x3; /* PIV=0, lu, naa */ arr[num++] = 0x0; arr[num++] = 0x8; arr[num++] = 0x53; /* naa-5 ieee company id=0x333333 (fake) */ arr[num++] = 0x33; arr[num++] = 0x33; arr[num++] = 0x30; arr[num++] = (dev_id_num >> 24); arr[num++] = (dev_id_num >> 16) & 0xff; arr[num++] = (dev_id_num >> 8) & 0xff; arr[num++] = dev_id_num & 0xff; /* Target relative port number */ arr[num++] = 0x61; /* proto=sas, binary */ arr[num++] = 0x94; /* PIV=1, target port, rel port */ arr[num++] = 0x0; /* reserved */ arr[num++] = 0x4; /* length */ arr[num++] = 0x0; /* reserved */ arr[num++] = 0x0; /* reserved */ arr[num++] = 0x0; arr[num++] = 0x1; /* relative port A */ } /* NAA-5, Target port identifier */ arr[num++] = 0x61; /* proto=sas, binary */ arr[num++] = 0x93; /* piv=1, target port, naa */ arr[num++] = 0x0; arr[num++] = 0x8; arr[num++] = 0x52; /* naa-5, company id=0x222222 (fake) */ arr[num++] = 0x22; arr[num++] = 0x22; arr[num++] = 0x20; arr[num++] = (port_a >> 24); arr[num++] = (port_a >> 16) & 0xff; arr[num++] = (port_a >> 8) & 0xff; arr[num++] = port_a & 0xff; /* NAA-5, Target port group identifier */ arr[num++] = 0x61; /* proto=sas, binary */ arr[num++] = 0x95; /* piv=1, target port group id */ arr[num++] = 0x0; arr[num++] = 0x4; arr[num++] = 0; arr[num++] = 0; arr[num++] = (port_group_id >> 8) & 0xff; arr[num++] = port_group_id & 0xff; /* NAA-5, Target device identifier */ arr[num++] = 0x61; /* proto=sas, binary */ arr[num++] = 0xa3; /* piv=1, target device, naa */ arr[num++] = 0x0; arr[num++] = 0x8; arr[num++] = 0x52; /* naa-5, company id=0x222222 (fake) */ arr[num++] = 0x22; arr[num++] = 0x22; arr[num++] = 0x20; arr[num++] = (target_dev_id >> 24); arr[num++] = (target_dev_id >> 16) & 0xff; arr[num++] = (target_dev_id >> 8) & 0xff; arr[num++] = target_dev_id & 0xff; /* SCSI name string: Target device identifier */ arr[num++] = 0x63; /* proto=sas, UTF-8 */ arr[num++] = 0xa8; /* piv=1, target device, SCSI name string */ arr[num++] = 0x0; arr[num++] = 24; memcpy(arr + num, "naa.52222220", 12); num += 12; snprintf(b, sizeof(b), "%08X", target_dev_id); memcpy(arr + num, b, 8); num += 8; memset(arr + num, 0, 4); num += 4; return num; } static unsigned char vpd84_data[] = { /* from 4th byte */ 0x22,0x22,0x22,0x0,0xbb,0x0, 0x22,0x22,0x22,0x0,0xbb,0x1, 0x22,0x22,0x22,0x0,0xbb,0x2, }; static int inquiry_evpd_84(unsigned char * arr) { memcpy(arr, vpd84_data, sizeof(vpd84_data)); return sizeof(vpd84_data); } static int inquiry_evpd_85(unsigned char * arr) { int num = 0; const char * na1 = "https://www.kernel.org/config"; const char * na2 = "http://www.kernel.org/log"; int plen, olen; arr[num++] = 0x1; /* lu, storage config */ arr[num++] = 0x0; /* reserved */ arr[num++] = 0x0; olen = strlen(na1); plen = olen + 1; if (plen % 4) plen = ((plen / 4) + 1) * 4; arr[num++] = plen; /* length, null termianted, padded */ memcpy(arr + num, na1, olen); memset(arr + num + olen, 0, plen - olen); num += plen; arr[num++] = 0x4; /* lu, logging */ arr[num++] = 0x0; /* reserved */ arr[num++] = 0x0; olen = strlen(na2); plen = olen + 1; if (plen % 4) plen = ((plen / 4) + 1) * 4; arr[num++] = plen; /* length, null terminated, padded */ memcpy(arr + num, na2, olen); memset(arr + num + olen, 0, plen - olen); num += plen; return num; } /* SCSI ports VPD page */ static int inquiry_evpd_88(unsigned char * arr, int target_dev_id) { int num = 0; int port_a, port_b; port_a = target_dev_id + 1; port_b = port_a + 1; arr[num++] = 0x0; /* reserved */ arr[num++] = 0x0; /* reserved */ arr[num++] = 0x0; arr[num++] = 0x1; /* relative port 1 (primary) */ memset(arr + num, 0, 6); num += 6; arr[num++] = 0x0; arr[num++] = 12; /* length tp descriptor */ /* naa-5 target port identifier (A) */ arr[num++] = 0x61; /* proto=sas, binary */ arr[num++] = 0x93; /* PIV=1, target port, NAA */ arr[num++] = 0x0; /* reserved */ arr[num++] = 0x8; /* length */ arr[num++] = 0x52; /* NAA-5, company_id=0x222222 (fake) */ arr[num++] = 0x22; arr[num++] = 0x22; arr[num++] = 0x20; arr[num++] = (port_a >> 24); arr[num++] = (port_a >> 16) & 0xff; arr[num++] = (port_a >> 8) & 0xff; arr[num++] = port_a & 0xff; arr[num++] = 0x0; /* reserved */ arr[num++] = 0x0; /* reserved */ arr[num++] = 0x0; arr[num++] = 0x2; /* relative port 2 (secondary) */ memset(arr + num, 0, 6); num += 6; arr[num++] = 0x0; arr[num++] = 12; /* length tp descriptor */ /* naa-5 target port identifier (B) */ arr[num++] = 0x61; /* proto=sas, binary */ arr[num++] = 0x93; /* PIV=1, target port, NAA */ arr[num++] = 0x0; /* reserved */ arr[num++] = 0x8; /* length */ arr[num++] = 0x52; /* NAA-5, company_id=0x222222 (fake) */ arr[num++] = 0x22; arr[num++] = 0x22; arr[num++] = 0x20; arr[num++] = (port_b >> 24); arr[num++] = (port_b >> 16) & 0xff; arr[num++] = (port_b >> 8) & 0xff; arr[num++] = port_b & 0xff; return num; } static unsigned char vpd89_data[] = { /* from 4th byte */ 0,0,0,0, 'l','i','n','u','x',' ',' ',' ', 'S','A','T',' ','s','c','s','i','_','d','e','b','u','g',' ',' ', '1','2','3','4', 0x34,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 0xec,0,0,0, 0x5a,0xc,0xff,0x3f,0x37,0xc8,0x10,0,0,0,0,0,0x3f,0,0,0, 0,0,0,0,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0,0,0,0x40,0x4,0,0x2e,0x33, 0x38,0x31,0x20,0x20,0x20,0x20,0x54,0x53,0x38,0x33,0x30,0x30,0x33,0x31, 0x53,0x41, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x10,0x80, 0,0,0,0x2f,0,0,0,0x2,0,0x2,0x7,0,0xff,0xff,0x1,0, 0x3f,0,0xc1,0xff,0x3e,0,0x10,0x1,0xb0,0xf8,0x50,0x9,0,0,0x7,0, 0x3,0,0x78,0,0x78,0,0xf0,0,0x78,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0x2,0,0,0,0,0,0,0, 0x7e,0,0x1b,0,0x6b,0x34,0x1,0x7d,0x3,0x40,0x69,0x34,0x1,0x3c,0x3,0x40, 0x7f,0x40,0,0,0,0,0xfe,0xfe,0,0,0,0,0,0xfe,0,0, 0,0,0,0,0,0,0,0,0xb0,0xf8,0x50,0x9,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0x1,0,0xb0,0xf8,0x50,0x9,0xb0,0xf8,0x50,0x9,0x20,0x20,0x2,0,0xb6,0x42, 0,0x80,0x8a,0,0x6,0x3c,0xa,0x3c,0xff,0xff,0xc6,0x7,0,0x1,0,0x8, 0xf0,0xf,0,0x10,0x2,0,0x30,0,0,0,0,0,0,0,0x6,0xfe, 0,0,0x2,0,0x50,0,0x8a,0,0x4f,0x95,0,0,0x21,0,0xb,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xa5,0x51, }; static int inquiry_evpd_89(unsigned char * arr) { memcpy(arr, vpd89_data, sizeof(vpd89_data)); return sizeof(vpd89_data); } /* Block limits VPD page (SBC-3) */ static unsigned char vpdb0_data[] = { /* from 4th byte */ 0,0,0,4, 0,0,0x4,0, 0,0,0,64, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, }; static int inquiry_evpd_b0(unsigned char * arr) { unsigned int gran; memcpy(arr, vpdb0_data, sizeof(vpdb0_data)); /* Optimal transfer length granularity */ gran = 1 << scsi_debug_physblk_exp; arr[2] = (gran >> 8) & 0xff; arr[3] = gran & 0xff; /* Maximum Transfer Length */ if (sdebug_store_sectors > 0x400) { arr[4] = (sdebug_store_sectors >> 24) & 0xff; arr[5] = (sdebug_store_sectors >> 16) & 0xff; arr[6] = (sdebug_store_sectors >> 8) & 0xff; arr[7] = sdebug_store_sectors & 0xff; } /* Optimal Transfer Length */ put_unaligned_be32(scsi_debug_opt_blks, &arr[8]); if (scsi_debug_lbpu) { /* Maximum Unmap LBA Count */ put_unaligned_be32(scsi_debug_unmap_max_blocks, &arr[16]); /* Maximum Unmap Block Descriptor Count */ put_unaligned_be32(scsi_debug_unmap_max_desc, &arr[20]); } /* Unmap Granularity Alignment */ if (scsi_debug_unmap_alignment) { put_unaligned_be32(scsi_debug_unmap_alignment, &arr[28]); arr[28] |= 0x80; /* UGAVALID */ } /* Optimal Unmap Granularity */ put_unaligned_be32(scsi_debug_unmap_granularity, &arr[24]); /* Maximum WRITE SAME Length */ put_unaligned_be64(scsi_debug_write_same_length, &arr[32]); return 0x3c; /* Mandatory page length for Logical Block Provisioning */ return sizeof(vpdb0_data); } /* Block device characteristics VPD page (SBC-3) */ static int inquiry_evpd_b1(unsigned char *arr) { memset(arr, 0, 0x3c); arr[0] = 0; arr[1] = 1; /* non rotating medium (e.g. solid state) */ arr[2] = 0; arr[3] = 5; /* less than 1.8" */ return 0x3c; } /* Thin provisioning VPD page (SBC-3) */ static int inquiry_evpd_b2(unsigned char *arr) { memset(arr, 0, 0x8); arr[0] = 0; /* threshold exponent */ if (scsi_debug_lbpu) arr[1] = 1 << 7; if (scsi_debug_lbpws) arr[1] |= 1 << 6; if (scsi_debug_lbpws10) arr[1] |= 1 << 5; return 0x8; } #define SDEBUG_LONG_INQ_SZ 96 #define SDEBUG_MAX_INQ_ARR_SZ 584 static int resp_inquiry(struct scsi_cmnd * scp, int target, struct sdebug_dev_info * devip) { unsigned char pq_pdt; unsigned char * arr; unsigned char *cmd = (unsigned char *)scp->cmnd; int alloc_len, n, ret; alloc_len = (cmd[3] << 8) + cmd[4]; arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC); if (! arr) return DID_REQUEUE << 16; if (devip->wlun) pq_pdt = 0x1e; /* present, wlun */ else if (scsi_debug_no_lun_0 && (0 == devip->lun)) pq_pdt = 0x7f; /* not present, no device type */ else pq_pdt = (scsi_debug_ptype & 0x1f); arr[0] = pq_pdt; if (0x2 & cmd[1]) { /* CMDDT bit set */ mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); kfree(arr); return check_condition_result; } else if (0x1 & cmd[1]) { /* EVPD bit set */ int lu_id_num, port_group_id, target_dev_id, len; char lu_id_str[6]; int host_no = devip->sdbg_host->shost->host_no; port_group_id = (((host_no + 1) & 0x7f) << 8) + (devip->channel & 0x7f); if (0 == scsi_debug_vpd_use_hostno) host_no = 0; lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) + (devip->target * 1000) + devip->lun); target_dev_id = ((host_no + 1) * 2000) + (devip->target * 1000) - 3; len = scnprintf(lu_id_str, 6, "%d", lu_id_num); if (0 == cmd[2]) { /* supported vital product data pages */ arr[1] = cmd[2]; /*sanity */ n = 4; arr[n++] = 0x0; /* this page */ arr[n++] = 0x80; /* unit serial number */ arr[n++] = 0x83; /* device identification */ arr[n++] = 0x84; /* software interface ident. */ arr[n++] = 0x85; /* management network addresses */ arr[n++] = 0x86; /* extended inquiry */ arr[n++] = 0x87; /* mode page policy */ arr[n++] = 0x88; /* SCSI ports */ arr[n++] = 0x89; /* ATA information */ arr[n++] = 0xb0; /* Block limits (SBC) */ arr[n++] = 0xb1; /* Block characteristics (SBC) */ if (scsi_debug_lbp()) /* Logical Block Prov. (SBC) */ arr[n++] = 0xb2; arr[3] = n - 4; /* number of supported VPD pages */ } else if (0x80 == cmd[2]) { /* unit serial number */ arr[1] = cmd[2]; /*sanity */ arr[3] = len; memcpy(&arr[4], lu_id_str, len); } else if (0x83 == cmd[2]) { /* device identification */ arr[1] = cmd[2]; /*sanity */ arr[3] = inquiry_evpd_83(&arr[4], port_group_id, target_dev_id, lu_id_num, lu_id_str, len); } else if (0x84 == cmd[2]) { /* Software interface ident. */ arr[1] = cmd[2]; /*sanity */ arr[3] = inquiry_evpd_84(&arr[4]); } else if (0x85 == cmd[2]) { /* Management network addresses */ arr[1] = cmd[2]; /*sanity */ arr[3] = inquiry_evpd_85(&arr[4]); } else if (0x86 == cmd[2]) { /* extended inquiry */ arr[1] = cmd[2]; /*sanity */ arr[3] = 0x3c; /* number of following entries */ if (scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) arr[4] = 0x4; /* SPT: GRD_CHK:1 */ else if (scsi_debug_dif) arr[4] = 0x5; /* SPT: GRD_CHK:1, REF_CHK:1 */ else arr[4] = 0x0; /* no protection stuff */ arr[5] = 0x7; /* head of q, ordered + simple q's */ } else if (0x87 == cmd[2]) { /* mode page policy */ arr[1] = cmd[2]; /*sanity */ arr[3] = 0x8; /* number of following entries */ arr[4] = 0x2; /* disconnect-reconnect mp */ arr[6] = 0x80; /* mlus, shared */ arr[8] = 0x18; /* protocol specific lu */ arr[10] = 0x82; /* mlus, per initiator port */ } else if (0x88 == cmd[2]) { /* SCSI Ports */ arr[1] = cmd[2]; /*sanity */ arr[3] = inquiry_evpd_88(&arr[4], target_dev_id); } else if (0x89 == cmd[2]) { /* ATA information */ arr[1] = cmd[2]; /*sanity */ n = inquiry_evpd_89(&arr[4]); arr[2] = (n >> 8); arr[3] = (n & 0xff); } else if (0xb0 == cmd[2]) { /* Block limits (SBC) */ arr[1] = cmd[2]; /*sanity */ arr[3] = inquiry_evpd_b0(&arr[4]); } else if (0xb1 == cmd[2]) { /* Block characteristics (SBC) */ arr[1] = cmd[2]; /*sanity */ arr[3] = inquiry_evpd_b1(&arr[4]); } else if (0xb2 == cmd[2]) { /* Logical Block Prov. (SBC) */ arr[1] = cmd[2]; /*sanity */ arr[3] = inquiry_evpd_b2(&arr[4]); } else { /* Illegal request, invalid field in cdb */ mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); kfree(arr); return check_condition_result; } len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len); ret = fill_from_dev_buffer(scp, arr, min(len, SDEBUG_MAX_INQ_ARR_SZ)); kfree(arr); return ret; } /* drops through here for a standard inquiry */ arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0; /* Removable disk */ arr[2] = scsi_debug_scsi_level; arr[3] = 2; /* response_data_format==2 */ arr[4] = SDEBUG_LONG_INQ_SZ - 5; arr[5] = scsi_debug_dif ? 1 : 0; /* PROTECT bit */ if (0 == scsi_debug_vpd_use_hostno) arr[5] = 0x10; /* claim: implicit TGPS */ arr[6] = 0x10; /* claim: MultiP */ /* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */ arr[7] = 0xa; /* claim: LINKED + CMDQUE */ memcpy(&arr[8], inq_vendor_id, 8); memcpy(&arr[16], inq_product_id, 16); memcpy(&arr[32], inq_product_rev, 4); /* version descriptors (2 bytes each) follow */ arr[58] = 0x0; arr[59] = 0x77; /* SAM-3 ANSI */ arr[60] = 0x3; arr[61] = 0x14; /* SPC-3 ANSI */ n = 62; if (scsi_debug_ptype == 0) { arr[n++] = 0x3; arr[n++] = 0x3d; /* SBC-2 ANSI */ } else if (scsi_debug_ptype == 1) { arr[n++] = 0x3; arr[n++] = 0x60; /* SSC-2 no version */ } arr[n++] = 0xc; arr[n++] = 0xf; /* SAS-1.1 rev 10 */ ret = fill_from_dev_buffer(scp, arr, min(alloc_len, SDEBUG_LONG_INQ_SZ)); kfree(arr); return ret; } static int resp_requests(struct scsi_cmnd * scp, struct sdebug_dev_info * devip) { unsigned char * sbuff; unsigned char *cmd = (unsigned char *)scp->cmnd; unsigned char arr[SDEBUG_SENSE_LEN]; int want_dsense; int len = 18; memset(arr, 0, sizeof(arr)); if (devip->reset == 1) mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0); want_dsense = !!(cmd[1] & 1) || scsi_debug_dsense; sbuff = devip->sense_buff; if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) { if (want_dsense) { arr[0] = 0x72; arr[1] = 0x0; /* NO_SENSE in sense_key */ arr[2] = THRESHOLD_EXCEEDED; arr[3] = 0xff; /* TEST set and MRIE==6 */ } else { arr[0] = 0x70; arr[2] = 0x0; /* NO_SENSE in sense_key */ arr[7] = 0xa; /* 18 byte sense buffer */ arr[12] = THRESHOLD_EXCEEDED; arr[13] = 0xff; /* TEST set and MRIE==6 */ } } else { memcpy(arr, sbuff, SDEBUG_SENSE_LEN); if ((cmd[1] & 1) && (! scsi_debug_dsense)) { /* DESC bit set and sense_buff in fixed format */ memset(arr, 0, sizeof(arr)); arr[0] = 0x72; arr[1] = sbuff[2]; /* sense key */ arr[2] = sbuff[12]; /* asc */ arr[3] = sbuff[13]; /* ascq */ len = 8; } } mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0); return fill_from_dev_buffer(scp, arr, len); } static int resp_start_stop(struct scsi_cmnd * scp, struct sdebug_dev_info * devip) { unsigned char *cmd = (unsigned char *)scp->cmnd; int power_cond, errsts, start; if ((errsts = check_readiness(scp, 1, devip))) return errsts; power_cond = (cmd[4] & 0xf0) >> 4; if (power_cond) { mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } start = cmd[4] & 1; if (start == devip->stopped) devip->stopped = !start; return 0; } static sector_t get_sdebug_capacity(void) { if (scsi_debug_virtual_gb > 0) return (sector_t)scsi_debug_virtual_gb * (1073741824 / scsi_debug_sector_size); else return sdebug_store_sectors; } #define SDEBUG_READCAP_ARR_SZ 8 static int resp_readcap(struct scsi_cmnd * scp, struct sdebug_dev_info * devip) { unsigned char arr[SDEBUG_READCAP_ARR_SZ]; unsigned int capac; int errsts; if ((errsts = check_readiness(scp, 1, devip))) return errsts; /* following just in case virtual_gb changed */ sdebug_capacity = get_sdebug_capacity(); memset(arr, 0, SDEBUG_READCAP_ARR_SZ); if (sdebug_capacity < 0xffffffff) { capac = (unsigned int)sdebug_capacity - 1; arr[0] = (capac >> 24); arr[1] = (capac >> 16) & 0xff; arr[2] = (capac >> 8) & 0xff; arr[3] = capac & 0xff; } else { arr[0] = 0xff; arr[1] = 0xff; arr[2] = 0xff; arr[3] = 0xff; } arr[6] = (scsi_debug_sector_size >> 8) & 0xff; arr[7] = scsi_debug_sector_size & 0xff; return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ); } #define SDEBUG_READCAP16_ARR_SZ 32 static int resp_readcap16(struct scsi_cmnd * scp, struct sdebug_dev_info * devip) { unsigned char *cmd = (unsigned char *)scp->cmnd; unsigned char arr[SDEBUG_READCAP16_ARR_SZ]; unsigned long long capac; int errsts, k, alloc_len; if ((errsts = check_readiness(scp, 1, devip))) return errsts; alloc_len = ((cmd[10] << 24) + (cmd[11] << 16) + (cmd[12] << 8) + cmd[13]); /* following just in case virtual_gb changed */ sdebug_capacity = get_sdebug_capacity(); memset(arr, 0, SDEBUG_READCAP16_ARR_SZ); capac = sdebug_capacity - 1; for (k = 0; k < 8; ++k, capac >>= 8) arr[7 - k] = capac & 0xff; arr[8] = (scsi_debug_sector_size >> 24) & 0xff; arr[9] = (scsi_debug_sector_size >> 16) & 0xff; arr[10] = (scsi_debug_sector_size >> 8) & 0xff; arr[11] = scsi_debug_sector_size & 0xff; arr[13] = scsi_debug_physblk_exp & 0xf; arr[14] = (scsi_debug_lowest_aligned >> 8) & 0x3f; if (scsi_debug_lbp()) arr[14] |= 0x80; /* LBPME */ arr[15] = scsi_debug_lowest_aligned & 0xff; if (scsi_debug_dif) { arr[12] = (scsi_debug_dif - 1) << 1; /* P_TYPE */ arr[12] |= 1; /* PROT_EN */ } return fill_from_dev_buffer(scp, arr, min(alloc_len, SDEBUG_READCAP16_ARR_SZ)); } #define SDEBUG_MAX_TGTPGS_ARR_SZ 1412 static int resp_report_tgtpgs(struct scsi_cmnd * scp, struct sdebug_dev_info * devip) { unsigned char *cmd = (unsigned char *)scp->cmnd; unsigned char * arr; int host_no = devip->sdbg_host->shost->host_no; int n, ret, alen, rlen; int port_group_a, port_group_b, port_a, port_b; alen = ((cmd[6] << 24) + (cmd[7] << 16) + (cmd[8] << 8) + cmd[9]); arr = kzalloc(SDEBUG_MAX_TGTPGS_ARR_SZ, GFP_ATOMIC); if (! arr) return DID_REQUEUE << 16; /* * EVPD page 0x88 states we have two ports, one * real and a fake port with no device connected. * So we create two port groups with one port each * and set the group with port B to unavailable. */ port_a = 0x1; /* relative port A */ port_b = 0x2; /* relative port B */ port_group_a = (((host_no + 1) & 0x7f) << 8) + (devip->channel & 0x7f); port_group_b = (((host_no + 1) & 0x7f) << 8) + (devip->channel & 0x7f) + 0x80; /* * The asymmetric access state is cycled according to the host_id. */ n = 4; if (0 == scsi_debug_vpd_use_hostno) { arr[n++] = host_no % 3; /* Asymm access state */ arr[n++] = 0x0F; /* claim: all states are supported */ } else { arr[n++] = 0x0; /* Active/Optimized path */ arr[n++] = 0x01; /* claim: only support active/optimized paths */ } arr[n++] = (port_group_a >> 8) & 0xff; arr[n++] = port_group_a & 0xff; arr[n++] = 0; /* Reserved */ arr[n++] = 0; /* Status code */ arr[n++] = 0; /* Vendor unique */ arr[n++] = 0x1; /* One port per group */ arr[n++] = 0; /* Reserved */ arr[n++] = 0; /* Reserved */ arr[n++] = (port_a >> 8) & 0xff; arr[n++] = port_a & 0xff; arr[n++] = 3; /* Port unavailable */ arr[n++] = 0x08; /* claim: only unavailalbe paths are supported */ arr[n++] = (port_group_b >> 8) & 0xff; arr[n++] = port_group_b & 0xff; arr[n++] = 0; /* Reserved */ arr[n++] = 0; /* Status code */ arr[n++] = 0; /* Vendor unique */ arr[n++] = 0x1; /* One port per group */ arr[n++] = 0; /* Reserved */ arr[n++] = 0; /* Reserved */ arr[n++] = (port_b >> 8) & 0xff; arr[n++] = port_b & 0xff; rlen = n - 4; arr[0] = (rlen >> 24) & 0xff; arr[1] = (rlen >> 16) & 0xff; arr[2] = (rlen >> 8) & 0xff; arr[3] = rlen & 0xff; /* * Return the smallest value of either * - The allocated length * - The constructed command length * - The maximum array size */ rlen = min(alen,n); ret = fill_from_dev_buffer(scp, arr, min(rlen, SDEBUG_MAX_TGTPGS_ARR_SZ)); kfree(arr); return ret; } /* <<Following mode page info copied from ST318451LW>> */ static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target) { /* Read-Write Error Recovery page for mode_sense */ unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0, 5, 0, 0xff, 0xff}; memcpy(p, err_recov_pg, sizeof(err_recov_pg)); if (1 == pcontrol) memset(p + 2, 0, sizeof(err_recov_pg) - 2); return sizeof(err_recov_pg); } static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target) { /* Disconnect-Reconnect page for mode_sense */ unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; memcpy(p, disconnect_pg, sizeof(disconnect_pg)); if (1 == pcontrol) memset(p + 2, 0, sizeof(disconnect_pg) - 2); return sizeof(disconnect_pg); } static int resp_format_pg(unsigned char * p, int pcontrol, int target) { /* Format device page for mode_sense */ unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x40, 0, 0, 0}; memcpy(p, format_pg, sizeof(format_pg)); p[10] = (sdebug_sectors_per >> 8) & 0xff; p[11] = sdebug_sectors_per & 0xff; p[12] = (scsi_debug_sector_size >> 8) & 0xff; p[13] = scsi_debug_sector_size & 0xff; if (DEV_REMOVEABLE(target)) p[20] |= 0x20; /* should agree with INQUIRY */ if (1 == pcontrol) memset(p + 2, 0, sizeof(format_pg) - 2); return sizeof(format_pg); } static int resp_caching_pg(unsigned char * p, int pcontrol, int target) { /* Caching page for mode_sense */ unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0}; memcpy(p, caching_pg, sizeof(caching_pg)); if (1 == pcontrol) memset(p + 2, 0, sizeof(caching_pg) - 2); return sizeof(caching_pg); } static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target) { /* Control mode page for mode_sense */ unsigned char ch_ctrl_m_pg[] = {/* 0xa, 10, */ 0x6, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char d_ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0, 0, 0, 0x2, 0x4b}; if (scsi_debug_dsense) ctrl_m_pg[2] |= 0x4; else ctrl_m_pg[2] &= ~0x4; if (scsi_debug_ato) ctrl_m_pg[5] |= 0x80; /* ATO=1 */ memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg)); if (1 == pcontrol) memcpy(p + 2, ch_ctrl_m_pg, sizeof(ch_ctrl_m_pg)); else if (2 == pcontrol) memcpy(p, d_ctrl_m_pg, sizeof(d_ctrl_m_pg)); return sizeof(ctrl_m_pg); } static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target) { /* Informational Exceptions control mode page for mode_sense */ unsigned char ch_iec_m_pg[] = {/* 0x1c, 0xa, */ 0x4, 0xf, 0, 0, 0, 0, 0, 0, 0x0, 0x0}; unsigned char d_iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0, 0, 0, 0x0, 0x0}; memcpy(p, iec_m_pg, sizeof(iec_m_pg)); if (1 == pcontrol) memcpy(p + 2, ch_iec_m_pg, sizeof(ch_iec_m_pg)); else if (2 == pcontrol) memcpy(p, d_iec_m_pg, sizeof(d_iec_m_pg)); return sizeof(iec_m_pg); } static int resp_sas_sf_m_pg(unsigned char * p, int pcontrol, int target) { /* SAS SSP mode page - short format for mode_sense */ unsigned char sas_sf_m_pg[] = {0x19, 0x6, 0x6, 0x0, 0x7, 0xd0, 0x0, 0x0}; memcpy(p, sas_sf_m_pg, sizeof(sas_sf_m_pg)); if (1 == pcontrol) memset(p + 2, 0, sizeof(sas_sf_m_pg) - 2); return sizeof(sas_sf_m_pg); } static int resp_sas_pcd_m_spg(unsigned char * p, int pcontrol, int target, int target_dev_id) { /* SAS phy control and discover mode page for mode_sense */ unsigned char sas_pcd_m_pg[] = {0x59, 0x1, 0, 0x64, 0, 0x6, 0, 2, 0, 0, 0, 0, 0x10, 0x9, 0x8, 0x0, 0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0, 0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1, 0x2, 0, 0, 0, 0, 0, 0, 0, 0x88, 0x99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0x10, 0x9, 0x8, 0x0, 0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0, 0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1, 0x3, 0, 0, 0, 0, 0, 0, 0, 0x88, 0x99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; int port_a, port_b; port_a = target_dev_id + 1; port_b = port_a + 1; memcpy(p, sas_pcd_m_pg, sizeof(sas_pcd_m_pg)); p[20] = (port_a >> 24); p[21] = (port_a >> 16) & 0xff; p[22] = (port_a >> 8) & 0xff; p[23] = port_a & 0xff; p[48 + 20] = (port_b >> 24); p[48 + 21] = (port_b >> 16) & 0xff; p[48 + 22] = (port_b >> 8) & 0xff; p[48 + 23] = port_b & 0xff; if (1 == pcontrol) memset(p + 4, 0, sizeof(sas_pcd_m_pg) - 4); return sizeof(sas_pcd_m_pg); } static int resp_sas_sha_m_spg(unsigned char * p, int pcontrol) { /* SAS SSP shared protocol specific port mode subpage */ unsigned char sas_sha_m_pg[] = {0x59, 0x2, 0, 0xc, 0, 0x6, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; memcpy(p, sas_sha_m_pg, sizeof(sas_sha_m_pg)); if (1 == pcontrol) memset(p + 4, 0, sizeof(sas_sha_m_pg) - 4); return sizeof(sas_sha_m_pg); } #define SDEBUG_MAX_MSENSE_SZ 256 static int resp_mode_sense(struct scsi_cmnd * scp, int target, struct sdebug_dev_info * devip) { unsigned char dbd, llbaa; int pcontrol, pcode, subpcode, bd_len; unsigned char dev_spec; int k, alloc_len, msense_6, offset, len, errsts, target_dev_id; unsigned char * ap; unsigned char arr[SDEBUG_MAX_MSENSE_SZ]; unsigned char *cmd = (unsigned char *)scp->cmnd; if ((errsts = check_readiness(scp, 1, devip))) return errsts; dbd = !!(cmd[1] & 0x8); pcontrol = (cmd[2] & 0xc0) >> 6; pcode = cmd[2] & 0x3f; subpcode = cmd[3]; msense_6 = (MODE_SENSE == cmd[0]); llbaa = msense_6 ? 0 : !!(cmd[1] & 0x10); if ((0 == scsi_debug_ptype) && (0 == dbd)) bd_len = llbaa ? 16 : 8; else bd_len = 0; alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]); memset(arr, 0, SDEBUG_MAX_MSENSE_SZ); if (0x3 == pcontrol) { /* Saving values not supported */ mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP, 0); return check_condition_result; } target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) + (devip->target * 1000) - 3; /* set DPOFUA bit for disks */ if (0 == scsi_debug_ptype) dev_spec = (DEV_READONLY(target) ? 0x80 : 0x0) | 0x10; else dev_spec = 0x0; if (msense_6) { arr[2] = dev_spec; arr[3] = bd_len; offset = 4; } else { arr[3] = dev_spec; if (16 == bd_len) arr[4] = 0x1; /* set LONGLBA bit */ arr[7] = bd_len; /* assume 255 or less */ offset = 8; } ap = arr + offset; if ((bd_len > 0) && (!sdebug_capacity)) sdebug_capacity = get_sdebug_capacity(); if (8 == bd_len) { if (sdebug_capacity > 0xfffffffe) { ap[0] = 0xff; ap[1] = 0xff; ap[2] = 0xff; ap[3] = 0xff; } else { ap[0] = (sdebug_capacity >> 24) & 0xff; ap[1] = (sdebug_capacity >> 16) & 0xff; ap[2] = (sdebug_capacity >> 8) & 0xff; ap[3] = sdebug_capacity & 0xff; } ap[6] = (scsi_debug_sector_size >> 8) & 0xff; ap[7] = scsi_debug_sector_size & 0xff; offset += bd_len; ap = arr + offset; } else if (16 == bd_len) { unsigned long long capac = sdebug_capacity; for (k = 0; k < 8; ++k, capac >>= 8) ap[7 - k] = capac & 0xff; ap[12] = (scsi_debug_sector_size >> 24) & 0xff; ap[13] = (scsi_debug_sector_size >> 16) & 0xff; ap[14] = (scsi_debug_sector_size >> 8) & 0xff; ap[15] = scsi_debug_sector_size & 0xff; offset += bd_len; ap = arr + offset; } if ((subpcode > 0x0) && (subpcode < 0xff) && (0x19 != pcode)) { /* TODO: Control Extension page */ mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } switch (pcode) { case 0x1: /* Read-Write error recovery page, direct access */ len = resp_err_recov_pg(ap, pcontrol, target); offset += len; break; case 0x2: /* Disconnect-Reconnect page, all devices */ len = resp_disconnect_pg(ap, pcontrol, target); offset += len; break; case 0x3: /* Format device page, direct access */ len = resp_format_pg(ap, pcontrol, target); offset += len; break; case 0x8: /* Caching page, direct access */ len = resp_caching_pg(ap, pcontrol, target); offset += len; break; case 0xa: /* Control Mode page, all devices */ len = resp_ctrl_m_pg(ap, pcontrol, target); offset += len; break; case 0x19: /* if spc==1 then sas phy, control+discover */ if ((subpcode > 0x2) && (subpcode < 0xff)) { mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } len = 0; if ((0x0 == subpcode) || (0xff == subpcode)) len += resp_sas_sf_m_pg(ap + len, pcontrol, target); if ((0x1 == subpcode) || (0xff == subpcode)) len += resp_sas_pcd_m_spg(ap + len, pcontrol, target, target_dev_id); if ((0x2 == subpcode) || (0xff == subpcode)) len += resp_sas_sha_m_spg(ap + len, pcontrol); offset += len; break; case 0x1c: /* Informational Exceptions Mode page, all devices */ len = resp_iec_m_pg(ap, pcontrol, target); offset += len; break; case 0x3f: /* Read all Mode pages */ if ((0 == subpcode) || (0xff == subpcode)) { len = resp_err_recov_pg(ap, pcontrol, target); len += resp_disconnect_pg(ap + len, pcontrol, target); len += resp_format_pg(ap + len, pcontrol, target); len += resp_caching_pg(ap + len, pcontrol, target); len += resp_ctrl_m_pg(ap + len, pcontrol, target); len += resp_sas_sf_m_pg(ap + len, pcontrol, target); if (0xff == subpcode) { len += resp_sas_pcd_m_spg(ap + len, pcontrol, target, target_dev_id); len += resp_sas_sha_m_spg(ap + len, pcontrol); } len += resp_iec_m_pg(ap + len, pcontrol, target); } else { mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } offset += len; break; default: mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } if (msense_6) arr[0] = offset - 1; else { arr[0] = ((offset - 2) >> 8) & 0xff; arr[1] = (offset - 2) & 0xff; } return fill_from_dev_buffer(scp, arr, min(alloc_len, offset)); } #define SDEBUG_MAX_MSELECT_SZ 512 static int resp_mode_select(struct scsi_cmnd * scp, int mselect6, struct sdebug_dev_info * devip) { int pf, sp, ps, md_len, bd_len, off, spf, pg_len; int param_len, res, errsts, mpage; unsigned char arr[SDEBUG_MAX_MSELECT_SZ]; unsigned char *cmd = (unsigned char *)scp->cmnd; if ((errsts = check_readiness(scp, 1, devip))) return errsts; memset(arr, 0, sizeof(arr)); pf = cmd[1] & 0x10; sp = cmd[1] & 0x1; param_len = mselect6 ? cmd[4] : ((cmd[7] << 8) + cmd[8]); if ((0 == pf) || sp || (param_len > SDEBUG_MAX_MSELECT_SZ)) { mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } res = fetch_to_dev_buffer(scp, arr, param_len); if (-1 == res) return (DID_ERROR << 16); else if ((res < param_len) && (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)) printk(KERN_INFO "scsi_debug: mode_select: cdb indicated=%d, " " IO sent=%d bytes\n", param_len, res); md_len = mselect6 ? (arr[0] + 1) : ((arr[0] << 8) + arr[1] + 2); bd_len = mselect6 ? arr[3] : ((arr[6] << 8) + arr[7]); if (md_len > 2) { mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_PARAM_LIST, 0); return check_condition_result; } off = bd_len + (mselect6 ? 4 : 8); mpage = arr[off] & 0x3f; ps = !!(arr[off] & 0x80); if (ps) { mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_PARAM_LIST, 0); return check_condition_result; } spf = !!(arr[off] & 0x40); pg_len = spf ? ((arr[off + 2] << 8) + arr[off + 3] + 4) : (arr[off + 1] + 2); if ((pg_len + off) > param_len) { mk_sense_buffer(devip, ILLEGAL_REQUEST, PARAMETER_LIST_LENGTH_ERR, 0); return check_condition_result; } switch (mpage) { case 0xa: /* Control Mode page */ if (ctrl_m_pg[1] == arr[off + 1]) { memcpy(ctrl_m_pg + 2, arr + off + 2, sizeof(ctrl_m_pg) - 2); scsi_debug_dsense = !!(ctrl_m_pg[2] & 0x4); return 0; } break; case 0x1c: /* Informational Exceptions Mode page */ if (iec_m_pg[1] == arr[off + 1]) { memcpy(iec_m_pg + 2, arr + off + 2, sizeof(iec_m_pg) - 2); return 0; } break; default: break; } mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_PARAM_LIST, 0); return check_condition_result; } static int resp_temp_l_pg(unsigned char * arr) { unsigned char temp_l_pg[] = {0x0, 0x0, 0x3, 0x2, 0x0, 38, 0x0, 0x1, 0x3, 0x2, 0x0, 65, }; memcpy(arr, temp_l_pg, sizeof(temp_l_pg)); return sizeof(temp_l_pg); } static int resp_ie_l_pg(unsigned char * arr) { unsigned char ie_l_pg[] = {0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 38, }; memcpy(arr, ie_l_pg, sizeof(ie_l_pg)); if (iec_m_pg[2] & 0x4) { /* TEST bit set */ arr[4] = THRESHOLD_EXCEEDED; arr[5] = 0xff; } return sizeof(ie_l_pg); } #define SDEBUG_MAX_LSENSE_SZ 512 static int resp_log_sense(struct scsi_cmnd * scp, struct sdebug_dev_info * devip) { int ppc, sp, pcontrol, pcode, subpcode, alloc_len, errsts, len, n; unsigned char arr[SDEBUG_MAX_LSENSE_SZ]; unsigned char *cmd = (unsigned char *)scp->cmnd; if ((errsts = check_readiness(scp, 1, devip))) return errsts; memset(arr, 0, sizeof(arr)); ppc = cmd[1] & 0x2; sp = cmd[1] & 0x1; if (ppc || sp) { mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } pcontrol = (cmd[2] & 0xc0) >> 6; pcode = cmd[2] & 0x3f; subpcode = cmd[3] & 0xff; alloc_len = (cmd[7] << 8) + cmd[8]; arr[0] = pcode; if (0 == subpcode) { switch (pcode) { case 0x0: /* Supported log pages log page */ n = 4; arr[n++] = 0x0; /* this page */ arr[n++] = 0xd; /* Temperature */ arr[n++] = 0x2f; /* Informational exceptions */ arr[3] = n - 4; break; case 0xd: /* Temperature log page */ arr[3] = resp_temp_l_pg(arr + 4); break; case 0x2f: /* Informational exceptions log page */ arr[3] = resp_ie_l_pg(arr + 4); break; default: mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } } else if (0xff == subpcode) { arr[0] |= 0x40; arr[1] = subpcode; switch (pcode) { case 0x0: /* Supported log pages and subpages log page */ n = 4; arr[n++] = 0x0; arr[n++] = 0x0; /* 0,0 page */ arr[n++] = 0x0; arr[n++] = 0xff; /* this page */ arr[n++] = 0xd; arr[n++] = 0x0; /* Temperature */ arr[n++] = 0x2f; arr[n++] = 0x0; /* Informational exceptions */ arr[3] = n - 4; break; case 0xd: /* Temperature subpages */ n = 4; arr[n++] = 0xd; arr[n++] = 0x0; /* Temperature */ arr[3] = n - 4; break; case 0x2f: /* Informational exceptions subpages */ n = 4; arr[n++] = 0x2f; arr[n++] = 0x0; /* Informational exceptions */ arr[3] = n - 4; break; default: mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } } else { mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len); return fill_from_dev_buffer(scp, arr, min(len, SDEBUG_MAX_INQ_ARR_SZ)); } static int check_device_access_params(struct sdebug_dev_info *devi, unsigned long long lba, unsigned int num) { if (lba + num > sdebug_capacity) { mk_sense_buffer(devi, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE, 0); return check_condition_result; } /* transfer length excessive (tie in to block limits VPD page) */ if (num > sdebug_store_sectors) { mk_sense_buffer(devi, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } return 0; } static int do_device_access(struct scsi_cmnd *scmd, struct sdebug_dev_info *devi, unsigned long long lba, unsigned int num, int write) { int ret; unsigned long long block, rest = 0; int (*func)(struct scsi_cmnd *, unsigned char *, int); func = write ? fetch_to_dev_buffer : fill_from_dev_buffer; block = do_div(lba, sdebug_store_sectors); if (block + num > sdebug_store_sectors) rest = block + num - sdebug_store_sectors; ret = func(scmd, fake_storep + (block * scsi_debug_sector_size), (num - rest) * scsi_debug_sector_size); if (!ret && rest) ret = func(scmd, fake_storep, rest * scsi_debug_sector_size); return ret; } static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec, unsigned int sectors, u32 ei_lba) { unsigned int i, resid; struct scatterlist *psgl; struct sd_dif_tuple *sdt; sector_t sector; sector_t tmp_sec = start_sec; void *paddr; start_sec = do_div(tmp_sec, sdebug_store_sectors); sdt = (struct sd_dif_tuple *)(dif_storep + dif_offset(start_sec)); for (i = 0 ; i < sectors ; i++) { u16 csum; if (sdt[i].app_tag == 0xffff) continue; sector = start_sec + i; switch (scsi_debug_guard) { case 1: csum = ip_compute_csum(fake_storep + sector * scsi_debug_sector_size, scsi_debug_sector_size); break; case 0: csum = crc_t10dif(fake_storep + sector * scsi_debug_sector_size, scsi_debug_sector_size); csum = cpu_to_be16(csum); break; default: BUG(); } if (sdt[i].guard_tag != csum) { printk(KERN_ERR "%s: GUARD check failed on sector %lu" \ " rcvd 0x%04x, data 0x%04x\n", __func__, (unsigned long)sector, be16_to_cpu(sdt[i].guard_tag), be16_to_cpu(csum)); dif_errors++; return 0x01; } if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION && be32_to_cpu(sdt[i].ref_tag) != (sector & 0xffffffff)) { printk(KERN_ERR "%s: REF check failed on sector %lu\n", __func__, (unsigned long)sector); dif_errors++; return 0x03; } if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && be32_to_cpu(sdt[i].ref_tag) != ei_lba) { printk(KERN_ERR "%s: REF check failed on sector %lu\n", __func__, (unsigned long)sector); dif_errors++; return 0x03; } ei_lba++; } resid = sectors * 8; /* Bytes of protection data to copy into sgl */ sector = start_sec; scsi_for_each_prot_sg(SCpnt, psgl, scsi_prot_sg_count(SCpnt), i) { int len = min(psgl->length, resid); paddr = kmap_atomic(sg_page(psgl), KM_IRQ0) + psgl->offset; memcpy(paddr, dif_storep + dif_offset(sector), len); sector += len >> 3; if (sector >= sdebug_store_sectors) { /* Force wrap */ tmp_sec = sector; sector = do_div(tmp_sec, sdebug_store_sectors); } resid -= len; kunmap_atomic(paddr, KM_IRQ0); } dix_reads++; return 0; } static int resp_read(struct scsi_cmnd *SCpnt, unsigned long long lba, unsigned int num, struct sdebug_dev_info *devip, u32 ei_lba) { unsigned long iflags; int ret; ret = check_device_access_params(devip, lba, num); if (ret) return ret; if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) && (lba <= (OPT_MEDIUM_ERR_ADDR + OPT_MEDIUM_ERR_NUM - 1)) && ((lba + num) > OPT_MEDIUM_ERR_ADDR)) { /* claim unrecoverable read error */ mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR, 0); /* set info field and valid bit for fixed descriptor */ if (0x70 == (devip->sense_buff[0] & 0x7f)) { devip->sense_buff[0] |= 0x80; /* Valid bit */ ret = (lba < OPT_MEDIUM_ERR_ADDR) ? OPT_MEDIUM_ERR_ADDR : (int)lba; devip->sense_buff[3] = (ret >> 24) & 0xff; devip->sense_buff[4] = (ret >> 16) & 0xff; devip->sense_buff[5] = (ret >> 8) & 0xff; devip->sense_buff[6] = ret & 0xff; } scsi_set_resid(SCpnt, scsi_bufflen(SCpnt)); return check_condition_result; } /* DIX + T10 DIF */ if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) { int prot_ret = prot_verify_read(SCpnt, lba, num, ei_lba); if (prot_ret) { mk_sense_buffer(devip, ABORTED_COMMAND, 0x10, prot_ret); return illegal_condition_result; } } read_lock_irqsave(&atomic_rw, iflags); ret = do_device_access(SCpnt, devip, lba, num, 0); read_unlock_irqrestore(&atomic_rw, iflags); return ret; } void dump_sector(unsigned char *buf, int len) { int i, j; printk(KERN_ERR ">>> Sector Dump <<<\n"); for (i = 0 ; i < len ; i += 16) { printk(KERN_ERR "%04d: ", i); for (j = 0 ; j < 16 ; j++) { unsigned char c = buf[i+j]; if (c >= 0x20 && c < 0x7e) printk(" %c ", buf[i+j]); else printk("%02x ", buf[i+j]); } printk("\n"); } } static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec, unsigned int sectors, u32 ei_lba) { int i, j, ret; struct sd_dif_tuple *sdt; struct scatterlist *dsgl = scsi_sglist(SCpnt); struct scatterlist *psgl = scsi_prot_sglist(SCpnt); void *daddr, *paddr; sector_t tmp_sec = start_sec; sector_t sector; int ppage_offset; unsigned short csum; sector = do_div(tmp_sec, sdebug_store_sectors); BUG_ON(scsi_sg_count(SCpnt) == 0); BUG_ON(scsi_prot_sg_count(SCpnt) == 0); paddr = kmap_atomic(sg_page(psgl), KM_IRQ1) + psgl->offset; ppage_offset = 0; /* For each data page */ scsi_for_each_sg(SCpnt, dsgl, scsi_sg_count(SCpnt), i) { daddr = kmap_atomic(sg_page(dsgl), KM_IRQ0) + dsgl->offset; /* For each sector-sized chunk in data page */ for (j = 0 ; j < dsgl->length ; j += scsi_debug_sector_size) { /* If we're at the end of the current * protection page advance to the next one */ if (ppage_offset >= psgl->length) { kunmap_atomic(paddr, KM_IRQ1); psgl = sg_next(psgl); BUG_ON(psgl == NULL); paddr = kmap_atomic(sg_page(psgl), KM_IRQ1) + psgl->offset; ppage_offset = 0; } sdt = paddr + ppage_offset; switch (scsi_debug_guard) { case 1: csum = ip_compute_csum(daddr, scsi_debug_sector_size); break; case 0: csum = cpu_to_be16(crc_t10dif(daddr, scsi_debug_sector_size)); break; default: BUG(); ret = 0; goto out; } if (sdt->guard_tag != csum) { printk(KERN_ERR "%s: GUARD check failed on sector %lu " \ "rcvd 0x%04x, calculated 0x%04x\n", __func__, (unsigned long)sector, be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum)); ret = 0x01; dump_sector(daddr, scsi_debug_sector_size); goto out; } if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION && be32_to_cpu(sdt->ref_tag) != (start_sec & 0xffffffff)) { printk(KERN_ERR "%s: REF check failed on sector %lu\n", __func__, (unsigned long)sector); ret = 0x03; dump_sector(daddr, scsi_debug_sector_size); goto out; } if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION && be32_to_cpu(sdt->ref_tag) != ei_lba) { printk(KERN_ERR "%s: REF check failed on sector %lu\n", __func__, (unsigned long)sector); ret = 0x03; dump_sector(daddr, scsi_debug_sector_size); goto out; } /* Would be great to copy this in bigger * chunks. However, for the sake of * correctness we need to verify each sector * before writing it to "stable" storage */ memcpy(dif_storep + dif_offset(sector), sdt, 8); sector++; if (sector == sdebug_store_sectors) sector = 0; /* Force wrap */ start_sec++; ei_lba++; daddr += scsi_debug_sector_size; ppage_offset += sizeof(struct sd_dif_tuple); } kunmap_atomic(daddr, KM_IRQ0); } kunmap_atomic(paddr, KM_IRQ1); dix_writes++; return 0; out: dif_errors++; kunmap_atomic(daddr, KM_IRQ0); kunmap_atomic(paddr, KM_IRQ1); return ret; } static unsigned int map_state(sector_t lba, unsigned int *num) { unsigned int granularity, alignment, mapped; sector_t block, next, end; granularity = scsi_debug_unmap_granularity; alignment = granularity - scsi_debug_unmap_alignment; block = lba + alignment; do_div(block, granularity); mapped = test_bit(block, map_storep); if (mapped) next = find_next_zero_bit(map_storep, map_size, block); else next = find_next_bit(map_storep, map_size, block); end = next * granularity - scsi_debug_unmap_alignment; *num = end - lba; return mapped; } static void map_region(sector_t lba, unsigned int len) { unsigned int granularity, alignment; sector_t end = lba + len; granularity = scsi_debug_unmap_granularity; alignment = granularity - scsi_debug_unmap_alignment; while (lba < end) { sector_t block, rem; block = lba + alignment; rem = do_div(block, granularity); if (block < map_size) set_bit(block, map_storep); lba += granularity - rem; } } static void unmap_region(sector_t lba, unsigned int len) { unsigned int granularity, alignment; sector_t end = lba + len; granularity = scsi_debug_unmap_granularity; alignment = granularity - scsi_debug_unmap_alignment; while (lba < end) { sector_t block, rem; block = lba + alignment; rem = do_div(block, granularity); if (rem == 0 && lba + granularity <= end && block < map_size) clear_bit(block, map_storep); lba += granularity - rem; } } static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba, unsigned int num, struct sdebug_dev_info *devip, u32 ei_lba) { unsigned long iflags; int ret; ret = check_device_access_params(devip, lba, num); if (ret) return ret; /* DIX + T10 DIF */ if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) { int prot_ret = prot_verify_write(SCpnt, lba, num, ei_lba); if (prot_ret) { mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x10, prot_ret); return illegal_condition_result; } } write_lock_irqsave(&atomic_rw, iflags); ret = do_device_access(SCpnt, devip, lba, num, 1); if (scsi_debug_unmap_granularity) map_region(lba, num); write_unlock_irqrestore(&atomic_rw, iflags); if (-1 == ret) return (DID_ERROR << 16); else if ((ret < (num * scsi_debug_sector_size)) && (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)) printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, " " IO sent=%d bytes\n", num * scsi_debug_sector_size, ret); return 0; } static int resp_write_same(struct scsi_cmnd *scmd, unsigned long long lba, unsigned int num, struct sdebug_dev_info *devip, u32 ei_lba, unsigned int unmap) { unsigned long iflags; unsigned long long i; int ret; ret = check_device_access_params(devip, lba, num); if (ret) return ret; if (num > scsi_debug_write_same_length) { mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } write_lock_irqsave(&atomic_rw, iflags); if (unmap && scsi_debug_unmap_granularity) { unmap_region(lba, num); goto out; } /* Else fetch one logical block */ ret = fetch_to_dev_buffer(scmd, fake_storep + (lba * scsi_debug_sector_size), scsi_debug_sector_size); if (-1 == ret) { write_unlock_irqrestore(&atomic_rw, iflags); return (DID_ERROR << 16); } else if ((ret < (num * scsi_debug_sector_size)) && (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)) printk(KERN_INFO "scsi_debug: write same: cdb indicated=%u, " " IO sent=%d bytes\n", num * scsi_debug_sector_size, ret); /* Copy first sector to remaining blocks */ for (i = 1 ; i < num ; i++) memcpy(fake_storep + ((lba + i) * scsi_debug_sector_size), fake_storep + (lba * scsi_debug_sector_size), scsi_debug_sector_size); if (scsi_debug_unmap_granularity) map_region(lba, num); out: write_unlock_irqrestore(&atomic_rw, iflags); return 0; } struct unmap_block_desc { __be64 lba; __be32 blocks; __be32 __reserved; }; static int resp_unmap(struct scsi_cmnd * scmd, struct sdebug_dev_info * devip) { unsigned char *buf; struct unmap_block_desc *desc; unsigned int i, payload_len, descriptors; int ret; ret = check_readiness(scmd, 1, devip); if (ret) return ret; payload_len = get_unaligned_be16(&scmd->cmnd[7]); BUG_ON(scsi_bufflen(scmd) != payload_len); descriptors = (payload_len - 8) / 16; buf = kmalloc(scsi_bufflen(scmd), GFP_ATOMIC); if (!buf) return check_condition_result; scsi_sg_copy_to_buffer(scmd, buf, scsi_bufflen(scmd)); BUG_ON(get_unaligned_be16(&buf[0]) != payload_len - 2); BUG_ON(get_unaligned_be16(&buf[2]) != descriptors * 16); desc = (void *)&buf[8]; for (i = 0 ; i < descriptors ; i++) { unsigned long long lba = get_unaligned_be64(&desc[i].lba); unsigned int num = get_unaligned_be32(&desc[i].blocks); ret = check_device_access_params(devip, lba, num); if (ret) goto out; unmap_region(lba, num); } ret = 0; out: kfree(buf); return ret; } #define SDEBUG_GET_LBA_STATUS_LEN 32 static int resp_get_lba_status(struct scsi_cmnd * scmd, struct sdebug_dev_info * devip) { unsigned long long lba; unsigned int alloc_len, mapped, num; unsigned char arr[SDEBUG_GET_LBA_STATUS_LEN]; int ret; ret = check_readiness(scmd, 1, devip); if (ret) return ret; lba = get_unaligned_be64(&scmd->cmnd[2]); alloc_len = get_unaligned_be32(&scmd->cmnd[10]); if (alloc_len < 24) return 0; ret = check_device_access_params(devip, lba, 1); if (ret) return ret; mapped = map_state(lba, &num); memset(arr, 0, SDEBUG_GET_LBA_STATUS_LEN); put_unaligned_be32(16, &arr[0]); /* Parameter Data Length */ put_unaligned_be64(lba, &arr[8]); /* LBA */ put_unaligned_be32(num, &arr[16]); /* Number of blocks */ arr[20] = !mapped; /* mapped = 0, unmapped = 1 */ return fill_from_dev_buffer(scmd, arr, SDEBUG_GET_LBA_STATUS_LEN); } #define SDEBUG_RLUN_ARR_SZ 256 static int resp_report_luns(struct scsi_cmnd * scp, struct sdebug_dev_info * devip) { unsigned int alloc_len; int lun_cnt, i, upper, num, n, wlun, lun; unsigned char *cmd = (unsigned char *)scp->cmnd; int select_report = (int)cmd[2]; struct scsi_lun *one_lun; unsigned char arr[SDEBUG_RLUN_ARR_SZ]; unsigned char * max_addr; alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24); if ((alloc_len < 4) || (select_report > 2)) { mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0); return check_condition_result; } /* can produce response with up to 16k luns (lun 0 to lun 16383) */ memset(arr, 0, SDEBUG_RLUN_ARR_SZ); lun_cnt = scsi_debug_max_luns; if (1 == select_report) lun_cnt = 0; else if (scsi_debug_no_lun_0 && (lun_cnt > 0)) --lun_cnt; wlun = (select_report > 0) ? 1 : 0; num = lun_cnt + wlun; arr[2] = ((sizeof(struct scsi_lun) * num) >> 8) & 0xff; arr[3] = (sizeof(struct scsi_lun) * num) & 0xff; n = min((int)((SDEBUG_RLUN_ARR_SZ - 8) / sizeof(struct scsi_lun)), num); if (n < num) { wlun = 0; lun_cnt = n; } one_lun = (struct scsi_lun *) &arr[8]; max_addr = arr + SDEBUG_RLUN_ARR_SZ; for (i = 0, lun = (scsi_debug_no_lun_0 ? 1 : 0); ((i < lun_cnt) && ((unsigned char *)(one_lun + i) < max_addr)); i++, lun++) { upper = (lun >> 8) & 0x3f; if (upper) one_lun[i].scsi_lun[0] = (upper | (SAM2_LUN_ADDRESS_METHOD << 6)); one_lun[i].scsi_lun[1] = lun & 0xff; } if (wlun) { one_lun[i].scsi_lun[0] = (SAM2_WLUN_REPORT_LUNS >> 8) & 0xff; one_lun[i].scsi_lun[1] = SAM2_WLUN_REPORT_LUNS & 0xff; i++; } alloc_len = (unsigned char *)(one_lun + i) - arr; return fill_from_dev_buffer(scp, arr, min((int)alloc_len, SDEBUG_RLUN_ARR_SZ)); } static int resp_xdwriteread(struct scsi_cmnd *scp, unsigned long long lba, unsigned int num, struct sdebug_dev_info *devip) { int i, j, ret = -1; unsigned char *kaddr, *buf; unsigned int offset; struct scatterlist *sg; struct scsi_data_buffer *sdb = scsi_in(scp); /* better not to use temporary buffer. */ buf = kmalloc(scsi_bufflen(scp), GFP_ATOMIC); if (!buf) return ret; scsi_sg_copy_to_buffer(scp, buf, scsi_bufflen(scp)); offset = 0; for_each_sg(sdb->table.sgl, sg, sdb->table.nents, i) { kaddr = (unsigned char *)kmap_atomic(sg_page(sg), KM_USER0); if (!kaddr) goto out; for (j = 0; j < sg->length; j++) *(kaddr + sg->offset + j) ^= *(buf + offset + j); offset += sg->length; kunmap_atomic(kaddr, KM_USER0); } ret = 0; out: kfree(buf); return ret; } /* When timer goes off this function is called. */ static void timer_intr_handler(unsigned long indx) { struct sdebug_queued_cmd * sqcp; unsigned long iflags; if (indx >= scsi_debug_max_queue) { printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too " "large\n"); return; } spin_lock_irqsave(&queued_arr_lock, iflags); sqcp = &queued_arr[(int)indx]; if (! sqcp->in_use) { printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected " "interrupt\n"); spin_unlock_irqrestore(&queued_arr_lock, iflags); return; } sqcp->in_use = 0; if (sqcp->done_funct) { sqcp->a_cmnd->result = sqcp->scsi_result; sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */ } sqcp->done_funct = NULL; spin_unlock_irqrestore(&queued_arr_lock, iflags); } static struct sdebug_dev_info * sdebug_device_create(struct sdebug_host_info *sdbg_host, gfp_t flags) { struct sdebug_dev_info *devip; devip = kzalloc(sizeof(*devip), flags); if (devip) { devip->sdbg_host = sdbg_host; list_add_tail(&devip->dev_list, &sdbg_host->dev_info_list); } return devip; } static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev) { struct sdebug_host_info * sdbg_host; struct sdebug_dev_info * open_devip = NULL; struct sdebug_dev_info * devip = (struct sdebug_dev_info *)sdev->hostdata; if (devip) return devip; sdbg_host = *(struct sdebug_host_info **)shost_priv(sdev->host); if (!sdbg_host) { printk(KERN_ERR "Host info NULL\n"); return NULL; } list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) { if ((devip->used) && (devip->channel == sdev->channel) && (devip->target == sdev->id) && (devip->lun == sdev->lun)) return devip; else { if ((!devip->used) && (!open_devip)) open_devip = devip; } } if (!open_devip) { /* try and make a new one */ open_devip = sdebug_device_create(sdbg_host, GFP_ATOMIC); if (!open_devip) { printk(KERN_ERR "%s: out of memory at line %d\n", __func__, __LINE__); return NULL; } } open_devip->channel = sdev->channel; open_devip->target = sdev->id; open_devip->lun = sdev->lun; open_devip->sdbg_host = sdbg_host; open_devip->reset = 1; open_devip->used = 1; memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN); if (scsi_debug_dsense) open_devip->sense_buff[0] = 0x72; else { open_devip->sense_buff[0] = 0x70; open_devip->sense_buff[7] = 0xa; } if (sdev->lun == SAM2_WLUN_REPORT_LUNS) open_devip->wlun = SAM2_WLUN_REPORT_LUNS & 0xff; return open_devip; } static int scsi_debug_slave_alloc(struct scsi_device *sdp) { if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); queue_flag_set_unlocked(QUEUE_FLAG_BIDI, sdp->request_queue); return 0; } static int scsi_debug_slave_configure(struct scsi_device *sdp) { struct sdebug_dev_info *devip; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %u>\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN) sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN; devip = devInfoReg(sdp); if (NULL == devip) return 1; /* no resources, will be marked offline */ sdp->hostdata = devip; if (sdp->host->cmd_per_lun) scsi_adjust_queue_depth(sdp, SDEBUG_TAGGED_QUEUING, sdp->host->cmd_per_lun); blk_queue_max_segment_size(sdp->request_queue, 256 * 1024); if (scsi_debug_no_uld) sdp->no_uld_attach = 1; return 0; } static void scsi_debug_slave_destroy(struct scsi_device *sdp) { struct sdebug_dev_info *devip = (struct sdebug_dev_info *)sdp->hostdata; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %u>\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); if (devip) { /* make this slot available for re-use */ devip->used = 0; sdp->hostdata = NULL; } } /* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */ static int stop_queued_cmnd(struct scsi_cmnd *cmnd) { unsigned long iflags; int k; struct sdebug_queued_cmd *sqcp; spin_lock_irqsave(&queued_arr_lock, iflags); for (k = 0; k < scsi_debug_max_queue; ++k) { sqcp = &queued_arr[k]; if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) { del_timer_sync(&sqcp->cmnd_timer); sqcp->in_use = 0; sqcp->a_cmnd = NULL; break; } } spin_unlock_irqrestore(&queued_arr_lock, iflags); return (k < scsi_debug_max_queue) ? 1 : 0; } /* Deletes (stops) timers of all queued commands */ static void stop_all_queued(void) { unsigned long iflags; int k; struct sdebug_queued_cmd *sqcp; spin_lock_irqsave(&queued_arr_lock, iflags); for (k = 0; k < scsi_debug_max_queue; ++k) { sqcp = &queued_arr[k]; if (sqcp->in_use && sqcp->a_cmnd) { del_timer_sync(&sqcp->cmnd_timer); sqcp->in_use = 0; sqcp->a_cmnd = NULL; } } spin_unlock_irqrestore(&queued_arr_lock, iflags); } static int scsi_debug_abort(struct scsi_cmnd * SCpnt) { if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: abort\n"); ++num_aborts; stop_queued_cmnd(SCpnt); return SUCCESS; } static int scsi_debug_biosparam(struct scsi_device *sdev, struct block_device * bdev, sector_t capacity, int *info) { int res; unsigned char *buf; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: biosparam\n"); buf = scsi_bios_ptable(bdev); if (buf) { res = scsi_partsize(buf, capacity, &info[2], &info[0], &info[1]); kfree(buf); if (! res) return res; } info[0] = sdebug_heads; info[1] = sdebug_sectors_per; info[2] = sdebug_cylinders_per; return 0; } static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt) { struct sdebug_dev_info * devip; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: device_reset\n"); ++num_dev_resets; if (SCpnt) { devip = devInfoReg(SCpnt->device); if (devip) devip->reset = 1; } return SUCCESS; } static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt) { struct sdebug_host_info *sdbg_host; struct sdebug_dev_info * dev_info; struct scsi_device * sdp; struct Scsi_Host * hp; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: bus_reset\n"); ++num_bus_resets; if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) { sdbg_host = *(struct sdebug_host_info **)shost_priv(hp); if (sdbg_host) { list_for_each_entry(dev_info, &sdbg_host->dev_info_list, dev_list) dev_info->reset = 1; } } return SUCCESS; } static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt) { struct sdebug_host_info * sdbg_host; struct sdebug_dev_info * dev_info; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: host_reset\n"); ++num_host_resets; spin_lock(&sdebug_host_list_lock); list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) { list_for_each_entry(dev_info, &sdbg_host->dev_info_list, dev_list) dev_info->reset = 1; } spin_unlock(&sdebug_host_list_lock); stop_all_queued(); return SUCCESS; } /* Initializes timers in queued array */ static void __init init_all_queued(void) { unsigned long iflags; int k; struct sdebug_queued_cmd * sqcp; spin_lock_irqsave(&queued_arr_lock, iflags); for (k = 0; k < scsi_debug_max_queue; ++k) { sqcp = &queued_arr[k]; init_timer(&sqcp->cmnd_timer); sqcp->in_use = 0; sqcp->a_cmnd = NULL; } spin_unlock_irqrestore(&queued_arr_lock, iflags); } static void __init sdebug_build_parts(unsigned char *ramp, unsigned long store_size) { struct partition * pp; int starts[SDEBUG_MAX_PARTS + 2]; int sectors_per_part, num_sectors, k; int heads_by_sects, start_sec, end_sec; /* assume partition table already zeroed */ if ((scsi_debug_num_parts < 1) || (store_size < 1048576)) return; if (scsi_debug_num_parts > SDEBUG_MAX_PARTS) { scsi_debug_num_parts = SDEBUG_MAX_PARTS; printk(KERN_WARNING "scsi_debug:build_parts: reducing " "partitions to %d\n", SDEBUG_MAX_PARTS); } num_sectors = (int)sdebug_store_sectors; sectors_per_part = (num_sectors - sdebug_sectors_per) / scsi_debug_num_parts; heads_by_sects = sdebug_heads * sdebug_sectors_per; starts[0] = sdebug_sectors_per; for (k = 1; k < scsi_debug_num_parts; ++k) starts[k] = ((k * sectors_per_part) / heads_by_sects) * heads_by_sects; starts[scsi_debug_num_parts] = num_sectors; starts[scsi_debug_num_parts + 1] = 0; ramp[510] = 0x55; /* magic partition markings */ ramp[511] = 0xAA; pp = (struct partition *)(ramp + 0x1be); for (k = 0; starts[k + 1]; ++k, ++pp) { start_sec = starts[k]; end_sec = starts[k + 1] - 1; pp->boot_ind = 0; pp->cyl = start_sec / heads_by_sects; pp->head = (start_sec - (pp->cyl * heads_by_sects)) / sdebug_sectors_per; pp->sector = (start_sec % sdebug_sectors_per) + 1; pp->end_cyl = end_sec / heads_by_sects; pp->end_head = (end_sec - (pp->end_cyl * heads_by_sects)) / sdebug_sectors_per; pp->end_sector = (end_sec % sdebug_sectors_per) + 1; pp->start_sect = start_sec; pp->nr_sects = end_sec - start_sec + 1; pp->sys_ind = 0x83; /* plain Linux partition */ } } static int schedule_resp(struct scsi_cmnd * cmnd, struct sdebug_dev_info * devip, done_funct_t done, int scsi_result, int delta_jiff) { if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmnd) { if (scsi_result) { struct scsi_device * sdp = cmnd->device; printk(KERN_INFO "scsi_debug: <%u %u %u %u> " "non-zero result=0x%x\n", sdp->host->host_no, sdp->channel, sdp->id, sdp->lun, scsi_result); } } if (cmnd && devip) { /* simulate autosense by this driver */ if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xff)) memcpy(cmnd->sense_buffer, devip->sense_buff, (SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ? SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE); } if (delta_jiff <= 0) { if (cmnd) cmnd->result = scsi_result; if (done) done(cmnd); return 0; } else { unsigned long iflags; int k; struct sdebug_queued_cmd * sqcp = NULL; spin_lock_irqsave(&queued_arr_lock, iflags); for (k = 0; k < scsi_debug_max_queue; ++k) { sqcp = &queued_arr[k]; if (! sqcp->in_use) break; } if (k >= scsi_debug_max_queue) { spin_unlock_irqrestore(&queued_arr_lock, iflags); printk(KERN_WARNING "scsi_debug: can_queue exceeded\n"); return 1; /* report busy to mid level */ } sqcp->in_use = 1; sqcp->a_cmnd = cmnd; sqcp->scsi_result = scsi_result; sqcp->done_funct = done; sqcp->cmnd_timer.function = timer_intr_handler; sqcp->cmnd_timer.data = k; sqcp->cmnd_timer.expires = jiffies + delta_jiff; add_timer(&sqcp->cmnd_timer); spin_unlock_irqrestore(&queued_arr_lock, iflags); if (cmnd) cmnd->result = 0; return 0; } } /* Note: The following macros create attribute files in the /sys/module/scsi_debug/parameters directory. Unfortunately this driver is unaware of a change and cannot trigger auxiliary actions as it can when the corresponding attribute in the /sys/bus/pseudo/drivers/scsi_debug directory is changed. */ module_param_named(add_host, scsi_debug_add_host, int, S_IRUGO | S_IWUSR); module_param_named(ato, scsi_debug_ato, int, S_IRUGO); module_param_named(delay, scsi_debug_delay, int, S_IRUGO | S_IWUSR); module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, S_IRUGO); module_param_named(dif, scsi_debug_dif, int, S_IRUGO); module_param_named(dix, scsi_debug_dix, int, S_IRUGO); module_param_named(dsense, scsi_debug_dsense, int, S_IRUGO | S_IWUSR); module_param_named(every_nth, scsi_debug_every_nth, int, S_IRUGO | S_IWUSR); module_param_named(fake_rw, scsi_debug_fake_rw, int, S_IRUGO | S_IWUSR); module_param_named(guard, scsi_debug_guard, int, S_IRUGO); module_param_named(lbpu, scsi_debug_lbpu, int, S_IRUGO); module_param_named(lbpws, scsi_debug_lbpws, int, S_IRUGO); module_param_named(lbpws10, scsi_debug_lbpws10, int, S_IRUGO); module_param_named(lowest_aligned, scsi_debug_lowest_aligned, int, S_IRUGO); module_param_named(max_luns, scsi_debug_max_luns, int, S_IRUGO | S_IWUSR); module_param_named(max_queue, scsi_debug_max_queue, int, S_IRUGO | S_IWUSR); module_param_named(no_lun_0, scsi_debug_no_lun_0, int, S_IRUGO | S_IWUSR); module_param_named(no_uld, scsi_debug_no_uld, int, S_IRUGO); module_param_named(num_parts, scsi_debug_num_parts, int, S_IRUGO); module_param_named(num_tgts, scsi_debug_num_tgts, int, S_IRUGO | S_IWUSR); module_param_named(opt_blks, scsi_debug_opt_blks, int, S_IRUGO); module_param_named(opts, scsi_debug_opts, int, S_IRUGO | S_IWUSR); module_param_named(physblk_exp, scsi_debug_physblk_exp, int, S_IRUGO); module_param_named(ptype, scsi_debug_ptype, int, S_IRUGO | S_IWUSR); module_param_named(scsi_level, scsi_debug_scsi_level, int, S_IRUGO); module_param_named(sector_size, scsi_debug_sector_size, int, S_IRUGO); module_param_named(unmap_alignment, scsi_debug_unmap_alignment, int, S_IRUGO); module_param_named(unmap_granularity, scsi_debug_unmap_granularity, int, S_IRUGO); module_param_named(unmap_max_blocks, scsi_debug_unmap_max_blocks, int, S_IRUGO); module_param_named(unmap_max_desc, scsi_debug_unmap_max_desc, int, S_IRUGO); module_param_named(virtual_gb, scsi_debug_virtual_gb, int, S_IRUGO | S_IWUSR); module_param_named(vpd_use_hostno, scsi_debug_vpd_use_hostno, int, S_IRUGO | S_IWUSR); module_param_named(write_same_length, scsi_debug_write_same_length, int, S_IRUGO | S_IWUSR); MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert"); MODULE_DESCRIPTION("SCSI debug adapter driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(SCSI_DEBUG_VERSION); MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)"); MODULE_PARM_DESC(ato, "application tag ownership: 0=disk 1=host (def=1)"); MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)"); MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs(def=8)"); MODULE_PARM_DESC(dif, "data integrity field type: 0-3 (def=0)"); MODULE_PARM_DESC(dix, "data integrity extensions mask (def=0)"); MODULE_PARM_DESC(dsense, "use descriptor sense format(def=0 -> fixed)"); MODULE_PARM_DESC(every_nth, "timeout every nth command(def=0)"); MODULE_PARM_DESC(fake_rw, "fake reads/writes instead of copying (def=0)"); MODULE_PARM_DESC(guard, "protection checksum: 0=crc, 1=ip (def=0)"); MODULE_PARM_DESC(lbpu, "enable LBP, support UNMAP command (def=0)"); MODULE_PARM_DESC(lbpws, "enable LBP, support WRITE SAME(16) with UNMAP bit (def=0)"); MODULE_PARM_DESC(lbpws10, "enable LBP, support WRITE SAME(10) with UNMAP bit (def=0)"); MODULE_PARM_DESC(lowest_aligned, "lowest aligned lba (def=0)"); MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)"); MODULE_PARM_DESC(max_queue, "max number of queued commands (1 to 255(def))"); MODULE_PARM_DESC(no_lun_0, "no LU number 0 (def=0 -> have lun 0)"); MODULE_PARM_DESC(no_uld, "stop ULD (e.g. sd driver) attaching (def=0))"); MODULE_PARM_DESC(num_parts, "number of partitions(def=0)"); MODULE_PARM_DESC(num_tgts, "number of targets per host to simulate(def=1)"); MODULE_PARM_DESC(opt_blks, "optimal transfer length in block (def=64)"); MODULE_PARM_DESC(opts, "1->noise, 2->medium_err, 4->timeout, 8->recovered_err... (def=0)"); MODULE_PARM_DESC(physblk_exp, "physical block exponent (def=0)"); MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])"); MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])"); MODULE_PARM_DESC(sector_size, "logical block size in bytes (def=512)"); MODULE_PARM_DESC(unmap_alignment, "lowest aligned thin provisioning lba (def=0)"); MODULE_PARM_DESC(unmap_granularity, "thin provisioning granularity in blocks (def=1)"); MODULE_PARM_DESC(unmap_max_blocks, "max # of blocks can be unmapped in one cmd (def=0xffffffff)"); MODULE_PARM_DESC(unmap_max_desc, "max # of ranges that can be unmapped in one cmd (def=256)"); MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)"); MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)"); MODULE_PARM_DESC(write_same_length, "Maximum blocks per WRITE SAME cmd (def=0xffff)"); static char sdebug_info[256]; static const char * scsi_debug_info(struct Scsi_Host * shp) { sprintf(sdebug_info, "scsi_debug, version %s [%s], " "dev_size_mb=%d, opts=0x%x", SCSI_DEBUG_VERSION, scsi_debug_version_date, scsi_debug_dev_size_mb, scsi_debug_opts); return sdebug_info; } /* scsi_debug_proc_info * Used if the driver currently has no own support for /proc/scsi */ static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length, int inout) { int len, pos, begin; int orig_length; orig_length = length; if (inout == 1) { char arr[16]; int minLen = length > 15 ? 15 : length; if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) return -EACCES; memcpy(arr, buffer, minLen); arr[minLen] = '\0'; if (1 != sscanf(arr, "%d", &pos)) return -EINVAL; scsi_debug_opts = pos; if (scsi_debug_every_nth != 0) scsi_debug_cmnd_count = 0; return length; } begin = 0; pos = len = sprintf(buffer, "scsi_debug adapter driver, version " "%s [%s]\n" "num_tgts=%d, shared (ram) size=%d MB, opts=0x%x, " "every_nth=%d(curr:%d)\n" "delay=%d, max_luns=%d, scsi_level=%d\n" "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n" "number of aborts=%d, device_reset=%d, bus_resets=%d, " "host_resets=%d\ndix_reads=%d dix_writes=%d dif_errors=%d\n", SCSI_DEBUG_VERSION, scsi_debug_version_date, scsi_debug_num_tgts, scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth, scsi_debug_cmnd_count, scsi_debug_delay, scsi_debug_max_luns, scsi_debug_scsi_level, scsi_debug_sector_size, sdebug_cylinders_per, sdebug_heads, sdebug_sectors_per, num_aborts, num_dev_resets, num_bus_resets, num_host_resets, dix_reads, dix_writes, dif_errors); if (pos < offset) { len = 0; begin = pos; } *start = buffer + (offset - begin); /* Start of wanted data */ len -= (offset - begin); if (len > length) len = length; return len; } static ssize_t sdebug_delay_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_delay); } static ssize_t sdebug_delay_store(struct device_driver * ddp, const char * buf, size_t count) { int delay; char work[20]; if (1 == sscanf(buf, "%10s", work)) { if ((1 == sscanf(work, "%d", &delay)) && (delay >= 0)) { scsi_debug_delay = delay; return count; } } return -EINVAL; } DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_show, sdebug_delay_store); static ssize_t sdebug_opts_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "0x%x\n", scsi_debug_opts); } static ssize_t sdebug_opts_store(struct device_driver * ddp, const char * buf, size_t count) { int opts; char work[20]; if (1 == sscanf(buf, "%10s", work)) { if (0 == strnicmp(work,"0x", 2)) { if (1 == sscanf(&work[2], "%x", &opts)) goto opts_done; } else { if (1 == sscanf(work, "%d", &opts)) goto opts_done; } } return -EINVAL; opts_done: scsi_debug_opts = opts; scsi_debug_cmnd_count = 0; return count; } DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_show, sdebug_opts_store); static ssize_t sdebug_ptype_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_ptype); } static ssize_t sdebug_ptype_store(struct device_driver * ddp, const char * buf, size_t count) { int n; if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { scsi_debug_ptype = n; return count; } return -EINVAL; } DRIVER_ATTR(ptype, S_IRUGO | S_IWUSR, sdebug_ptype_show, sdebug_ptype_store); static ssize_t sdebug_dsense_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dsense); } static ssize_t sdebug_dsense_store(struct device_driver * ddp, const char * buf, size_t count) { int n; if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { scsi_debug_dsense = n; return count; } return -EINVAL; } DRIVER_ATTR(dsense, S_IRUGO | S_IWUSR, sdebug_dsense_show, sdebug_dsense_store); static ssize_t sdebug_fake_rw_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_fake_rw); } static ssize_t sdebug_fake_rw_store(struct device_driver * ddp, const char * buf, size_t count) { int n; if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { scsi_debug_fake_rw = n; return count; } return -EINVAL; } DRIVER_ATTR(fake_rw, S_IRUGO | S_IWUSR, sdebug_fake_rw_show, sdebug_fake_rw_store); static ssize_t sdebug_no_lun_0_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_no_lun_0); } static ssize_t sdebug_no_lun_0_store(struct device_driver * ddp, const char * buf, size_t count) { int n; if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { scsi_debug_no_lun_0 = n; return count; } return -EINVAL; } DRIVER_ATTR(no_lun_0, S_IRUGO | S_IWUSR, sdebug_no_lun_0_show, sdebug_no_lun_0_store); static ssize_t sdebug_num_tgts_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_tgts); } static ssize_t sdebug_num_tgts_store(struct device_driver * ddp, const char * buf, size_t count) { int n; if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { scsi_debug_num_tgts = n; sdebug_max_tgts_luns(); return count; } return -EINVAL; } DRIVER_ATTR(num_tgts, S_IRUGO | S_IWUSR, sdebug_num_tgts_show, sdebug_num_tgts_store); static ssize_t sdebug_dev_size_mb_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dev_size_mb); } DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_show, NULL); static ssize_t sdebug_num_parts_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_parts); } DRIVER_ATTR(num_parts, S_IRUGO, sdebug_num_parts_show, NULL); static ssize_t sdebug_every_nth_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_every_nth); } static ssize_t sdebug_every_nth_store(struct device_driver * ddp, const char * buf, size_t count) { int nth; if ((count > 0) && (1 == sscanf(buf, "%d", &nth))) { scsi_debug_every_nth = nth; scsi_debug_cmnd_count = 0; return count; } return -EINVAL; } DRIVER_ATTR(every_nth, S_IRUGO | S_IWUSR, sdebug_every_nth_show, sdebug_every_nth_store); static ssize_t sdebug_max_luns_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_luns); } static ssize_t sdebug_max_luns_store(struct device_driver * ddp, const char * buf, size_t count) { int n; if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { scsi_debug_max_luns = n; sdebug_max_tgts_luns(); return count; } return -EINVAL; } DRIVER_ATTR(max_luns, S_IRUGO | S_IWUSR, sdebug_max_luns_show, sdebug_max_luns_store); static ssize_t sdebug_max_queue_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_queue); } static ssize_t sdebug_max_queue_store(struct device_driver * ddp, const char * buf, size_t count) { int n; if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n > 0) && (n <= SCSI_DEBUG_CANQUEUE)) { scsi_debug_max_queue = n; return count; } return -EINVAL; } DRIVER_ATTR(max_queue, S_IRUGO | S_IWUSR, sdebug_max_queue_show, sdebug_max_queue_store); static ssize_t sdebug_no_uld_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_no_uld); } DRIVER_ATTR(no_uld, S_IRUGO, sdebug_no_uld_show, NULL); static ssize_t sdebug_scsi_level_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_scsi_level); } DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_show, NULL); static ssize_t sdebug_virtual_gb_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_virtual_gb); } static ssize_t sdebug_virtual_gb_store(struct device_driver * ddp, const char * buf, size_t count) { int n; if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { scsi_debug_virtual_gb = n; sdebug_capacity = get_sdebug_capacity(); return count; } return -EINVAL; } DRIVER_ATTR(virtual_gb, S_IRUGO | S_IWUSR, sdebug_virtual_gb_show, sdebug_virtual_gb_store); static ssize_t sdebug_add_host_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_add_host); } static ssize_t sdebug_add_host_store(struct device_driver * ddp, const char * buf, size_t count) { int delta_hosts; if (sscanf(buf, "%d", &delta_hosts) != 1) return -EINVAL; if (delta_hosts > 0) { do { sdebug_add_adapter(); } while (--delta_hosts); } else if (delta_hosts < 0) { do { sdebug_remove_adapter(); } while (++delta_hosts); } return count; } DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_show, sdebug_add_host_store); static ssize_t sdebug_vpd_use_hostno_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_vpd_use_hostno); } static ssize_t sdebug_vpd_use_hostno_store(struct device_driver * ddp, const char * buf, size_t count) { int n; if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { scsi_debug_vpd_use_hostno = n; return count; } return -EINVAL; } DRIVER_ATTR(vpd_use_hostno, S_IRUGO | S_IWUSR, sdebug_vpd_use_hostno_show, sdebug_vpd_use_hostno_store); static ssize_t sdebug_sector_size_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%u\n", scsi_debug_sector_size); } DRIVER_ATTR(sector_size, S_IRUGO, sdebug_sector_size_show, NULL); static ssize_t sdebug_dix_show(struct device_driver *ddp, char *buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dix); } DRIVER_ATTR(dix, S_IRUGO, sdebug_dix_show, NULL); static ssize_t sdebug_dif_show(struct device_driver *ddp, char *buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dif); } DRIVER_ATTR(dif, S_IRUGO, sdebug_dif_show, NULL); static ssize_t sdebug_guard_show(struct device_driver *ddp, char *buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_guard); } DRIVER_ATTR(guard, S_IRUGO, sdebug_guard_show, NULL); static ssize_t sdebug_ato_show(struct device_driver *ddp, char *buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_ato); } DRIVER_ATTR(ato, S_IRUGO, sdebug_ato_show, NULL); static ssize_t sdebug_map_show(struct device_driver *ddp, char *buf) { ssize_t count; if (!scsi_debug_lbp()) return scnprintf(buf, PAGE_SIZE, "0-%u\n", sdebug_store_sectors); count = bitmap_scnlistprintf(buf, PAGE_SIZE, map_storep, map_size); buf[count++] = '\n'; buf[count++] = 0; return count; } DRIVER_ATTR(map, S_IRUGO, sdebug_map_show, NULL); /* Note: The following function creates attribute files in the /sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these files (over those found in the /sys/module/scsi_debug/parameters directory) is that auxiliary actions can be triggered when an attribute is changed. For example see: sdebug_add_host_store() above. */ static int do_create_driverfs_files(void) { int ret; ret = driver_create_file(&sdebug_driverfs_driver, &driver_attr_add_host); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_delay); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dsense); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_every_nth); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_fake_rw); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_luns); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_queue); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_no_uld); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_parts); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_tgts); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ptype); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_sector_size); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dix); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dif); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_guard); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ato); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_map); return ret; } static void do_remove_driverfs_files(void) { driver_remove_file(&sdebug_driverfs_driver, &driver_attr_map); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ato); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_guard); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dif); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dix); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_sector_size); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ptype); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_tgts); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_parts); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_no_uld); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_queue); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_luns); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_fake_rw); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_every_nth); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dsense); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_add_host); } struct device *pseudo_primary; static int __init scsi_debug_init(void) { unsigned long sz; int host_to_add; int k; int ret; switch (scsi_debug_sector_size) { case 512: case 1024: case 2048: case 4096: break;