usb-msc: go through TEST UNIT READY for hard disks.
[seabios.git] / src / blockcmd.c
1 // Support for several common scsi like command data block requests
2 //
3 // Copyright (C) 2010  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
7
8 #include "biosvar.h" // GET_GLOBAL
9 #include "util.h" // htonl
10 #include "disk.h" // struct disk_op_s
11 #include "blockcmd.h" // struct cdb_request_sense
12 #include "ata.h" // atapi_cmd_data
13 #include "ahci.h" // atapi_cmd_data
14 #include "usb-msc.h" // usb_cmd_data
15
16 // Route command to low-level handler.
17 static int
18 cdb_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
19 {
20     u8 type = GET_GLOBAL(op->drive_g->type);
21     switch (type) {
22     case DTYPE_ATAPI:
23         return atapi_cmd_data(op, cdbcmd, blocksize);
24     case DTYPE_USB:
25         return usb_cmd_data(op, cdbcmd, blocksize);
26     case DTYPE_AHCI:
27         return ahci_cmd_data(op, cdbcmd, blocksize);
28     default:
29         op->count = 0;
30         return DISK_RET_EPARAM;
31     }
32 }
33
34 int
35 scsi_is_ready(struct disk_op_s *op)
36 {
37     dprintf(6, "scsi_is_ready (drive=%p)\n", op->drive_g);
38
39     /* Retry TEST UNIT READY for 5 seconds unless MEDIUM NOT PRESENT is
40      * reported by the device.  If the device reports "IN PROGRESS",
41      * 30 seconds is added. */
42     int in_progress = 0;
43     u64 end = calc_future_tsc(5000);
44     for (;;) {
45         if (check_tsc(end)) {
46             dprintf(1, "test unit ready failed\n");
47             return -1;
48         }
49
50         int ret = cdb_test_unit_ready(op);
51         if (!ret)
52             // Success
53             break;
54
55         struct cdbres_request_sense sense;
56         ret = cdb_get_sense(op, &sense);
57         if (ret)
58             // Error - retry.
59             continue;
60
61         // Sense succeeded.
62         if (sense.asc == 0x3a) { /* MEDIUM NOT PRESENT */
63             dprintf(1, "Device reports MEDIUM NOT PRESENT\n");
64             return -1;
65         }
66
67         if (sense.asc == 0x04 && sense.ascq == 0x01 && !in_progress) {
68             /* IN PROGRESS OF BECOMING READY */
69             printf("Waiting for device to detect medium... ");
70             /* Allow 30 seconds more */
71             end = calc_future_tsc(30000);
72             in_progress = 1;
73         }
74     }
75     return 0;
76 }
77
78 int
79 cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data)
80 {
81     struct cdb_request_sense cmd;
82     memset(&cmd, 0, sizeof(cmd));
83     cmd.command = CDB_CMD_INQUIRY;
84     cmd.length = sizeof(*data);
85     op->count = 1;
86     op->buf_fl = data;
87     return cdb_cmd_data(op, &cmd, sizeof(*data));
88 }
89
90 // Request SENSE
91 int
92 cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data)
93 {
94     struct cdb_request_sense cmd;
95     memset(&cmd, 0, sizeof(cmd));
96     cmd.command = CDB_CMD_REQUEST_SENSE;
97     cmd.length = sizeof(*data);
98     op->count = 1;
99     op->buf_fl = data;
100     return cdb_cmd_data(op, &cmd, sizeof(*data));
101 }
102
103 // Test unit ready
104 int
105 cdb_test_unit_ready(struct disk_op_s *op)
106 {
107     struct cdb_request_sense cmd;
108     memset(&cmd, 0, sizeof(cmd));
109     cmd.command = CDB_CMD_TEST_UNIT_READY;
110     op->count = 0;
111     op->buf_fl = NULL;
112     return cdb_cmd_data(op, &cmd, 0);
113 }
114
115 // Request capacity
116 int
117 cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data)
118 {
119     struct cdb_read_capacity cmd;
120     memset(&cmd, 0, sizeof(cmd));
121     cmd.command = CDB_CMD_READ_CAPACITY;
122     op->count = 1;
123     op->buf_fl = data;
124     return cdb_cmd_data(op, &cmd, sizeof(*data));
125 }
126
127 // Read sectors.
128 int
129 cdb_read(struct disk_op_s *op)
130 {
131     struct cdb_rwdata_10 cmd;
132     memset(&cmd, 0, sizeof(cmd));
133     cmd.command = CDB_CMD_READ_10;
134     cmd.lba = htonl(op->lba);
135     cmd.count = htons(op->count);
136     return cdb_cmd_data(op, &cmd, GET_GLOBAL(op->drive_g->blksize));
137 }
138
139 // Write sectors.
140 int
141 cdb_write(struct disk_op_s *op)
142 {
143     struct cdb_rwdata_10 cmd;
144     memset(&cmd, 0, sizeof(cmd));
145     cmd.command = CDB_CMD_WRITE_10;
146     cmd.lba = htonl(op->lba);
147     cmd.count = htons(op->count);
148     return cdb_cmd_data(op, &cmd, GET_GLOBAL(op->drive_g->blksize));
149 }