usb-msc: move common scsi code to blockcmd.c
[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 // Validate drive and find block size and sector count.
79 int
80 scsi_init_drive(struct drive_s *drive, const char *s, int *pdt, char **desc)
81 {
82     if (!CONFIG_USB_MSC)
83         return 0;
84
85     struct disk_op_s dop;
86     memset(&dop, 0, sizeof(dop));
87     dop.drive_g = drive;
88     struct cdbres_inquiry data;
89     int ret = cdb_get_inquiry(&dop, &data);
90     if (ret)
91         return ret;
92     char vendor[sizeof(data.vendor)+1], product[sizeof(data.product)+1];
93     char rev[sizeof(data.rev)+1];
94     strtcpy(vendor, data.vendor, sizeof(vendor));
95     nullTrailingSpace(vendor);
96     strtcpy(product, data.product, sizeof(product));
97     nullTrailingSpace(product);
98     strtcpy(rev, data.rev, sizeof(rev));
99     nullTrailingSpace(rev);
100     *pdt = data.pdt & 0x1f;
101     int removable = !!(data.removable & 0x80);
102     dprintf(1, "%s vendor='%s' product='%s' rev='%s' type=%d removable=%d\n"
103             , s, vendor, product, rev, *pdt, removable);
104     drive->removable = removable;
105
106     if (*pdt == SCSI_TYPE_CDROM) {
107         drive->blksize = CDROM_SECTOR_SIZE;
108         drive->sectors = (u64)-1;
109
110         *desc = znprintf(MAXDESCSIZE, "DVD/CD [%s Drive %s %s %s]"
111                               , s, vendor, product, rev);
112         return 0;
113     }
114
115     ret = scsi_is_ready(&dop);
116     if (ret) {
117         dprintf(1, "scsi_is_ready returned %d\n", ret);
118         return ret;
119     }
120
121     struct cdbres_read_capacity capdata;
122     ret = cdb_read_capacity(&dop, &capdata);
123     if (ret)
124         return ret;
125
126     // READ CAPACITY returns the address of the last block.
127     // We do not bother with READ CAPACITY(16) because BIOS does not support
128     // 64-bit LBA anyway.
129     drive->blksize = ntohl(capdata.blksize);
130     drive->sectors = (u64)ntohl(capdata.sectors) + 1;
131     dprintf(1, "%s blksize=%d sectors=%d\n"
132             , s, drive->blksize, (unsigned)drive->sectors);
133
134     *desc = znprintf(MAXDESCSIZE, "%s Drive %s %s %s"
135                       , s, vendor, product, rev);
136     return 0;
137 }
138
139 int
140 cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data)
141 {
142     struct cdb_request_sense cmd;
143     memset(&cmd, 0, sizeof(cmd));
144     cmd.command = CDB_CMD_INQUIRY;
145     cmd.length = sizeof(*data);
146     op->count = 1;
147     op->buf_fl = data;
148     return cdb_cmd_data(op, &cmd, sizeof(*data));
149 }
150
151 // Request SENSE
152 int
153 cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data)
154 {
155     struct cdb_request_sense cmd;
156     memset(&cmd, 0, sizeof(cmd));
157     cmd.command = CDB_CMD_REQUEST_SENSE;
158     cmd.length = sizeof(*data);
159     op->count = 1;
160     op->buf_fl = data;
161     return cdb_cmd_data(op, &cmd, sizeof(*data));
162 }
163
164 // Test unit ready
165 int
166 cdb_test_unit_ready(struct disk_op_s *op)
167 {
168     struct cdb_request_sense cmd;
169     memset(&cmd, 0, sizeof(cmd));
170     cmd.command = CDB_CMD_TEST_UNIT_READY;
171     op->count = 0;
172     op->buf_fl = NULL;
173     return cdb_cmd_data(op, &cmd, 0);
174 }
175
176 // Request capacity
177 int
178 cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data)
179 {
180     struct cdb_read_capacity cmd;
181     memset(&cmd, 0, sizeof(cmd));
182     cmd.command = CDB_CMD_READ_CAPACITY;
183     op->count = 1;
184     op->buf_fl = data;
185     return cdb_cmd_data(op, &cmd, sizeof(*data));
186 }
187
188 // Read sectors.
189 int
190 cdb_read(struct disk_op_s *op)
191 {
192     struct cdb_rwdata_10 cmd;
193     memset(&cmd, 0, sizeof(cmd));
194     cmd.command = CDB_CMD_READ_10;
195     cmd.lba = htonl(op->lba);
196     cmd.count = htons(op->count);
197     return cdb_cmd_data(op, &cmd, GET_GLOBAL(op->drive_g->blksize));
198 }
199
200 // Write sectors.
201 int
202 cdb_write(struct disk_op_s *op)
203 {
204     struct cdb_rwdata_10 cmd;
205     memset(&cmd, 0, sizeof(cmd));
206     cmd.command = CDB_CMD_WRITE_10;
207     cmd.lba = htonl(op->lba);
208     cmd.count = htons(op->count);
209     return cdb_cmd_data(op, &cmd, GET_GLOBAL(op->drive_g->blksize));
210 }