|
@@ -24,6 +24,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
24
|
24
|
#include <byteswap.h>
|
25
|
25
|
#include <errno.h>
|
26
|
26
|
#include <ipxe/list.h>
|
|
27
|
+#include <ipxe/process.h>
|
|
28
|
+#include <ipxe/xfer.h>
|
27
|
29
|
#include <ipxe/blockdev.h>
|
28
|
30
|
#include <ipxe/scsi.h>
|
29
|
31
|
|
|
@@ -202,11 +204,24 @@ struct scsi_device {
|
202
|
204
|
|
203
|
205
|
/** SCSI LUN */
|
204
|
206
|
struct scsi_lun lun;
|
|
207
|
+ /** Flags */
|
|
208
|
+ unsigned int flags;
|
|
209
|
+
|
|
210
|
+ /** TEST UNIT READY interface */
|
|
211
|
+ struct interface ready;
|
|
212
|
+ /** TEST UNIT READY process */
|
|
213
|
+ struct process process;
|
205
|
214
|
|
206
|
215
|
/** List of commands */
|
207
|
216
|
struct list_head cmds;
|
208
|
217
|
};
|
209
|
218
|
|
|
219
|
+/** SCSI device flags */
|
|
220
|
+enum scsi_device_flags {
|
|
221
|
+ /** Unit is ready */
|
|
222
|
+ SCSIDEV_UNIT_READY = 0x0001,
|
|
223
|
+};
|
|
224
|
+
|
210
|
225
|
/** A SCSI command */
|
211
|
226
|
struct scsi_command {
|
212
|
227
|
/** Reference count */
|
|
@@ -624,6 +639,26 @@ static struct scsi_command_type scsicmd_read_capacity = {
|
624
|
639
|
.done = scsicmd_read_capacity_done,
|
625
|
640
|
};
|
626
|
641
|
|
|
642
|
+/**
|
|
643
|
+ * Construct SCSI TEST UNIT READY command
|
|
644
|
+ *
|
|
645
|
+ * @v scsicmd SCSI command
|
|
646
|
+ * @v command SCSI command IU
|
|
647
|
+ */
|
|
648
|
+static void scsicmd_test_unit_ready_cmd ( struct scsi_command *scsicmd __unused,
|
|
649
|
+ struct scsi_cmd *command ) {
|
|
650
|
+ struct scsi_cdb_test_unit_ready *testready = &command->cdb.testready;
|
|
651
|
+
|
|
652
|
+ testready->opcode = SCSI_OPCODE_TEST_UNIT_READY;
|
|
653
|
+}
|
|
654
|
+
|
|
655
|
+/** SCSI TEST UNIT READY command type */
|
|
656
|
+static struct scsi_command_type scsicmd_test_unit_ready = {
|
|
657
|
+ .name = "TEST UNIT READY",
|
|
658
|
+ .cmd = scsicmd_test_unit_ready_cmd,
|
|
659
|
+ .done = scsicmd_close,
|
|
660
|
+};
|
|
661
|
+
|
627
|
662
|
/** SCSI command block interface operations */
|
628
|
663
|
static struct interface_operation scsicmd_block_op[] = {
|
629
|
664
|
INTF_OP ( intf_close, struct scsi_command *, scsicmd_close ),
|
|
@@ -751,6 +786,34 @@ static int scsidev_read_capacity ( struct scsi_device *scsidev,
|
751
|
786
|
0, 0, UNULL, 0 );
|
752
|
787
|
}
|
753
|
788
|
|
|
789
|
+/**
|
|
790
|
+ * Test to see if SCSI device is ready
|
|
791
|
+ *
|
|
792
|
+ * @v scsidev SCSI device
|
|
793
|
+ * @v block Block data interface
|
|
794
|
+ * @ret rc Return status code
|
|
795
|
+ */
|
|
796
|
+static int scsidev_test_unit_ready ( struct scsi_device *scsidev,
|
|
797
|
+ struct interface *block ) {
|
|
798
|
+ return scsidev_command ( scsidev, block, &scsicmd_test_unit_ready,
|
|
799
|
+ 0, 0, UNULL, 0 );
|
|
800
|
+}
|
|
801
|
+
|
|
802
|
+/**
|
|
803
|
+ * Check SCSI device flow-control window
|
|
804
|
+ *
|
|
805
|
+ * @v scsidev SCSI device
|
|
806
|
+ * @ret len Length of window
|
|
807
|
+ */
|
|
808
|
+static size_t scsidev_window ( struct scsi_device *scsidev ) {
|
|
809
|
+
|
|
810
|
+ /* Refuse commands until unit is confirmed ready */
|
|
811
|
+ if ( ! ( scsidev->flags & SCSIDEV_UNIT_READY ) )
|
|
812
|
+ return 0;
|
|
813
|
+
|
|
814
|
+ return xfer_window ( &scsidev->scsi );
|
|
815
|
+}
|
|
816
|
+
|
754
|
817
|
/**
|
755
|
818
|
* Close SCSI device
|
756
|
819
|
*
|
|
@@ -761,9 +824,13 @@ static void scsidev_close ( struct scsi_device *scsidev, int rc ) {
|
761
|
824
|
struct scsi_command *scsicmd;
|
762
|
825
|
struct scsi_command *tmp;
|
763
|
826
|
|
|
827
|
+ /* Stop process */
|
|
828
|
+ process_del ( &scsidev->process );
|
|
829
|
+
|
764
|
830
|
/* Shut down interfaces */
|
765
|
831
|
intf_shutdown ( &scsidev->block, rc );
|
766
|
832
|
intf_shutdown ( &scsidev->scsi, rc );
|
|
833
|
+ intf_shutdown ( &scsidev->ready, rc );
|
767
|
834
|
|
768
|
835
|
/* Shut down any remaining commands */
|
769
|
836
|
list_for_each_entry_safe ( scsicmd, tmp, &scsidev->cmds, list ) {
|
|
@@ -775,6 +842,7 @@ static void scsidev_close ( struct scsi_device *scsidev, int rc ) {
|
775
|
842
|
|
776
|
843
|
/** SCSI device block interface operations */
|
777
|
844
|
static struct interface_operation scsidev_block_op[] = {
|
|
845
|
+ INTF_OP ( xfer_window, struct scsi_device *, scsidev_window ),
|
778
|
846
|
INTF_OP ( block_read, struct scsi_device *, scsidev_read ),
|
779
|
847
|
INTF_OP ( block_write, struct scsi_device *, scsidev_write ),
|
780
|
848
|
INTF_OP ( block_read_capacity, struct scsi_device *,
|
|
@@ -787,6 +855,67 @@ static struct interface_descriptor scsidev_block_desc =
|
787
|
855
|
INTF_DESC_PASSTHRU ( struct scsi_device, block,
|
788
|
856
|
scsidev_block_op, scsi );
|
789
|
857
|
|
|
858
|
+/**
|
|
859
|
+ * Handle SCSI TEST UNIT READY response
|
|
860
|
+ *
|
|
861
|
+ * @v scsidev SCSI device
|
|
862
|
+ * @v rc Reason for close
|
|
863
|
+ */
|
|
864
|
+static void scsidev_ready ( struct scsi_device *scsidev, int rc ) {
|
|
865
|
+
|
|
866
|
+ /* Shut down interface */
|
|
867
|
+ intf_shutdown ( &scsidev->ready, rc );
|
|
868
|
+
|
|
869
|
+ /* Close device on failure */
|
|
870
|
+ if ( rc != 0 ) {
|
|
871
|
+ DBGC ( scsidev, "SCSI %p not ready: %s\n",
|
|
872
|
+ scsidev, strerror ( rc ) );
|
|
873
|
+ scsidev_close ( scsidev, rc );
|
|
874
|
+ return;
|
|
875
|
+ }
|
|
876
|
+
|
|
877
|
+ /* Mark device as ready */
|
|
878
|
+ scsidev->flags |= SCSIDEV_UNIT_READY;
|
|
879
|
+ xfer_window_changed ( &scsidev->block );
|
|
880
|
+ DBGC ( scsidev, "SCSI %p unit is ready\n", scsidev );
|
|
881
|
+}
|
|
882
|
+
|
|
883
|
+/** SCSI device TEST UNIT READY interface operations */
|
|
884
|
+static struct interface_operation scsidev_ready_op[] = {
|
|
885
|
+ INTF_OP ( intf_close, struct scsi_device *, scsidev_ready ),
|
|
886
|
+};
|
|
887
|
+
|
|
888
|
+/** SCSI device TEST UNIT READY interface descriptor */
|
|
889
|
+static struct interface_descriptor scsidev_ready_desc =
|
|
890
|
+ INTF_DESC ( struct scsi_device, ready, scsidev_ready_op );
|
|
891
|
+
|
|
892
|
+/**
|
|
893
|
+ * SCSI TEST UNIT READY process
|
|
894
|
+ *
|
|
895
|
+ * @v process Process
|
|
896
|
+ */
|
|
897
|
+static void scsidev_step ( struct process *process ) {
|
|
898
|
+ struct scsi_device *scsidev =
|
|
899
|
+ container_of ( process, struct scsi_device, process );
|
|
900
|
+ int rc;
|
|
901
|
+
|
|
902
|
+ /* Wait until underlying SCSI device is ready */
|
|
903
|
+ if ( xfer_window ( &scsidev->scsi ) == 0 )
|
|
904
|
+ return;
|
|
905
|
+
|
|
906
|
+ /* Stop process */
|
|
907
|
+ process_del ( &scsidev->process );
|
|
908
|
+
|
|
909
|
+ DBGC ( scsidev, "SCSI %p waiting for unit to become ready\n",
|
|
910
|
+ scsidev );
|
|
911
|
+
|
|
912
|
+ /* Issue TEST UNIT READY command */
|
|
913
|
+ if ( ( rc = scsidev_test_unit_ready ( scsidev, &scsidev->ready )) !=0){
|
|
914
|
+ scsidev_close ( scsidev, rc );
|
|
915
|
+ return;
|
|
916
|
+ }
|
|
917
|
+}
|
|
918
|
+
|
790
|
919
|
/** SCSI device SCSI interface operations */
|
791
|
920
|
static struct interface_operation scsidev_scsi_op[] = {
|
792
|
921
|
INTF_OP ( intf_close, struct scsi_device *, scsidev_close ),
|
|
@@ -816,14 +945,14 @@ int scsi_open ( struct interface *block, struct interface *scsi,
|
816
|
945
|
ref_init ( &scsidev->refcnt, NULL );
|
817
|
946
|
intf_init ( &scsidev->block, &scsidev_block_desc, &scsidev->refcnt );
|
818
|
947
|
intf_init ( &scsidev->scsi, &scsidev_scsi_desc, &scsidev->refcnt );
|
|
948
|
+ intf_init ( &scsidev->ready, &scsidev_ready_desc, &scsidev->refcnt );
|
|
949
|
+ process_init ( &scsidev->process, scsidev_step, &scsidev->refcnt );
|
819
|
950
|
INIT_LIST_HEAD ( &scsidev->cmds );
|
820
|
951
|
memcpy ( &scsidev->lun, lun, sizeof ( scsidev->lun ) );
|
821
|
952
|
DBGC ( scsidev, "SCSI %p created for LUN " SCSI_LUN_FORMAT "\n",
|
822
|
953
|
scsidev, SCSI_LUN_DATA ( scsidev->lun ) );
|
823
|
954
|
|
824
|
|
- /* Attach to SCSI and parent and interfaces, mortalise self,
|
825
|
|
- * and return
|
826
|
|
- */
|
|
955
|
+ /* Attach to SCSI and parent interfaces, mortalise self, and return */
|
827
|
956
|
intf_plug_plug ( &scsidev->scsi, scsi );
|
828
|
957
|
intf_plug_plug ( &scsidev->block, block );
|
829
|
958
|
ref_put ( &scsidev->refcnt );
|