grml...
[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     struct cdbres_mode_sense_geom geomdata;
135     ret = cdb_mode_sense_geom(&dop, &geomdata);
136     if (ret == 0) {
137         u32 cylinders;
138         cylinders = geomdata.cyl[0] << 16;
139         cylinders |= geomdata.cyl[1] << 8;
140         cylinders |= geomdata.cyl[2];
141         if (cylinders && geomdata.heads &&
142             drive->sectors <= 0xFFFFFFFFULL &&
143             ((u32)drive->sectors % (geomdata.heads * cylinders) == 0)) {
144             drive->pchs.cylinders = cylinders;
145             drive->pchs.heads = geomdata.heads;
146             drive->pchs.spt = (u32)drive->sectors
147      / (geomdata.heads * cylinders);
148         }
149     }
150
151     *desc = znprintf(MAXDESCSIZE, "%s Drive %s %s %s"
152                       , s, vendor, product, rev);
153     return 0;
154 }
155
156 int
157 cdb_get_inquiry(struct disk_op_s *op, struct cdbres_inquiry *data)
158 {
159     struct cdb_request_sense cmd;
160     memset(&cmd, 0, sizeof(cmd));
161     cmd.command = CDB_CMD_INQUIRY;
162     cmd.length = sizeof(*data);
163     op->count = 1;
164     op->buf_fl = data;
165     return cdb_cmd_data(op, &cmd, sizeof(*data));
166 }
167
168 // Request SENSE
169 int
170 cdb_get_sense(struct disk_op_s *op, struct cdbres_request_sense *data)
171 {
172     struct cdb_request_sense cmd;
173     memset(&cmd, 0, sizeof(cmd));
174     cmd.command = CDB_CMD_REQUEST_SENSE;
175     cmd.length = sizeof(*data);
176     op->count = 1;
177     op->buf_fl = data;
178     return cdb_cmd_data(op, &cmd, sizeof(*data));
179 }
180
181 // Test unit ready
182 int
183 cdb_test_unit_ready(struct disk_op_s *op)
184 {
185     struct cdb_request_sense cmd;
186     memset(&cmd, 0, sizeof(cmd));
187     cmd.command = CDB_CMD_TEST_UNIT_READY;
188     op->count = 0;
189     op->buf_fl = NULL;
190     return cdb_cmd_data(op, &cmd, 0);
191 }
192
193 // Request capacity
194 int
195 cdb_read_capacity(struct disk_op_s *op, struct cdbres_read_capacity *data)
196 {
197     struct cdb_read_capacity cmd;
198     memset(&cmd, 0, sizeof(cmd));
199     cmd.command = CDB_CMD_READ_CAPACITY;
200     op->count = 1;
201     op->buf_fl = data;
202     return cdb_cmd_data(op, &cmd, sizeof(*data));
203 }
204
205 // Mode sense, geometry page.
206 int
207 cdb_mode_sense_geom(struct disk_op_s *op, struct cdbres_mode_sense_geom *data)
208 {
209     struct cdb_mode_sense cmd;
210     memset(&cmd, 0, sizeof(cmd));
211     cmd.command = CDB_CMD_MODE_SENSE;
212     cmd.flags = 8; /* DBD */
213     cmd.page = MODE_PAGE_HD_GEOMETRY;
214     cmd.count = htons(sizeof(*data));
215     op->count = 1;
216     op->buf_fl = data;
217     return cdb_cmd_data(op, &cmd, sizeof(*data));
218 }
219
220 // Read sectors.
221 int
222 cdb_read(struct disk_op_s *op)
223 {
224     struct cdb_rwdata_10 cmd;
225     memset(&cmd, 0, sizeof(cmd));
226     cmd.command = CDB_CMD_READ_10;
227     cmd.lba = htonl(op->lba);
228     cmd.count = htons(op->count);
229     return cdb_cmd_data(op, &cmd, GET_GLOBAL(op->drive_g->blksize));
230 }
231
232 // Write sectors.
233 int
234 cdb_write(struct disk_op_s *op)
235 {
236     struct cdb_rwdata_10 cmd;
237     memset(&cmd, 0, sizeof(cmd));
238     cmd.command = CDB_CMD_WRITE_10;
239     cmd.lba = htonl(op->lba);
240     cmd.count = htons(op->count);
241     return cdb_cmd_data(op, &cmd, GET_GLOBAL(op->drive_g->blksize));
242 }